15

I have followed an example and would like to pass the Database adapter to a fieldset to create a drop down menu.

The code below is how i call the fieldset.
How can i access the database adapter in the BrandFieldset class?

$this->add(array(
    'type' => 'Application\Form\BrandFieldset',
    'name' => 'brand',
    'options' => array(
        'label' => 'Brand of the product',
    ),
));
Cœur
  • 37,241
  • 25
  • 195
  • 267
Matt
  • 383
  • 1
  • 5
  • 20

2 Answers2

3

Instantiating a fieldset is responsibility of the FormElementManager. When you try to access a form, form element or fieldset, the FormElementManager knows where to find and how to create it. This behaviour summerized in Default Services section of the framework.

Since the proper way of accessing form elements is retrieving them from FormElementManager, I would write a BrandFieldsetFactory to inject that DB adapter or further dependencies to fieldset on construction to achieve this.

A ZF3 friendly fieldset factory would look like:

<?php
namespace Application\Form\Factory;

use Application\Form\BrandFieldset;
use Interop\Container\ContainerInterface;

class BrandFieldsetFactory
{
    /**
     * @return BrandFieldset
     */
    public function __invoke(ContainerInterface $fem, $name, array $options = null)
    {
        // FormElementManager is child of AbstractPluginManager 
        // which makes it a ContainerInterface instance
        $adapter = $fem->getServiceLocator()->get('Your\Db\Adapter');
        return new BrandFieldset($adapter);
    }
}

At this point, BrandFieldset should extend the Zend\Form\Fieldset\Fieldset and it's constructor may look like following:

private $dbAdapter;

/**
 * {@inheritdoc}
 */
public function __construct(My/Db/Adapter $db, $options = [])
{
    $this->dbAdapter = $db;
    return parent::__construct('brand-fieldset', $options);
}

Finally, in module.config.php file I'd have a configuration to tell FormElementManager about this factory:

<?php

use Application\Form\BrandFieldset;
use Application\Form\Factory\BrandFieldsetFactory;

return [
    // other config

    // Configuration for form element manager
    'form_elements' => [
        'factories' => [
            BrandFieldset::class => BrandFieldsetFactory::class
        ],
    ],
];

HINT: The BrandFieldset::init() method will be called automatically by FormElementManager after construction. You can put any post-initialization logic into this method.

edigu
  • 9,878
  • 5
  • 57
  • 80
  • Can this be used in ZF2? I'm having trouble getting it to work. – Matt May 01 '17 at 20:41
  • Sure, after make the factory ZF2 friendly, it should work. Just add a createService() method into the factory just like other zf2 factories and call _invoke from that method. – edigu May 01 '17 at 20:56
  • I'm not sure what you mean. If I var dump the $db in the fieldset I get the name of the class ("brandfieldset"). It doesn't look like the factory is ever called (if I exit or var dump in the factory nothing happends) – Matt May 01 '17 at 21:18
  • The Factory never seems to get called. public function __invoke(ContainerInterface $fem, $name, array $options = null) { echo 'does this work'; exit; // FormElementManager is child of AbstractPluginManager // which makes it a ContainerInterface instance $adapter = $fem->getServiceLocator()->get('Your\Db\Adapter'); return new BrandFieldset($adapter); } – Matt May 02 '17 at 20:07
1

Based of these docs I was able to find a solution.

https://framework.zend.com/manual/2.1/en/modules/zend.form.advanced-use-of-forms.html

'form_elements' => array(
    'invokables' => array(
        'fieldset' => BrandFieldsetFactory::class
    )
)

I needed to call the form using the service locator in the controller like below.

$sl = $this->getServiceLocator();
$form = $sl->get('FormElementManager')->get('Application\Form\CreateForm');

In addition I changed the __construct to init.

Matt
  • 383
  • 1
  • 5
  • 20