mario60

TYPO3.Flow / propertyMapper, bug?

Oct 18th, 2013
194
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 12.90 KB | None | 0 0
  1. ************SOLVED************
  2. I need to configurate the mapping. You do that by a initializeCreateAction(), as it follows.
  3. (I had a couple of annoying misprints such that it looked as it was not working)
  4.  
  5. /**
  6.      * Adds the given new array of father objects to the father repository
  7.      *
  8.      *
  9.      * @return void
  10.      */
  11.            public function initializeCreateAction() {
  12.  
  13.         $this->arguments['newFathers']->getPropertyMappingConfiguration()->forProperty('sons.*')->allowAllProperties()->setTypeConverterOption(
  14.                 'TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter',
  15.                 \TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED,
  16.                 TRUE
  17.         );      
  18. }
  19.  
  20.  
  21. --------------------------------------------
  22. MODEL father and son
  23.  
  24. FATHER
  25. <?php
  26. namespace Mario\Basic\Domain\Model;
  27.  
  28. /*                                                                        *
  29.  * This script belongs to the Flow package "Mario.Basic".                 *
  30.  *                                                                        *
  31.  *                                                                        */
  32. use Doctrine\ORM\Mapping as ORM;
  33. use TYPO3\Flow\Annotations as Flow;
  34.  
  35. /**
  36.  * A Father
  37.  *
  38.  * @Flow\Entity
  39.  */
  40. class Father {
  41.  
  42.         /**
  43.      * @var string
  44.      * @Flow\Validate(type="Text")
  45.          * @Flow\Validate(type="NotEmpty")
  46.      * @Flow\Validate(type="StringLength", options={ "minimum"=3, "maximum"=80 })
  47.      * @Flow\Identity
  48.      * @ORM\Column(length=80)
  49.      */
  50.     protected $name;
  51.  
  52.     /**
  53.      * The sons
  54.      * @var \Doctrine\Common\Collections\Collection<\Mario\Basic\Domain\Model\Son>
  55.      * @ORM\OneToMany(mappedBy="father", cascade={"persist"}, orphanRemoval=true)
  56.      */
  57.     protected $sons;
  58.  
  59.  
  60.     /**
  61.      * Get the Father's name
  62.      *
  63.      * @return string The Father's name
  64.      */
  65.     public function getName() {
  66.         return $this->name;
  67.     }
  68.  
  69.     /**
  70.      * Sets this Father's name
  71.      *
  72.      * @param string $name The Father's name
  73.      * @return void
  74.      */
  75.     public function setName($name) {
  76.         $this->name = $name;
  77.     }
  78.  
  79.         /**
  80.      * Adds a son to this father
  81.      *
  82.      * @param \Mario\Basic\Domain\Model\Son $son
  83.      * @return void
  84.      */
  85.     public function addSons(\Mario\Basic\Domain\Model\Son $son) {
  86.         $son->setFather($this);
  87.         $this->sons->add($son);
  88.     }
  89.  
  90.          
  91.     /**
  92.      * Removes a son from this father
  93.      *
  94.      * @param \Mario\Basic\Domain\Model\Son $son
  95.      * @return void
  96.      */
  97.     public function removeSons(\Mario\Basic\Domain\Model\Son $son) {
  98.         $this->sons->removeElement($son);
  99.     }
  100.  
  101.     /**
  102.      * Get the Father's sons
  103.      *
  104.      * @return \Doctrine\Common\Collections\Collection The Father's sons
  105.      */
  106.     public function getSons() {
  107.         return $this->sons;
  108.     }
  109.  
  110.     /**
  111.      * Sets this Father's sons
  112.      *
  113.      * @param \Doctrine\Common\Collections\Collection $sons The Father's sons
  114.      * @return void
  115.      */
  116.     public function setSons(\Doctrine\Common\Collections\Collection $sons) {
  117. /* NOTE next line to assign a father to each son, when you create a father and his sons from the same new template. */
  118.                  foreach ($sons as $son){$son->setFather($this);}
  119.          $this->sons = $sons;
  120.     }
  121. }
  122.  
  123. SON
  124.  
  125. <?php
  126. namespace Mario\Basic\Domain\Model;
  127.  
  128. /*                                                                        *
  129.  * This script belongs to the Flow package "Mario.Basic".                 *
  130.  *                                                                        *
  131.  *                                                                        */
  132.  
  133. use TYPO3\Flow\Annotations as Flow;
  134. use Doctrine\ORM\Mapping as ORM;
  135.  
  136. /**
  137.  * A Son
  138.  *
  139.  * @Flow\Entity
  140.  */
  141. class Son {
  142.  
  143.     /**
  144.      * The father
  145.      * @var \Mario\Basic\Domain\Model\Father
  146.      * @ORM\ManyToOne(inversedBy="sons")
  147.      */
  148.     protected $father;
  149.  
  150.     /**
  151.      * @var string
  152.      * @Flow\Validate(type="Text")
  153.          * @Flow\Validate(type="NotEmpty")
  154.      * @Flow\Validate(type="StringLength", options={ "minimum"=3, "maximum"=80 })
  155.      * @Flow\Identity
  156.      * @ORM\Column(length=80)
  157.      */
  158.     protected $nameson;
  159.  
  160.     /**
  161.      * Get the Son's father
  162.      *
  163.      * @return \Mario\Basic\Domain\Model\Father The Son's father
  164.      */
  165.     public function getFather() {
  166.         return $this->father;
  167.     }
  168.  
  169.     /**
  170.      * Sets this Son's father
  171.      *
  172.      * @param \Mario\Basic\Domain\Model\Father $father The Son's father
  173.      * @return void
  174.      */
  175.     public function setFather(\Mario\Basic\Domain\Model\Father $father) {
  176.         $this->father = $father;
  177.     }
  178.  
  179.     /**
  180.      * Get the Son's nameson
  181.      *
  182.      * @return string The Son's nameson
  183.      */
  184.     public function getNameson() {
  185.         return $this->nameson;
  186.     }
  187.  
  188.     /**
  189.      * Sets this Son's nameson
  190.      *
  191.      * @param string $name The Son's nameson
  192.      * @return void
  193.      */
  194.     public function setNameson($nameson) {
  195.         $this->nameson = $nameson;
  196.     }
  197. }
  198. ?>
  199.  
  200. FATHER CONTROLLER index, new, create and show
  201.  
  202. <?php
  203. namespace Mario\Basic\Controller;
  204.  
  205. /*                                                                        *
  206.  * This script belongs to the Flow package "Mario.Basic".                 *
  207.  *                                                                        *
  208.  *                                                                        */
  209.  
  210. use \TYPO3\Flow\Persistence\QueryInterface;
  211. use \TYPO3\Fluid\View\TemplateView;
  212. use TYPO3\Flow\Annotations as Flow;
  213.  
  214. /**
  215.  * Father controller for the Mario.Basic package
  216.  *
  217.  * @Flow\Scope("singleton")
  218.  */
  219. class FatherController extends \TYPO3\Flow\Mvc\Controller\ActionController {
  220.  
  221.     /**
  222.      * @Flow\Inject
  223.      * @var \Mario\Basic\Domain\Repository\FatherRepository
  224.      */
  225.     protected $fatherRepository;
  226.  
  227.         /**
  228.      * @Flow\Inject
  229.      * @var \Mario\Basic\Domain\Repository\SonRepository
  230.      */
  231.     protected $sonRepository;
  232.  
  233.         /**
  234.      * @var \Mario\Basic\Domain\Model\Father
  235.      */
  236.     protected $father;
  237.  
  238.     /**
  239.      * Shows a list of fathers
  240.      *
  241.      * @return void
  242.      */
  243.     public function indexAction() {
  244.         $this->view->assign('fathers', $this->fatherRepository->findAll());
  245.     }
  246.  
  247.     /**
  248.      * Shows a single father object
  249.      *
  250.      * @param \Mario\Basic\Domain\Model\Father $father The father to show
  251.      * @return void
  252.      */
  253.     public function showAction(\Mario\Basic\Domain\Model\Father $father) {
  254.         $this->view->assign('father', $father);
  255.     }
  256.  
  257.     /**
  258.      * Shows a form for creating a new father object
  259.      *
  260.      * @return void
  261.      */
  262.     public function newAction() {
  263.     }
  264.  
  265.     /**
  266.      * Adds the given new array of father objects to the father repository
  267.      *
  268.      * @param \Doctrine\Common\Collections\Collection<\Mario\Basic\Domain\Model\Father> $newFathers A collection of new fathers to add
  269.      * @return void
  270.      */
  271.     public function createAction(\Doctrine\Common\Collections\Collection $newFathers) {
  272.        
  273.         foreach($newFathers as $father){
  274.         $this->fatherRepository->add($father);  }
  275.  
  276.                 $this->addFlashMessage('Created a new father and his sons');
  277.         $this->redirect('index');
  278.     }
  279. }
  280.  
  281. ?>
  282.  
  283. TEMPLATE NEW. HTML FORM, case two fathers, a son each
  284.  
  285. <form name="newFathers" action="mario.basic/father/create" method="post">
  286. <div style="display: none">
  287. <input name="__referrer[@package]" value="Mario.Basic" type="hidden">
  288. <input name="__referrer[@subpackage]" value="" type="hidden">
  289. <input name="__referrer[@controller]" value="Father" type="hidden">
  290. <input name="__referrer[@action]" value="new" type="hidden">
  291. <input name="__referrer[arguments]" value="....." type="hidden">
  292. <input name="__trustedProperties" value=".....">
  293.  
  294. <label for="name">Name</label>
  295. <input name="newFathers[0][name]" type="text">
  296. <label for="nameson">Name Son</label><br>
  297. <input id="sonInput00" name="newFathers[0][sons][0][nameson]" type="text">
  298. <label for="name">Name</label>
  299. <input name="newFathers[1][name]" type="text">
  300. <label for="nameson">Name Son</label>
  301. <input id="sonInput10" name="newFathers[1][sons][0][nameson]" type="text">
  302. <input name="" value="Create" type="submit">
  303. </form>
  304.  
  305. note POST array:
  306. newFathers[0][name]
  307. newFathers[0][sons][0][nameson]
  308. newFathers[1][name]
  309. newFathers[1][sons][0][nameson]
  310.  
  311. The above case goes thru fine, both fathers are created, both sons too, every db field is filled in correctly.
  312.  
  313. If I try the same with two fathers, a son for the former and TWO sons for the latter
  314.  
  315. <form name="newFathers" action="mario.basic/father/create" method="post">
  316. <div style="display: none">
  317. <input name="__referrer[@package]" value="Mario.Basic" type="hidden">
  318. <input name="__referrer[@subpackage]" value="" type="hidden">
  319. <input name="__referrer[@controller]" value="Father" type="hidden">
  320. <input name="__referrer[@action]" value="new" type="hidden">
  321. <input name="__referrer[arguments]" value="....." type="hidden">
  322. <input name="__trustedProperties" value="....">
  323.  
  324. <label for="name">Name</label>
  325. <input name="newFathers[0][name]" type="text">
  326. <label for="nameson">Name Son</label><br>
  327. <input id="sonInput00" name="newFathers[0][sons][0][nameson]" type="text">
  328. <label for="name">Name</label>
  329. <input name="newFathers[1][name]" type="text">
  330. <label for="nameson">Name Son</label><br>
  331. <input id="sonInput10" name="newFathers[1][sons][0][nameson]" type="text">
  332. <input id="sonInput11" name="newFathers[1][sons][1][nameson]" type="text">
  333. <input name="" value="Create" type="submit">
  334. </form>
  335.  
  336. note POST array:
  337. newFathers[0][name]
  338. newFathers[0][sons][0][nameson]
  339. newFathers[1][name]
  340. newFathers[1][sons][0][nameson]
  341. newFathers[1][sons][1][nameson]    <<<<-----
  342.  
  343. then I get error,
  344. #1297759968: Exception while property mapping for target type "Doctrine\Common\Collections\Collection<\Mario\Basic\Domain\Model\Father>", at property path "1.sons": It is not allowed to map property "1".
  345.  
  346. I understand error is thrown in by propertyMapper::convert(), indeed by  propertyMapper::doMapping().
  347.  
  348. If I just - brutally - modify doMapping as it follows, objects (both father, all three sons) are created fine.
  349.  
  350. doMapping() ad hoc MODIFIED
  351.  
  352. <?php
  353.  
  354. protected function doMapping($source, $targetType, \TYPO3\Flow\Property\PropertyMappingConfigurationInterface $configuration, &$currentPropertyPath) {
  355.         if (is_object($source)) {
  356.             $targetType = $this->parseCompositeType($targetType);
  357.             if ($source instanceof $targetType) {
  358.                 return $source;
  359.             }
  360.         }
  361.  
  362.         if ($source === NULL) {
  363.             $source = '';
  364.         }
  365.  
  366.         $typeConverter = $this->findTypeConverter($source, $targetType, $configuration);
  367.         $targetType = $typeConverter->getTargetTypeForSource($source, $targetType, $configuration);
  368.  
  369.         if (!is_object($typeConverter) || !($typeConverter instanceof \TYPO3\Flow\Property\TypeConverterInterface)) {
  370.             throw new Exception\TypeConverterException('Type converter for "' . $source . '" -> "' . $targetType . '" not found.');
  371.         }
  372.  
  373.         $convertedChildProperties = array();
  374.         foreach ($typeConverter->getSourceChildPropertiesToBeConverted($source) as $sourcePropertyName => $sourcePropertyValue) {
  375.             $targetPropertyName = $configuration->getTargetPropertyName($sourcePropertyName);
  376.  
  377.  
  378. ///// ADD the following  configuration settings
  379. $configuration->setTypeConverterOptions('TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter', array(
  380.             \TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE,
  381.             \TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED => TRUE
  382.         ));
  383.  
  384.  
  385. /////// COMMENT OUT the following lines
  386. /*
  387. if ($configuration->shouldSkip($targetPropertyName)) {
  388.                 continue;
  389.             }
  390. //print_r($configuration);
  391. //print_r($configuration->getConfigurationFor($targetPropertyName));
  392.             if (!$configuration->shouldMap($targetPropertyName)) {
  393.                 if ($configuration->shouldSkipUnknownProperties()) {
  394.                     continue;
  395.                 }
  396.                 throw new Exception\InvalidPropertyMappingConfigurationException('It is not allowed to map property "' . $targetPropertyName . '". You need to use $propertyMappingConfiguration->allowProperties(\'' . $targetPropertyName . '\') to enable mapping of this property.', 1335969887);
  397.             }
  398. */
  399.  
  400.             $targetPropertyType = $typeConverter->getTypeOfChildProperty($targetType, $targetPropertyName, $configuration);
  401.  
  402.             $subConfiguration = $configuration->getConfigurationFor($targetPropertyName);
  403.  
  404.             $currentPropertyPath[] = $targetPropertyName;
  405.  
  406.             $targetPropertyValue = $this->doMapping($sourcePropertyValue, $targetPropertyType, $subConfiguration, $currentPropertyPath);
  407.             array_pop($currentPropertyPath);
  408.             if (!($targetPropertyValue instanceof \TYPO3\Flow\Error\Error)) {
  409.                 $convertedChildProperties[$targetPropertyName] = $targetPropertyValue;
  410.             }
  411.         }
  412.         $result = $typeConverter->convertFrom($source, $targetType, $convertedChildProperties, $configuration);
  413.  
  414.         if ($result instanceof \TYPO3\Flow\Error\Error) {
  415.             $this->messages->forProperty(implode('.', $currentPropertyPath))->addError($result);
  416.         }
  417.         return $result;
  418.     }
  419. ?>
  420.  
  421.  
  422. QUESTIONS ARE:
  423. -- should not be possible to create more fathers, each with many of his sons, at once? How to fix as that is possible without special configurations_
  424. -- isn't it somehow mis-programmed that the first case above goes thru and the second fails? and why so?
  425.  
  426. thks
Advertisement
Add Comment
Please, Sign In to add comment