phpzend-frameworkzend-framework2zend-inputfilter

Configure Zend Framework 2 Input Filter Using Plugin Manager


I have followed the zf2 album example. I am now trying to integrate the HTML Purifier.

This is the Module https://github.com/juriansluiman/Soflomo-Purifier#injecting-the-filtermanager

This is the way I have constructed my inputfilter.
http://framework.zend.com/manual/current/en/user-guide/forms-and-actions.html

namespace Album\Model;

 // Add these import statements
 use Zend\InputFilter\InputFilter;
 use Zend\InputFilter\InputFilterAwareInterface;
 use Zend\InputFilter\InputFilterInterface;

 class Album implements InputFilterAwareInterface
 {
     public $id;
     public $artist;
     public $title;
     protected $inputFilter;                       // <-- Add this variable

     public function exchangeArray($data)
     {
         $this->id     = (isset($data['id']))     ? $data['id']     : null;
         $this->artist = (isset($data['artist'])) ? $data['artist'] : null;
         $this->title  = (isset($data['title']))  ? $data['title']  : null;
     }

     // Add content to these methods:
     public function setInputFilter(InputFilterInterface $inputFilter)
     {
         throw new \Exception("Not used");
     }

     public function getInputFilter()
     {
         if (!$this->inputFilter) {
             $inputFilter = new InputFilter();

             $inputFilter->add(array(
                 'name'     => 'id',
                 'required' => true,
                 'filters'  => array(
                     array('name' => 'Int'),
                 ),
             ));

             $inputFilter->add(array(
                 'name'     => 'artist',
                 'required' => true,
                 'filters'  => array(
                     array('name' => 'StripTags'),
                     array('name' => 'StringTrim'),
                 ),
                 'validators' => array(
                     array(
                         'name'    => 'StringLength',
                         'options' => array(
                             'encoding' => 'UTF-8',
                             'min'      => 1,
                             'max'      => 100,
                         ),
                     ),
                 ),
             ));

             $inputFilter->add(array(
                 'name'     => 'title',
                 'required' => true,
                 'filters'  => array(
                     array('name' => 'StripTags'),
                     array('name' => 'StringTrim'),
                 ),
                 'validators' => array(
                     array(
                         'name'    => 'StringLength',
                         'options' => array(
                             'encoding' => 'UTF-8',
                             'min'      => 1,
                             'max'      => 100,
                         ),
                     ),
                 ),
             ));

             $this->inputFilter = $inputFilter;
         }

         return $this->inputFilter;
     }
 }

I am getting the following error.

As such, you get a ServiceNotFoundException: Zend\Filter\FilterPluginManager::get was unable to fetch or create an instance for htmlpurifier. This means the filter plugin manager was lazily instantiated, and does not know about the htmlpurifier plugin.

What is the proper way to construct my input filter to take advantage of this module. Many Thanks, Matt


Solution

  • The problem you're experiencing is caused exactly by what is noted in SoflomoPurifier documentation: since you're manually instantiating with new InputFilter, that instance is never wired with the configuration provided by additional modules.

    To ensure this wiring happens, I would suggest to create a new InputFilter subclass and put your all the InputFilter::add() calls into its init() method:

    use Zend\InputFilter\InputFilter:
    
    class AlbumInputFilter extends InputFilter
    {
        public function init()
        {
            $this->add(array(
                'name'     => 'id',
                'required' => true,
                'filters'  => array(
                    array('name' => 'Int'),
                ),
             ));
    
            $this->add(array(
                'name'     => 'artist',
                'required' => true,
                'filters'  => array(
                    array('name' => 'StripTags'),
                    array('name' => 'StringTrim'),
                ),
                'validators' => array(
                    array(
                        'name'    => 'StringLength',
                        'options' => array(
                            'encoding' => 'UTF-8',
                            'min'      => 1,
                            'max'      => 100,
                        ),
                    ),
                ),
             ));
    
            $this->add(array(
                'name'     => 'title',
                'required' => true,
                'filters'  => array(
                    array('name' => 'StripTags'),
                    array('name' => 'StringTrim'),
                ),
                'validators' => array(
                    array(
                        'name'    => 'StringLength',
                        'options' => array(
                            'encoding' => 'UTF-8',
                            'min'      => 1,
                            'max'      => 100,
                        ),
                    ),
                ),
             ));
        }
    }
    

    change your Album model constructor to use constructor injection:

    use Zend\InputFilter\InputFilterInterface;
    
    class Album
    {
        // etc etc
    
        public function __construct(InputFilterInterface $inputFilter)
        {
            $this->inputFilter = $inputFilter;
        }
    
        // etc etc
    }
    

    and then use a factory to inject it:

    // in your Module.php
    
    public function getServiceConfig()
    {
        return [
            'factories' => [
                'AlbumFactory' => function($serviceManager) {
                    $inputFilterManager = $serviceManager->get('InputFilterManager');
                    return function() use ($inputFilterManager) {
                        $inputFilter = $inputFilterManager->get(AlbumInputFilter::class);
                        return new Album($inputFilter)
                    }
                }
            ]
        ];
    }
    

    invoking InputFilterPluginManager::get() with an input filter FQCN will automatically invoke its constructor, wire it up with other modules, and then invoke its init() method.

    so now, rather than using $album = new Album, you should use the AlbumFactory service to create albums with their input filter correctly injected:

    $createAlbum = $serviceManager->get('AlbumFactory');
    $album = $createAlbum()
    

    obviously you are advised to inject this factory inside any consumer (i.e. controllers) instead of pulling it from the service manager, and possibly refactor the anonymous function into a dedicated factory class.