Over a million developers have joined DZone.

Using Select In Zend Framework 2

· Web Dev Zone

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

I’ve been using Zend Framework 2 on a recent project as a way to asses it’s features and one of the early blockers I had was trying to figure out how the heck to use the Select form element. As of this writing there is no good documentation on how to use it, so I’ll instead introduce how to use it in this blog post.

Album Manager, Revisited

For this demonstration I’ll be using the zend-skeleton application I originally worked on following through the zend framework 2 tutorial. I’ve left off from my previous post where we DRY’d up our code by using form annotations. You can follow me in this post by checking out my zend-skeleton project on github and checking out the “select-beginning” tag.

To start with, we have our annotated Album model object with just the title and artist:

<?php
// module/Album/src/Album/Model/Album.php:
namespace Album\Model;

use Zend\Form\Annotation as Form;

class Album{
    /**
* @Form\Required(false)
* @Form\Attributes({"type":"hidden"})
*/
    public $id;

    /**
* @Form\Required(true)
* @Form\Attributes({"type":"text"})
* @Form\Options({"label":"Artist"})
* @Form\Filter({"name":"StringTrim"})
* @Form\Validator({"name":"StringLength", "options":{"min":1, "max":100}})
*/
    public $artist;

    /**
* @Form\Required(true)
* @Form\Attributes({"type":"text"})
* @Form\Options({"label":"Title"})
* @Form\Filter({"name":"StringTrim"})
* @Form\Filter({"name":"StripTags"})
*/
    public $title;

So we’ve written our application and our customer is pleased, however he’d like to be able to tag the albums with a genre. When adding or editing a new album, there should be a genre drop down populated with options like Rap, Jazz, Alternative, Rock and Country.

Turns out this is a breeze, we just need to add a new field to our model and a Select form element:

 /**
* @Form\Type("Zend\Form\Element\Select")
* @Form\Options({"label":"Genre", "value_options":{
* "Rap":"Rap",
* "Jazz":"Jazz",
* "Alternative":"Alternative",
* "Rock":"Rock"
* }
* })
*/
    public $genre;

Now comes the boilerplate part, adding it to your views and AlbumTable (soon I’ll blog about replacing TableGateways). I’ll leave this as an exercise for the reader. See the tag select-annotated to see the completed work here. You’ll also need to add the genre column to the album table yourself (boo… no migrations).

Getting Values From an External Source

Commonly we’ll be getting genres from a database table or some other external source, not hard coded as a list of values in an annotation. To understand how to do this better, let’s first extract the value population out of the form annotation and do it elsewhere. The obvious place to manipulate the form after the fact is obviously not within the controller so let’s extract form creation out to something that can encapsulate it. With the lack of any kind of convention our guidance from the framework here, let’s just call it AlbumFormBuilder and go strong. :)

First we create a new class called FormBuilder under module/Album/src/Album/Form/FormBuilder. Previously (as in during the last tutorial) we applied extract method to extract a common method out of the addAction and editAction to handle the form building logic.

 private function form(){
      $builder = new AnnotationBuilder();
      $form = $builder->createForm(new Album());
      $form->add(array(
        'name' => 'submit',
        'attributes' => array(
            'type' => 'submit',
            'value' => 'Add',
            'id' => 'submitbutton',
        ),
      ));
      return $form;
    }

Now we simply need to move this method over to our new FormBuilder class and name it something meaningful, like “newForm”.

<?php
namespace Album\Form;

use Zend\Form\Annotation\AnnotationBuilder;
use Album\Model\Album;

class FormBuilder {
    public function newForm(){
        $builder = new AnnotationBuilder();
        $form = $builder->createForm(new Album());
        $form->add(array(
            'name' => 'submit',
            'attributes' => array(
            'type' => 'submit',
            'value' => 'Add',
            'id' => 'submitbutton',
            ),
        ));
        return $form;
    }

}

Now we could simply drop this object into the existing method in the AlbumController and see it still work (and I normally do this while refactoring). But to really harness the power we should create this new builder through ZF2′s factory mechanism. So we open up module/Album/Module.php and the following to the factories section:

