Michaël Gallego

This is my blog. What can you expect here? Well... Zend Framework 2, Amazon AWS...

Twitter

Google+

LinkedIn

Github

Last.fm

Discover what's coming for Zend\Form in ZF 2.1

As you may know, the very first minor release (ZF 2.1) of Zend Framework 2 is expected to show up at the end of the month. Following the policy of Zend Framework 2, no BC (breaking-compatibility) is expected in 2.1, so you can (okey… you might, I don’t want any troubles :o) safely migrate to 2.1, and just enjoy the new features.

Thanks to Oleg, once again, here is the russian version (well, I suppose it is a translation, I can’t say it for sure ^_^).

I’m going to talk here about the main new features of Zend\Form, hope you’ll like it !

New Elements

ZF 2.1 comes with two new elements I’ve written, namely DateSelect and MonthSelect. Instead of the traditional Date or Month element (that are rendered as text element or with a date picker in some browsers like Opera), DateSelect and MonthSelect are rendered using three selects (or two for MonthSelect). In some cases (birthdate…) this is by far more convenient for the user that a date picker.

To create such element, this is just deadly simple :

$this->add(array(
    'type'    => 'Zend\Form\Element\DateSelect',
    'name'    => 'birthDate',
    'options' => array(
        'label' => 'Your birthdate',
        'create_empty_option' => true,
    )
));

Those elements accept a lot of new options to finely tune it :

$this->add(array(
    'type'    => 'Zend\Form\Element\DateSelect',
    'name'    => 'birthDate',
    'options' => array(
        'label'               => 'Birthdate',
        'create_empty_option' => true,
        'min_year'            => date('Y') - 30,
        'max_year'            => date('Y') - 18,
        'day_attributes'      => array(
              'style'            => 'width: 22%'
        ),
        'month_attributes'    => array(
             'style'            => 'width: 35%'
        ),
        'year_attributes'     => array(
            'style'            => 'width: 25%'
        )
    )
));

As you can see, this element accepts different options :

  • create_empty_option: if set to true, it will automatically creates an option whose value is empty for all three selects (this is useful for many JavaScript select libraries that ask you an empty option to allow a placeholder).
  • min_year and max_year : the min and max year for the… year select options (captain obvious).
  • day_attributes, month_attributes, year_attributes : those allow to set attributes that will be applied to the selects.

Of course, the MonthSelect works exactly the same way, excepts it does not have any day select.

You might wonder about validation and localization ? Just don’t ! What’s cool is that it automatically validate for you the data. About localization, it uses Intl data, so that the order of the “day-month-year”, as well as the separator (“day-month-year”, “day/month/year”) and even the months names are automatically localized using the current Locale.

Please note too that if you call getValue on such an element, it will returns the date using the following format: ‘Y-m-d’. It is up to you to convert it to DateTime object if you want.

FormElementManager

This is the next big thing. The Form now follows other components and adopt a PluginHelperManager, which allow… well, a lot of things.

Short names

First of all, you won’t need to write the whole type of the element now. This means that this :

$this->add(
    'type' => 'Zend\Form\Element\Url',
    'name' => 'url'
);

Can simply be rewritten too :

$this->add(
    'type' => 'Url',
    'name' => 'url'
);

This works because form elements are now pulled from the Service Manager. This means that you can also override the standard Url element and provide your own.

Dependencies

Pulling objects from ServiceManager also means that dependencies are a lot easier to handle.

Let’s say that your Fieldset needs the ServiceManager. Before, you had to transfer it from Form to Fieldset (and if you use, like me, very deep structure, you may need to transfer it to many many fieldsets… although only the l ast one may use it).

Now, just make your Fieldset implements the ServiceLocatorAwareInterface :

use Zend\Form\Fieldset;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class MyFieldset extends Fieldset implements ServiceLocatorAwareInterface
{
    protected $serviceLocator;

    public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
    {
        $this->serviceLocator = $serviceLocator;
    }

    public function getServiceLocator()
    {
        return $this->serviceLocator;
    }
}

And add the fieldset to your form :

use Zend\Form\Form;

class MyForm extends Form
{
    public function init()
    {
        $this->add(array(
            'type' => 'MyFieldset',
            'name' => 'fieldset'
        ));
    }
}

Now, instead of directly instantiates the fieldset using the name, it will try to pull it from the ServiceManager first. You can also define the dependencies using the new getFormElementConfig function in your Module.php class :

public function getFormElementConfig()
{
    return array(
        'factories' => array(
            'MyFieldset' => function($sm) {
                // Your code
            }
        )
    );
}

Of course, for this to work, you have to instantiate your form using the service manager too. So in your controller, instead of doing that :

public function testAction()
{
    $form = new MyForm();
}

Write this :

public function testAction()
{
    $form = $this->serviceLocator()->get('FormElementManager')->get('MyForm');
}

We first get the FormElementManager class (that is a Plugin Manager class), and then get the form. Please note that you don’t need to declare “MyForm” as an invokables, as unknown strings will be considered by default as invokables.

Also, as most of the dependencies are set through initializers (for example ServiceLocatorAwareInterface), this means that adding elements in constructor is too soon, as the dependencies through initializers are not set yet. This is why we added an “init” function that is called after all the dependencies have been set.

So in your fieldset, instead of adding elements in __construct, just add them in “init” (yeah, like the old ZF1 style !).

Completely new File upload

Last but not least, thanks to cgmartin, the File upload has been completely updated. This is something we did not had time to update for ZF 2.0, and hence, updating file was a mess as it did not work at all with the new architecture of forms.

This is completely solved, and Chris has done a remarkable job for it ! I suggest you to have a look at the examples.

I hope you’ll like ZF 2 forms even more ! Enjoy !