5

I know this sounds much more basic, still I want to post my question as it is related to Zend Framework 2. I know this form from the Zend example module

namespace Album\Form;

use Zend\Form\Form;

class AlbumForm extends Form
{
    public function __construct($name = null)
    {
        // we want to ignore the name passed
        parent::__construct('album');
        $this->setAttribute('method', 'post');
        $this->add(array(
            'name' => 'id',
            'attributes' => array(
                'type'  => 'hidden',
            ),
        ));
        $this->add(array(
            'name' => 'artist',
            'attributes' => array(
                'type'  => 'text',
            ),
            'options' => array(
                'label' => 'Artist',
            ),
        ));
        $this->add(array(
            'name' => 'title',
            'attributes' => array(
                'type'  => 'text',
            ),
            'options' => array(
                'label' => 'Title',
            ),
        ));
        $this->add(array(
            'name' => 'submit',
            'attributes' => array(
                'type'  => 'submit',
                'value' => 'Go',
                'id' => 'submitbutton',
            ),
        ));
    }
}

And this is called in this fashion

<?php
$form = $this->form;
$form->setAttribute('action', $this->url('album', array('action' => 'add')));
$form->prepare();

echo $this->form()->openTag($form);
echo $this->formHidden($form->get('id'));
echo $this->formRow($form->get('title'));
echo $this->formRow($form->get('artist'));
echo $this->formSubmit($form->get('submit'));
echo $this->form()->closeTag();

How can I add a drop down list for the artist field where the list is stored in an associative array. Since Im getting into Zend Framework 2, I wanted the suggestions from the experts. I have followed this previous post but it was somewhat unclear to me.

Community
  • 1
  • 1
125369
  • 3,547
  • 20
  • 52
  • 70

1 Answers1

11

This is one way to do it for static options.

....

$this->add(array(
    'type' => 'Zend\Form\Element\Select',
    'name' => 'number'
    'options' array(
        'options' => array( '1' => 'one', '2', 'two' )
    )
));

Be warned....

Because you are creating the form within a constructor you will not have access the ServiceManger. This could cause a problem if you want to populate from a database.

Lets try something like...

class AlbumForm extends Form implements ServiceManagerAwareInterface
{

public function __construct()
{
    ....

    $this->add(array(
        'type' => 'Zend\Form\Element\Select',
        'name' => 'number'
    ));

    ....
}

....

public function initFormOptions()
{
    $this->get('number')->setAttribute('options', $this->getNumberOptions());
}

protected function getNumberOptions()
{
    // or however you want to load the data in
    $mapper = $this->getServiceManager()->get('NumberMapper');
    return $mapper->getList();
}

public function getServiceManager()
{
    if ( is_null($this->serviceManager) ) {
        throw new Exception('The ServiceManager has not been set.');
    }

    return $this->serviceManager;
}

public function setServiceManager(ServiceManager $serviceManager)
{
    $this->serviceManager = $serviceManager;
}

But that's not great, rethink...

Extending the Form so that you can create a form isn't quite right. We are not creating a new type of form, we are just setting up a form. This calls for a factory. Also, the advantages of using a factory here are that we can set it up in a way in which we can use the service manager to serve it up, that way the service manager can inject itself instead of us doing in manually from the controller. Another advantage is that we can invoke this form whenever we have the service manager.

Another point worth making is that where it makes sense, I think it's better to take code out of the controller. The controller is not a script dump so it's nice to have objects look after themselves. What I'm trying to say is that it's good to inject an object with objects it needs, but it's not okay to just hand it the data from the controller because it creates too much of a dependency. Don't spoon feed objects from the controller, inject the spoon.

Anyway, too much rant more code...

class MySpankingFormService implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceManager )
    {
        $mySpankingNewForm = new Form;

        // build that form baby,
        // you have a service manager,
        // inject it if you need to,
        // otherwise just use it.

        return $mySpankingNewForm;
    }
}

controller

<?php

class FooController
{
    ...
    protected function getForm()
    {
        if ( is_null($this->form) ) {
            $this->form =
                $this->getServiceManager()->get('MySpankingFormService');
        }
        return $this->form;
    }
    ...
}

module.config.php

...
'service_manager' => array (
        'factories' => array (
            ...
            'MySpankingFormService'
                => 'MyNameSpacing\Foo\MySpankingFormService',
            ...
thomas-peter
  • 7,738
  • 6
  • 43
  • 58
  • 7
    ZF2 forms are more confusing than ZF1 – Elzo Valugi Sep 18 '12 at 07:23
  • @Elzo - for me it was grasping the DI and runtime configuration. I have been working full time on a project using ZF2 for 9 weeks and I've only just got on my feet. Sort of. – thomas-peter Sep 18 '12 at 07:58
  • @tomwrong, thanks for such a elaborate example, but still as Elzo said ZF2 forms are more confusing and it would be much more helpful for guys like me who will definitely visit this post in future in pursuit of ZF2, in case if could tweak the same code to the standard Album example. I mean your explanation was good, can you please show us how to adapt the same for the standard Album module, especially injecting the servicemanager. May be we are asking too much, but as beginners it is easy to follow the Album module example and interpret your awesome explanation,on the verge of accepting :) – 125369 Sep 18 '12 at 09:06
  • injecting the service manager is done in exactly the same way as you would inject anything else, but calling the public setter from outside of the object. However, this isn't great as you have to keep passing it about like an unwanted orphan. In the example case, it doesn't seem too much of hassle but it quickly gets out of control. I stand by what I said before, putting a factory in the constructor is convenient but it's not a good pattern and certainly not one that fits well with ZF2. at least In my experience. – thomas-peter Sep 18 '12 at 09:19
  • dont' have enough time at the moment to adjust this answer for the Album example, but I'll consider fleshing out an example in a blog post if there is enough interest. – thomas-peter Sep 18 '12 at 09:32
  • that would be great if you could flesh out the example in a blog post, but please do update us when you try to post this in a blog – 125369 Sep 18 '12 at 09:40