public function getServiceConfig(){
        return array(
            'factories' => array(
                'Album\Model\AlbumTable' => function($sm) {
                    $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                    $table = new AlbumTable($dbAdapter);
                    return $table;
                },
                // add this
                'Album\Form\FormBuilder' => function($sm){
                    return new \Album\Form\FormBuilder();
                }
            ),
        );
    }

And modify the AlbumController to use it via the service locator.

 private function form(){
        $sm = $this->getServiceLocator();
        return $sm->get('Album\Form\FormBuilder')->newForm();
    }

Now with the logic better encapsulated away from the controller, let’s simply set the value options directly within the builder class.

...
        $form = $builder->createForm(new Album());

        $form->get('genre')->setValueOptions(
            array(
                "Alternative",
                "Country",
                "Jazz",
                "Rap",
                "Rock"
            )
        );
   ...

Now if we save this and check out the app, we’ll notice that our new addition completely overrides what was in the annotation. Since we don’t need those anymore, we can remove the value_options attribute from our form annotation. Go ahead and edit or add a new album, select a genre and save it. Notice something? If you use an array of string literals the values will all be the index number of the item while the actual values will be the labels.

We have two options here… either specify keys that represent option values or explicitly define the label and the value (yes this is a bit strange). See the examples below for a quick reference.

/**
 * Generates the following:
 * <option value="alt">Alternative</option>
 * <option value="country">Country</option>
 * <option value="jazz">Jazz</option>
 * <option value="rap">Rap</option>
 * <option value="rock">Rock</option>
 */
array(
    'alt' => 'Alternative',
    'country' => 'Country',
    'jazz' => 'Jazz',
    'rap' => 'Rap',
    'rock' => 'Rock'
)

This actually didn’t give me what I originally expected… I expected to see the array keys as labels and the values as, well, values. However using a keyed array will basically match the same left to right layout an actual select elements option items will have. If you want to be more explicit you can also do the following:

array(
    array('value' => 'alt', 'label' => 'Alternative'),
    array('value' => 'country', 'label' => 'Country'),
    array('value' => 'jazz', 'label' => 'Jazz'),
    array('value' => 'rock', 'label' => 'Rock'),
)

Final Step: Extract Values Out

There was a reason I used ZF2′s dependency management to fetch the new FormBuilder instance and that was to easily extract the value options out to be provided by something else, perhaps another table gateway. So let’s go ahead and move that array to be returned by a new GenreTable.

<?php
// modules/Album/src/Album/Model/GenreTable.php
namespace Album\Model;


class GenreTable {
    public function fetchAllAsArray(){
        return array(
                'alt' => 'Alternative',
                'country' => 'Country',
                'jazz' => 'Jazz',
                'rap' => 'Rap',
                'rock' => 'Rock'
            );
    }
}

Now we modify FormBuilder to take the GenreTable as a constructor argument and use it to populate the value options.

class FormBuilder {
    private $genres;
    public function __construct($genres) {
        $this->genres = $genres;
    }
    public function newForm(){
        $builder = new AnnotationBuilder();
        $form = $builder->createForm(new Album());

        $form->get('genre')->setValueOptions(
            $this->genres->fetchAllAsArray()
        );
    ...
    }
}

Finally, we update module/Album/Module.php to inject an instance of the new GenreTable.

  'Album\Model\GenreTable' => function($sm){
                    return new \Album\Model\GenreTable();
                },
                'Album\Form\FormBuilder' => function($sm){
                    return new \Album\Form\FormBuilder(
                        $sm->get('Album\Model\GenreTable')
                    );
                }

Now we’re free to change the object injected into FormBuilder to fetch genres from the database rather than use an in-memory array when we’re ready but I’m tired of writing TableGateways. In my next post I’ll make my code a bit DRY’er by ripping those table gateways out in favor of doctrine. :)

You can see my completed work for this post on github.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:

Published at DZone with permission of James Carr, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}