ZF2, Register a custom input filter and access it from form factory?

1k views Asked by At

I have a Form, FormFactory and and InputFilter. I want to instantiate a form and assign input filter to the form on createService() method.

Module has getFormElementConfig() method and getInputFilterConfig()

When I try to access $serviceManager->get('InputFilterManager')->get('Page\Form\NewsFormInputFilter') from form factory, I get following error mesage

Invalid filter specification provided; was neither a filter instance nor an array specification

module/Page/Module.php

<?php
namespace Page;

use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
use Zend\ModuleManager\Feature\ConfigProviderInterface;

class Module implements AutoloaderProviderInterface, ConfigProviderInterface
{
    public function getConfig()
    {
        return include __DIR__ . '/config/module.config.php';
    }

    public function getFormElementConfig()
    {
        return array(
            'factories' => array(
                'Page\Form\NewsForm' => 'Page\Form\NewsFormFactory',
            )
        );
    }

    public function getAutoloaderConfig()
    {
        return array(
            'Zend\Loader\StandardAutoloader' => array(
                'namespaces' => array(
                    __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__
                )
            )
        );
    }

    public function getInputFilterConfig()
    {
        return array(
            'invokables' => array(
                'Page\Form\NewsFormInputFilter' => 'Page\Form\NewsFormInputFilter'
            )
        );
    }

}

module/Page/src/Page/Form/NewsFormFactory.php

<?php
namespace Page\Form;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\MutableCreationOptionsInterface;
use Page\Form\NewsForm;


class NewsFormFactory implements FactoryInterface, MutableCreationOptionsInterface
{
    protected $options;

    public function setCreationOptions(array $options)
    {
        $this->options = $options;
    }

    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $serviceManager = $serviceLocator->getServiceLocator();

        $form = new NewsForm();
        $form->setTranslator($serviceManager->get('translator'));
        $form->setInputFilter($serviceManager->get('InputFilterManager')->get('Page\Form\NewsFormInputFilter'));

        return $form;
    }
}

module/Page/src/Page/Form/NewsForm.php

<?php
namespace Page\Form;

use Zend\Form\Element;
use Zend\InputFilter;
use Zend\Form\Form;
use Zend\Form\Element\Select;
use Zend\InputFilter\InputFilterInterface;
use Zend\I18n\Translator\TranslatorAwareTrait;

class NewsForm extends Form
{
    use TranslatorAwareTrait;

    public function __construct($name = null)
    {
        parent::__construct('News');
    }

    public function init()
    {
        $category = new Select('category');
        $category->setValueOptions($this->getNewsCategoryList());
        $this->add($category);
    }

    private function getNewsCategoryList()
    {
        // implementation
    }
}

module/Page/src/Page/Form/NewsFormInputFilter.php

<?php
namespace Page\Form;

use Zend\Filter\StringTrim;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\InputFilter\InputFilter;
use Zend\Form\Element;

class NewsFormInputFilter extends InputFilter
{
    public function init()
    {
        $this->add(
            array(
                'name' => 'category',
                'required' => true,
                'filters' => array(
                    'name' => 'StringTrim'
                )
            )
        );
    }
}

According to Module Manager Listeners, getInputFilterConfig() is available in Module.php

I'm not sure what I'm doing wrong. Any idea is welcome.

Edit:

According to InputFilter and explained as @Purple Hexagon I have removed InputFilterProviderInterface implementation from NewsFormInputFilter and extended only from InputFilter

Also removed duplicate implementation of $inputFilter property and getter method from NewsForm

2

There are 2 answers

4
Wilt On

Most likely you have a configuration issue. I think this line is most likely not returning anything:

$serviceManager->get('InputFilterManager')->get('Page\Form\NewsFormInputFilter');

I see you declared 'Page\Form\NewsFormInputFilter' in your Module.php getInputFilterConfig() method. But I don't see this InputFilter in your code. Do you have a file for that class?

You should have an input filter class named NewsFormInputFilter in the namespace Page\Form.

UPDATE

Try once to remove the getInputFilterSpecification and don't implement the InputFilterProviderInterface. It seems to me that you want init to be called. You don't need the interface and the getInputFilterSpecification method for that.

Make sure your module class implements Zend\ModuleManager\Feature\InputFilterProviderInterface.

Are you sure your files are in the Page module?

0
Purple Hexagon On

InputFilter classes do not need to implement InputFilterProviderSpecification. Also you need to provider the filters array section with an array for each filter (As below). You have the name key in the filters section rather than wrapped in an array in NewsFormInputFilter::init The following code is fine for your implementation

<?php
namespace Page\Form;

use Zend\Filter\StringTrim;
use Zend\InputFilter\InputFilter;
use Zend\Form\Element;


class NewsFormInputFilter extends InputFilter
{
    public function init()
    {
        $this->add(
          array(
            'name' => 'name',
            'required' => true,
            'filters' => array(
                array(
                    'name' => 'StringTrim'
                )
            )
          )
    );
    }
}

I have tested the above code and it works fine other than NewsForm::getNewsCategoryList not being implemented.

There are a few other bits that I'm not really sure why you are doing them:

1: in NewsForm class you override setInputFilter method and define the property again. This is all done for you in Zend\Form\Form class, which you're extending.

2: Seems strange that you have the getters and setters for serviceLocator defined in your form. They're not being used and are a pretty big code smell anyway. You have a factory for dependency injection already. Guess you might end up using in getNewsCategoryList but would be better to use constructor / setter injection of the dependency.

3: I wouldn't advise instantiating Element classes in your form directly. It will work out a lot better in the long run if you always use specification. Like:

public function init()
{
    $this->add(
        array(
            'name' => 'title',
            'type' => 'Zend\\Form\\Element\\Select',
            'options' => array(
                'empty_option' => 'Please Select A Value...',
                'label' => 'Title',
                'value_options' => $this->getNewsCategoryList()
            ),
            'attributes' => array(
                'required' => 'required',
            )
        )
    )
}