3
votes

I would like to create custom form elements in ZF2, which requires FormElementManager. I am currently using Doctrine Hydrator in the form creation as shown in this tutorial. In this method, an ObjectManager object is created in the controller and passed to the new form when it is instantiated:

public function editAction()
{
    // Get your ObjectManager from the ServiceManager
    $objectManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');

    // Create the form and inject the ObjectManager
    $form = new UpdateBlogPostForm($objectManager);

    // …

the update form:

namespace Application\Form;

use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Form;

class UpdateBlogPostForm extends Form
{
    public function __construct(ObjectManager $objectManager)
    {
        parent::__construct('update-blog-post-form');

        // The form will hydrate an object of type "BlogPost"
        $this->setHydrator(new DoctrineHydrator($objectManager));

        // Add the user fieldset, and set it as the base fieldset
        $blogPostFieldset = new BlogPostFieldset($objectManager);
        $blogPostFieldset->setUseAsBaseFieldset(true);
        $this->add($blogPostFieldset);

        // … add CSRF and submit elements …

        // Optionally set your validation group here
    }
}

the BlogPost fieldset:

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());

        // … add fieldset elements …

    }

    // … public function getInputFilterSpecification() …

}

Unfortunately, in order to use ZF2's FormElementManager, the ZF2 manual says, "The second catch is that you must not directly instantiate your form class, but rather get an instance of it through the Zend\Form\FormElementManager;" so I have to get the form like this:

 public function editAction()
 {
     $sl = $this->getServiceLocator();
     $form = $sl->get('FormElementManager')->get('\Application\Form\UpdateBlogPostForm');

Is there a way to pass the ObjectManager object $objectManager to the form through FormElementManager, or is there a way to create the object within the form so the hydrator and the fieldset can use it?

1

1 Answers

4
votes

DoctrineEntityHydratorFactory

A factory that creates the hydrator and injects the object manager.

namespace MyModule\Stdlib\Hydrator;

use DoctrineORMModule\Stdlib\Hydrator\DoctrineEntity;

class DoctrineEntityHydratorFactory
{
    public function __invoke($hydratorPluginManager, $name, $requestedName)
    {
        $serviceManager = $hydratorPluginManager->getServiceLocator();
        $objectManager  = $serviceManager->get('Doctrine\ORM\EntityManager');

        return new DoctrineEntity($objectManager);
    }
}

BlogPostFieldset

A reusable fieldset that contains all the elements for a Blog post and uses the EntityHydrator to hydrate a BlogPost entity.

namespace MyModule\Form;

use Zend\Form\Fieldset;

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;
    }
}

UpdateBlogPostFormFactory

A factory to create the blog post update form. This attaches the fieldset and sets the use_as_base_fieldset option which allows it to use the fieldsets hydrator.

namespace MyModule\Form;

class UpdateBlogPostFormFactory
{
    public function __invoke($formElementManager, $name, $requestedName)
    {
        $form = new Form('update_blog_post);

        //...
        $factory = new \Zend\Form\Factory($formElementManager);
        $form->setFormFactory($factory);
        $form->add([
            'name' => 'blog_post',
            'type' => 'BlogPostFieldset',
            'options' => [
                'use_as_base_fieldset' => true,   
            ]
        ]);
        //...

        return $form;
    }
}

You will also need to register the services with the required managers in module.config.php.

return [
    'form_elements' => [
        'factories' => [
            'UpdateBlogPostForm' => 'MyModule\Form\UpdateBlogPostFormFactory',
            'BlogPostFieldset' => 'MyModule\Form\BlogPostFieldsetFactory',
        ],
    ],
    'hydrators' => [
        'factories' => [
            'DoctrineEntityHydrator' => 'MyModule\Stdlib\Hydrator\DoctrineEntityHydratorFactory',
        ],
    ],
];

So with the above you can request your form in the controller.

$form = $formElementManager->get('UpdateBlogPostForm');