ZF2 Form and Doctrine 2 modify the value_options

2019-07-26 18:26发布

问题:

I am using Doctrine 2 in my Zend Framework 2 Project. I have now created a Form and create one of my Dropdowns with Values from the Database. My Problem now is that I want to change which values are used and not the one which I get back from my repository. Okay, here some Code for a better understanding:

$this->add(
            array(
                    'type' => 'DoctrineModule\Form\Element\ObjectSelect',
                    'name' => 'county',

                    'options' => array(
                            'object_manager' => $this->getObjectManager(),
                            'label' => 'County',
                            'target_class'   => 'Advert\Entity\Geolocation',
                            'property'       => 'county',
                            'is_method' => true,
                            'empty_option' => '--- select county ---',
                            'value_options'=> function($targetEntity) {
                                $values = array($targetEntity->getCounty() => $targetEntity->getCounty());
                                return $values;
                            },

                            'find_method'        => array(
                                    'name'   => 'getCounties',
                            ),
                    ),
                    'allow_empty'  => true,
                    'required'     => false,
                    'attributes' => array(
                            'id' => 'county',
                            'multiple' => false,
                    )
            )
    ); 

I want to set the value for my Select to be the County Name and not the ID. I thought that I would need the 'value_options' which needs an array. I tried it like above, but get the

Error Message: Argument 1 passed to Zend\Form\Element\Select::setValueOptions() must be of the type array, object given

Is this possible at all?

回答1:

I was going to suggest modifying your code, although after checking the ObjectSelect code i'm surprised that (as far as I can tell) this isn't actually possible without extending the class. This is because the value is always generated from the id.

I create all form elements using factories (without the ObjectSelect), especially complex ones that require varied lists.

Alternative solution

First create a new method in the Repository that returns the correct array. This will allow you to reuse that same method should you need it anywhere else (not just for forms!).

class FooRepository extends Repository
{
    public function getCounties()
    {
        // normal method unchanged, returns a collection
        // of counties
    }

    public function getCountiesAsArrayKeyedByCountyName()
    {
        $counties = array();
        foreach($this->getCounties() as $county) {
            $counties[$county->getName()] = $county->getName();
        }
        return $counties;
    }
}

Next create a custom select factory that will set the value options for you.

namespace MyModule\Form\Element;

use Zend\Form\Element\Select;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;

class CountiesByNameSelectFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $formElementManager)
    {
        $element = new Select;
        $element->setValueOptions($this->loadValueOptions($formElementManager));

        // set other select options etc
        $element->setName('foo')
                ->setOptions(array('foo' => 'bar'));

        return $element;
    }

    protected function loadValueOptions(ServiceLocatorInterface $formElementManager)
    {
        $serviceManager = $formElementManager->getServiceLocator();
        $repository = $serviceManager->get('DoctrineObjectManager')->getRepository('Foo/Entity/Bar');

        return $repository->getCountiesAsArrayKeyedByCountyName();
    }

}

Register the new element with the service manager by adding a new entry in Module.php or module.config.php.

// Module.php
public function getFormElementConfig()
{
    return array(
        'factories' => array(
            'MyModule\Form\Element\CountiesByNameSelect'
                => 'MyModule\Form\Element\CountiesByNameSelectFactory',
        ),
    );
}

Lastly change the form and remove your current select element and add the new one (use the name that you registered with the service manager as the type key)

$this->add(array(
    'name' => 'counties',
    'type' => 'MyModule\Form\Element\CountiesByNameSelect',
));

It might seem like a lot more code (because it is) however you will benefit from it being a much clearer separation of concerns and you can now reuse the element on multiple forms and only need to configure it in one place.