Handling dependencies in Zend Framework 2 Forms

zend-form, zend-framework2

Solution

So I got this to work with a few minor changes:

As you noted the `PropertyFieldSet` should call the parents construct like so:

parent::__construct('propertyfieldset'); 

ElementConfig should be like so:

public function getFormElementConfig() {
    return array(
        'factories' => array(
            'PropertyFieldset' => function($sm) {
                $serviceLocator = $sm->getServiceLocator();
                $property_type = $serviceLocator->get('Ctmm\Model\PropertyType');
                $fieldset = new PropertyFieldset($property_type);
                return $fieldset;
            },
        )
    );
}

And the AddPropertyForm should be like so:

namespace Ctmm\Form;
use Zend\Form\Form;

class AddPropertyForm extends Form {

    public function init() {

        parent::__construct('AddProperty');

        $this->setName('addProperty');
        $this->setAttribute('method', 'post');

        $this->add(array(
            'name' => 'addproperty',
            'type' => 'PropertyFieldset',
        ));
    }
}

Instead of using __construct we use init(). This function is apparently called when instantiated by factory: http://framework.zend.com/apidoc/2.1/classes/Zend.Form.Form.html#init

Regarding building the select, I would pass a TableGateway object to the fieldSet instead of a model. Then using a fetchAll function we could do the following in the form:

class PropertyFieldset extends Fieldset {

    public function __construct(PropertyTypeTable $propertyTypeTable) {
        parent::__construct('propertyfieldset');


        $propertyValOpts = array();
        foreach($propertyTypeTable->fetchAll() as $propertyRow) {
            array_push($propertyValOpts,$propertyRow->property_type);
        }

        $this->add(array(
            'name' => 'property_type',
            'type' => 'Zend\Form\Element\Select',
            'attributes' => array(
                'required' => true,
            ),
            'options' => array(
                'label' => 'Property Type',
                'value_options' => $propertyValOpts
            ),
        ));
    }
}

Hope this helps :)

Problem

I am trying to build a form in ZF2. The problem comes when I want to populate the options array of a Select input element from a database table. A response to this question Zend FrameWork 2 Get ServiceLocator In Form and populate a drop down list by @timdev pointed me to the ZF2 docs where the 'correct' method is described. I followed this carefully but I suspect that they must have left obvious code out assuming I could fill in the gaps as I cannot get it to work. Can anyone see what I am doing wrong? I start with a form to which I add a fieldset: ``` namespace Ctmm\Form; use Zend\Form\Form; class AddPropertyForm extends Form { public function __construct() { parent::__construct('AddProperty'); $this->setName('addProperty'); $this->setAttribute('method', 'post'); $this->add(array( 'name' => 'property', 'type' => 'PropertyFieldset' )); ``` } } I then create the fieldset: ``` namespace Ctmm\Form; use Ctmm\Model; use Zend\Form\Fieldset; class PropertyFieldset extends Fieldset { public function __construct(PropertyType $property_type) { $this->add(array( 'name' => 'property_type', 'type' => 'Zend\Form\Element\Select', 'attributes' => array( 'required' => true, ), 'options' => array( 'label' => 'Property Type', 'value_options' => array( 0 => 'Detached house', 1 => 'Semi-detached house', 2 => 'Terraced house', 3 => 'Bungalow', 4 => 'Maisonette', 5 => 'Flat', 6 => 'Land', 7 => 'Development Opportunity', ), ), )); } ``` } As you can see I inject PropertyType dependency into the fieldset. At this stage, I have not even used this to generate the options array. I have hard coded the array values to avoid adding another source of possible errors. Once I get the form to render I will then try to pull the array data from the PropertyType table. Now I set up the form element manager in my Module.php: ``` namespace Ctmm; use Ctmm\Form\PropertyFieldset; use Zend\ModuleManager\Feature\FormElementProviderInterface; class Module implements FormElementProviderInterface { public function getFormElementConfig() { return array( 'factories' => array( 'PropertyFieldset' => function($sm) { $serviceLocator = $sm->getServiceLocator(); $property_type = $serviceLocator->get('Ctmm\Model\PropertyType'); $fieldset = new PropertyFieldset($property_type); } ) ); } ``` } This code is straight from the docs. I have tried adding ``` return $fieldset; ``` to the PropertyFieldset factory, and I have even tried adding ``` 'invokables' => array( 'PropertyFieldset' => 'Ctmm\Form\PropertyFieldset' ) ``` to the getFormElementConfig array, as well as replacing the factory with the invokable. The last step is to create the form in my controller action using the form element manager: ``` public function addAction() { $formManager = $this->serviceLocator->get('FormElementManager'); $form = $formManager->get('Ctmm\Form\AddPropertyForm'); } ``` Whatever I do, I get an error saying the Servicemanager cannot create the PropertyFieldset: Zend\ServiceManager\Exception\ServiceNotFoundException File: ``` /home/mike/public_html/ctmm/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php:456 ``` Message: ``` Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for PropertyFieldset ``` Stack trace: ``` #0 /home/mike/public_html/ctmm/vendor/zendframework/zendframework/library/Zend/ServiceManager/AbstractPluginManager.php(103): Zend\ServiceManager\ServiceManager->get('PropertyFieldse...', true) #1 /home/mike/public_html/ctmm/vendor/zendframework/zendframework/library/Zend/Form/Factory.php(110): Zend\ServiceManager\AbstractPluginManager->get('PropertyFieldse...') #2 /home/mike/public_html/ctmm/vendor/zendframework/zendframework/library/Zend/Form/Form.php(145): Zend\Form\Factory->create(Array) #3 /home/mike/public_html/ctmm/module/Ctmm/src/Ctmm/Form/AddPropertyForm.php(33): Zend\Form\Form->add(Array) ``` Line 33 in AddPropertyForm.php is where I try to add my custom PropertyFieldset. Clearly I have an error in the fieldset itself or in the way I am declaring it. I have tried without injecting the PropertyType dependency, but that makes no difference. For completeness, the code for my PropertyType model is: ``` namespace Ctmm\Model; class PropertyType { public $id; public $property_type; protected $adapter; public function __construct($adapter) { $this->adapter = $adapter; } public function exchangeArray($data) { $this->id = (isset($data['id'])) ? $data['id'] : null; $this->property_type = (isset($data['property_type'])) ? $data['property_type'] : null; } public function getPropertyType() { return $this->property_type; } public function fetchAll() { $sql_query = "SELECT id, property_type from property_type"; $statement = $this->adapter->createStatement($sql_query); $results = $statement->execute(); return $results; } ``` } Edit: I don't have an answer but I have done some more investigation. I created a fieldset directly in the controller to test my PropertyFieldset class and it's dependent model. ``` $property_type = $this->getServiceLocator()->get('Ctmm\Model\PropertyType'); $fieldset = new PropertyFieldset($property_type); ``` This did not work immediately. First, I had to take the hinting out of the Fieldset contructor ``` public function __construct(PropertyFieldset $property_type) { ``` became ``` public function __construct($property_type) { ``` I then had to add ``` parent::__construct('propertyfieldset'); ``` before it would allow me to add an element. Once I had added these changes I was able to create a PropertyFieldset object in the controller. I could test this by var_dump()ing it. Unfortunately, these changes to the PropertyFieldset class did not fix the basic problem, so that when I try to create the form in the controller, it generates the same error as before. I have, at least exonerated the PropertyFieldset class and it's dependent model, which says to me that I have something wrong in the getFormElementConfig() in my Module.php class

Original source

Related problems