Zend\Form

Beginning with Zend Framework 2.1, forms elements can be registered using a designated plugin manager of
Zend\ServiceManager. This is similar to how view helpers, controller plugins, and
filters are registered. This new feature has a number of benefits, especially when you need to handle complex
dependencies in forms/fieldsets. This section describes all the benefits of this new architecture in ZF 2.1.

By implementing the Zend\InputFilter\InputProviderInterface interface, we are hinting to our form
object that this element provides some default input rules for filtering and/or validating values. In this
example the default input specification provides a Zend\Filter\StringTrim filter and a Zend\Validator\Regex
validator that validates that the value optionally has a + sign at the beginning and is followed by 11 or 12
digits.

The easiest way of start using your new custom element in your forms is to use the custom element’s FQCN:

If you don’t want to use the custom element’s FQCN, but rather a short name, as of Zend Framework 2.1 you can do so
by adding them to the Zend\Form\FormElementManager plugin manager by utilising the getFormElementConfig function.

Warning

To use custom elements with the FormElementManager needs a bit more work and most likely a change in how you write and
use your forms.

First, add the custom element to the plugin manager, in your Module.php class:

You can use a factory instead of an invokable in order to handle dependencies in your elements/fieldsets/forms.

And now comes the first catch.

If you are creating your form class by extending Zend\Form\Form, you must not add the custom element in the
__construct-or (as we have done in the previous example where we used the custom element’s FQCN),
but rather in the init() method:

The biggest gain of this is that you can easily override any built-in Zend Framework form elements if they do not fit your needs.
For instance, if you want to create your own Email element instead of the standard one, you can simply create your element and add it to
the form element config with the same key as the element you want to replace:

Now, whenever you’ll create an element whose type is ‘Email’, it will create the custom Email element instead of the built-in one.

Note

if you want to be able to use both the built-in one and your own one, you can still provide the FQCN of the element,
i.e. Zend\Form\Element\Email.

As you can see here, we first get the form manager (that we modified in our Module.php class), and create the form by specifying the fully
qualified class name of the form. Please note that you don’t need to add Application\Form\MyForm to the invokables array. If it is not
specified, the form manager will just instantiate it directly.

In short, to create your own form elements (or even reusable fieldsets !) and be able to use them in your form using the short-name notation, you need to:

Create your element (like you did before).

Add it to the form element manager by defining the getFormElementConfig, exactly like using getServiceConfig() and getControllerConfig.

Make sure the custom form element is not added in the form’s __construct-or, but rather in it’s init() method, or after getting an instance of the form.

Create your form through the form element manager instead of directly instantiating it.

One of the most complex issues in Zend\Form2.0 was dependency management. For instance, a very frequent use case
is a form that creates a fieldset, that itself need access to the database to populate a Select element. Previously
in such a situation, you would either rely on the Registry using the Singleton pattern, or either you would “transfer”
the dependency from controller to form, and from form to fieldset (and even from fieldset to another fieldset if you
have a complex form). This was ugly and not easy to use. Hopefully, Zend\ServiceManager solves this use case in an
elegant manner.

For instance, let’s say that a form create a fieldset called AlbumFieldset:

Let’s now create the AlbumFieldset that depends on an AlbumTable object that allows you to fetch albums from the
database.

1
2
3
4
5
6
7
8
9
10
11
12
13

namespaceApplication\Form;useAlbum\Model\AlbumTable;useZend\Form\Fieldset;classAlbumFieldsetextendsFieldset{publicfunction__construct(AlbumTable$albumTable){// Add any elements that need to fetch data from database// using the album table !}}

For this to work, you need to add a line to the form element manager by adding
an element to your Module.php class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

namespaceApplication;useApplication\Form\AlbumFieldset;useZend\ModuleManager\Feature\FormElementProviderInterface;classModuleimplementsFormElementProviderInterface{publicfunctiongetFormElementConfig(){returnarray('factories'=>array('AlbumFieldset'=>function($sm){// I assume here that the Album\Model\AlbumTable// dependency have been defined too$serviceLocator=$sm->getServiceLocator();$albumTable=$serviceLocator->get('Album\Model\AlbumTable');$fieldset=newAlbumFieldset($albumTable);return$fieldset;}));}}

Create your form using the form element manager instead of directly
instantiating it:

Et voilà! The dependency will be automatically handled by the form element manager, and you don’t need to create the
AlbumTable in your controller, transfer it to the form, which itself passes it over to the fieldset.

In the previous example, we explicitly defined the dependency in the constructor of the AlbumFieldset class.
However, in some cases, you may want to use an initializer (like Zend\ServiceManager\ServiceLocatorAwareInterface)
to inject a specific object to all your forms/fieldsets/elements.

The problem with initializers is that they are injected AFTER the construction of the object, which means that if you
need this dependency when you create elements, it won’t be available yet. For instance, this example won’t work:

namespaceApplication\Form;useAlbum\Model;useZend\Form\Fieldset;useZend\ServiceManager\ServiceLocatorAwareInterface;useZend\ServiceManager\ServiceLocatorInterface;classAlbumFieldsetextendsFieldsetimplementsServiceLocatorAwareInterface{protected$serviceLocator;publicfunction__construct(){// Here, $this->serviceLocator is null because it has not been// injected yet, as initializers are run after __construct}publicfunctionsetServiceLocator(ServiceLocatorInterface$sl){$this->serviceLocator=$sl;}publicfunctiongetServiceLocator(){return$this->serviceLocator;}}

Thankfully, there is an easy workaround: every form element now implements the new interface
Zend\Stdlib\InitializableInterface, that defines a single init() function. In the context of form elements,
this init() function is automatically called once all the dependencies (including all initializers) are resolved.
Therefore, the previous example can be rewritten as such:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

namespaceApplication\Form;useAlbum\Model;useZend\Form\Fieldset;useZend\ServiceManager\ServiceLocatorAwareInterface;useZend\ServiceManager\ServiceLocatorInterface;classAlbumFieldsetextendsFieldsetimplementsServiceLocatorAwareInterface{protected$serviceLocator;publicfunctioninit(){// Here, we have $this->serviceLocator !!}publicfunctionsetServiceLocator(ServiceLocatorInterface$sl){$this->serviceLocator=$sl;}publicfunctiongetServiceLocator(){return$this->serviceLocator;}}