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:
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:
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.
Now we simply need to move this method over to our new FormBuilder class and name it something meaningful, like “newForm”.
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:
And modify the AlbumController to use it via the service locator.
Now with the logic better encapsulated away from the controller, let’s simply set the value options directly within the builder class.
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.
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:
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.
Now we modify FormBuilder to take the GenreTable as a constructor argument and use it to populate the value options.
Finally, we update module/Album/Module.php to inject an instance of the new 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.