doctrine / doctrine-laminas-hydrator

Doctrine hydrators for Laminas applications
https://www.doctrine-project.org/projects/doctrine-laminas-hydrator.html
MIT License
33 stars 19 forks source link

Collection nested collection #63

Closed josemsalves closed 1 year ago

josemsalves commented 1 year ago

Live, I'm using laminas-doctrine-hydrator with 2-level collections in one-to-many relationships. The 2nd level fieldset gives the error "Call to a member function setValue() on null". Is there any limitation? As classes:

class Entity1 {
   /**
@ORM\OneToMany(targetEntity="Entity2", mappedBy="entity1", orphanRemoval=true, cascade={"persist", "remove", "merge"})
    */
   protected $entities2;
}
class Entity2 {
   /**
@ORM\ManyToOne(targetEntity="Entity1", inversedBy="entities2")
    */
   protected $entity1;
   /**
@ORM\OneToMany(targetEntity="Entity3", mappedBy="entity2", orphanRemoval=true, cascade={"persist", "remove", "merge"})
    */
   protected $entities3;
}
class Entity3 {
   /**
@ORM\ManyToOne(targetEntity="Entity2", inversedBy="entities3")
    */
   protected $entity2;
}

o form e fieldset:

class Entity1Form {
   public function __construct(EntityManager $entityManager) {
       // Some elements ...
       $this->add(array(
           'type' => 'Zend\Form\Element\Collection',
           'name' => 'entities2',
           'options' => array(
               'count' => 0,
               'should_create_template' => true,
               'allow_add' => true,
               'allow_remove' => true,
               'target_element' => new Entity2Fieldset($entityManager)
           )
       ));
       $this->setHydrator(new DoctrineObject($entityManager))->setObject(new Entity1());
   }
}
class Entity2Fieldset {
   public function __construct(EntityManager $entityManager) {
       // Some elements ...
       $this->add(array(
           'type' => 'Zend\Form\Element\Collection',
           'name' => 'entities3',
           'options' => array(
               'count' => 0,
               'should_create_template' => true,
               'allow_add' => true,
               'allow_remove' => true,
               'target_element' => new Entity3Fieldset($entityManager)
           )
       ));
       $this->setHydrator(new DoctrineObject($entityManager))->setObject(new Entity2());
   }
}
class Entity3Fieldset {
       public function __construct(EntityManager $entityManager) {
           // Some elements ...
           $this->setHydrator(new DoctrineObject($entityManager))->setObject(new Entity3());
       }
   }
josemsalves commented 1 year ago

https://github.com/josemsalves/testes

driehle commented 1 year ago

@josemsalves

I had a short look in your sample code, but the code under https://github.com/josemsalves/testes has several other (non-related) issues - a simple docker compose up -d doesn't get it running to repreduce the error you mentioned above. Please note that your application is outdated, as it is still using PHP 7.4. I recommend re-creating the project from the laminas-skeleton-application, to enable use of PHP 8.1.

Regarding your error message Call to a member function setValue() on null, you should provide a traceback to show the relevant class/method where the error occurs. One think that I see as problematic in your code snippets above is that you are creating the fieldsets with new ClassName(). Please take a look at the Laminas form documentation on how to use custom elements: https://docs.laminas.dev/laminas-form/v3/advanced/#creating-custom-elements. Elements should be created through the form factory, which initializes the elements correctly.

You should do something like this:

        $this->add([
            'type' => \Laminas\Form\Element\Collection::class,
            'name' => 'mySubFieldsetElement',
            'options' => [
                'allow_add' => true,
                'allow_remove' => true,
                'target_element' => [
                    'type' => \Application\Form\MySubFieldset::class,
                ],
            ],
        ]);

Or, in case you need the target element object itself, try this:

        $targetElement = $this->getFormFactory()->getFormElementManager()->get(\Application\Form\MySubFieldset::class);
        $this->add([
            'type' =>  \Laminas\Form\Element\Collection::class,
            'name' => 'mySubFieldsetElement',
            'options' => [
                'allow_add' => true,
                'allow_remove' => true,
                'target_element' => $targetElement,
            ],
        ]);