Как использовать коллекции в наборе полей factory в ZF2

Я разрабатываю проект с ZF2 и Doctrine. Я пытаюсь использовать Doctrine Hydrator в создании формы, как показано в этот учебник. В этом методе объект ObjectManager создается в контроллере и передается в новую форму при его создании. Передача объекта ObjectManager из контроллера в форму создает проблему, когда я хочу использовать ZF2 FormElementManager, потому что ZF2 требует, чтобы я получил экземпляр класса формы через Zend\Form\FormElementManager вместо прямого его создания. Чтобы обойти это требование, я создал фабрики форм и полей на основе ответа на вопрос Как передать ObjectManager Doctrine в форму через ZF2 FormElementManager. Метод, представленный в ответе на вопрос, работает для типичных элементов fieldset, но мне нужно определить, как включить элемент коллекции. Учебник использует объект ObjectManager в элементе коллекции в родительском наборе полей, и мне нужно выяснить, как добавить коллекцию с помощью factory.

TagFieldset из учебника, которое я пытаюсь подражать:

namespace Application\Form;

use Application\Entity\Tag;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;

class TagFieldset extends Fieldset implements InputFilterProviderInterface
{
    public function __construct(ObjectManager $objectManager)
    {
        parent::__construct('tag');

        $this->setHydrator(new DoctrineHydrator($objectManager))
             ->setObject(new Tag());

        $this->add(array(
            'type' => 'Zend\Form\Element\Hidden',
            'name' => 'id'
        ));

        $this->add(array(
            'type'    => 'Zend\Form\Element\Text',
            'name'    => 'name',
            'options' => array(
                'label' => 'Tag'
            )
        ));
    }

    public function getInputFilterSpecification()
    {
        return array(
            'id' => array(
                'required' => false
            ),

            'name' => array(
                'required' => true
            )
        );
    }
}

новый TagFieldsetFactory:

namespace Application\Form;

use Zend\Form\Fieldset;
use Application\Entity\Tag;

class TagFieldsetFactory
{
    public function __invoke($formElementManager, $name, $requestedName)
    {
        $serviceManager = $formElementManager->getServiceLocator();
        $hydrator = $serviceManager->get('HydratorManager')->get('DoctrineEntityHydrator');

        $fieldset = new Fieldset('tags');
        $fieldset->setHydrator($hydrator);
        $fieldset->setObject(new Tag);

        //... add fieldset elements.
        $fieldset->add(['...']);
        //...

        return $fieldset;
    }
}

BlogPostFieldset из учебника, которое я пытаюсь подражать:

namespace Application\Form;

use Application\Entity\BlogPost;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;

class BlogPostFieldset extends Fieldset implements InputFilterProviderInterface
{
    public function __construct(ObjectManager $objectManager)
    {
        parent::__construct('blog-post');

        $this->setHydrator(new DoctrineHydrator($objectManager))
             ->setObject(new BlogPost());

        $this->add(array(
            'type' => 'Zend\Form\Element\Text',
            'name' => 'title'
        ));

        $tagFieldset = new TagFieldset($objectManager);
        $this->add(array(
            'type'    => 'Zend\Form\Element\Collection',
            'name'    => 'tags',
            'options' => array(
                'count'           => 2,
                'target_element' => $tagFieldset
            )
        ));
    }

    public function getInputFilterSpecification()
    {
        return array(
            'title' => array(
                'required' => true
            ),
        );
    }
}

новый BlogPostFieldsetFactory:

namespace Application\Form;

use Zend\Form\Fieldset;
use Application\Entity\BlogPost;

class BlogPostFieldsetFactory
{
    public function __invoke($formElementManager, $name, $requestedName)
    {
        $serviceManager = $formElementManager->getServiceLocator();
        $hydrator = $serviceManager->get('HydratorManager')->get('DoctrineEntityHydrator');

        $fieldset = new Fieldset('blog_post');
        $fieldset->setHydrator($hydrator);
        $fieldset->setObject(new BlogPost);

        //... add fieldset elements.
        $fieldset->add(['...']);
        //...

        return $fieldset;
    }
}

в module.config.php:

'form_elements' => [
    'factories' => [
        'UpdateBlogPostForm' => 'Application\Form\UpdateBlogPostFormFactory',
        'BlogPostFieldset' => 'Application\Form\BlogPostFieldsetFactory',
        'TagFieldset' => 'Application\Form\TagFieldsetFactory',
    ],
],

Когда я добавляю элементы набора полей В моем новом BlogPostFieldsetFactory я заменяю этот код из исходного набора полей:

$this->add(array(
        'type' => 'Zend\Form\Element\Text',
        'name' => 'title'
));

с этим:

$fieldset->add(array(
        'type' => 'Zend\Form\Element\Text',
        'name' => 'title'
));

Как заменить элемент коллекции из исходного набора полей:

$tagFieldset = new TagFieldset($objectManager);
$this->add(array(
        'type'    => 'Zend\Form\Element\Collection',
        'name'    => 'tags',
        'options' => array(
                'count'           => 2,
                'target_element' => $tagFieldset
        )
));

Ответ 1

Возможно, у меня вопрос неправильный.... но если вы заменили это

$this->add(array(
        'type' => 'Zend\Form\Element\Text',
        'name' => 'title'
));

с этим:

$fieldset->add(array(
        'type' => 'Zend\Form\Element\Text',
        'name' => 'title'
));

то вы, вероятно, можете заменить это:

$tagFieldset = new TagFieldset($objectManager);
$this->add(array(
        'type'    => 'Zend\Form\Element\Collection',
        'name'    => 'tags',
        'options' => array(
                'count'           => 2,
                'target_element' => $tagFieldset
        )
));

с этим:

$tagFieldset = new TagFieldset($objectManager);
$fieldset->add(array(
        'type'    => 'Zend\Form\Element\Collection',
        'name'    => 'tags',
        'options' => array(
                'count'           => 2,
                'target_element' => $tagFieldset
        )
));

теперь, если вы не можете передать объект $objectManger в форму... ну, если вы посмотрите на код, у вас есть эта вещь, доступная $serviceManager, эта вещь выглядит как контейнер DI, и вы наверняка сможете получить экземпляр $objectManager оттуда, а если нет, вы можете, вероятно, поместить его экземпляр внутри.

Таким образом, окончательный код, вероятно, заканчивается, выглядит следующим образом:

$objectManager =  $serviceManager->get('DoctrineObjectManager') //or something like this
$tagFieldset = new TagFieldset($objectManager);
$fieldset->add(array(
        'type'    => 'Zend\Form\Element\Collection',
        'name'    => 'tags',
        'options' => array(
                'count'           => 2,
                'target_element' => $tagFieldset
        )
));