Zend Framework 2过滤器中的依赖注入

时间:2014-10-10 10:14:13

标签: php dependency-injection zend-framework2

我现在正试图在ZF2中实现我自己的过滤器。

但是,我遇到了一个问题,我找不到足够清晰的文档。

我的过滤器需要选项数组(在我的情况下,它包含widthheight字段),以及服务定位器实例

但是,我无法让它接收WebinoImageThumb类实例。

以下是过滤器代码(此处,$this->serviceLocator始终为NULL,这是问题):

<?php

namespace Application\Form\Filter;

use Zend\Filter\Exception;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class FitImage extends \Zend\Filter\AbstractFilter implements ServiceLocatorAwareInterface
{
    protected $serviceLocator;

    private $width;
    private $height;
    private $thumbnailer;

    public function __construct($generalServiceLocator/*$options*/)
    {
        var_dump($generalServiceLocator);

        $width = $options['width'];
        $height = $options['height'];

        $this->width = $width;
        $this->height = $height;

        // We encourage to use Dependency Injection instead of Service Locator
        $this->thumbnailer = $this->getServiceLocator()->get('WebinoImageThumb');
    }

    public function filter($value)
    {
        $thumb = $thumbnailer->create($value, $options = array(), $plugins = array());

        $thumb->resize($this->width, $this->height);

        $thumb->save($value);

        return $value;
    }

    public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
    {
        $this->serviceLocator = $serviceLocator;
    }


    public function getServiceLocator()
    {
        return $this->serviceLocator;
    }
}

我以这样的形式使用它:

<?php
namespace Backend\Form;

use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\InputFilter\InputFilterInterface;
use Zend\Form\Form;

class AuthorForm extends Form implements InputFilterProviderInterface
{
    public function __construct($name = null)
    {
        // we want to ignore the name passed
        parent::__construct('author');

        $this->add(array(
            'name' => 'portrait_file',
            'type' => 'file',
            'options' => array(
                'label' => 'Портрет',
            )
        ));

        $this->add(array(
            'name' => 'submit',
            'type' => 'Submit',
            'attributes' => array(
                'value' => 'Go',
                'id' => 'submitbutton',
            ),
        ));
    }

    public function getInputFilterSpecification()
    {
        return array(
            array(
                'name' => 'portrait_file',
                'required' => false,
                'filters' => array(
                    array(
                        'name' => 'Application\Form\Filter\FitImage',
                        'options' => array(
                            'width' => 300,
                            'height' => 250
                        )
                    )
                ),
                // validators go here
            )
        );
    }
}

似乎通过实现ServiceLocatorAwareInterface,FitImage过滤器应该通过setServiceLocator接收serviceLocator,但它不会。

我错过了什么?

任何帮助表示感谢。

2 个答案:

答案 0 :(得分:1)

注入服务定位器是一种糟糕的设计策略;更好的方法是注入课堂所需的内容,即WebinoImageThumb'服务'。

首先删除ServiceLocator引用,然后将新服务添加为构造函数参数。

namespace MyModule\Filter;

class FitImage extends AbstractFilter
{    
    protected $thumbnailService;

    protected $height;

    protected $width;

    // Type hinting on 'ThumbnailServiceInterface' would mean you can swap
    // out the 'service' with another at a later date
    public function __construct(ThumbnailServiceInterface $thumbnailService, $height = 100, $width = 100)
    {
        $this->thumbnailService = $thumbnailService;
        $this->height = $height;
        $this->width = $width;
    }

    public function filter($value)
    {
        $thumb = $this->thumbnailService->create($value);
        $thumb->resize($this->height, $this->width);
        $thumb->save($value);

        return $value;
    }

    public function setHeight($height) {
       $this->height = intval($height);
    }

    public function setWidth($width) {
        $this->width = intval($width);
    }

}

然后创建一个服务工厂来创建过滤器。

namespace MyModule\Filter;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class FitImageFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        // $serviceLocator here is the 'filter plugin manager'
        $serviceManager = $serviceLocator->getServiceLocator();
        $options = $this->getOptions($serviceLocator);

        return new FitImage(
            $serviceManager->get('WebinoImageThumb'),
            $options['height'],
            $options['width']
        );
    }

    protected function getOptions(ServiceLocatorInterface $serviceLocator)
    {
        // This could be loaded from config
        return array('height' => 100, 'width' => 100);
    }
}

最后,添加对模块配置的引用或Module.php,如下所示

public function getFilterConfig()
{
    return array(
        'factories' => array(
            'MyModule\Filter\FitImage' => 'MyModule\Filter\FitImageFactory',
        ),
    );
}

在不知道具体项目的情况下,您可能需要考虑将缩略图创建从“过滤器”移到单独的服务中。过滤器旨在过滤/修改给定的输入$value并返回格式化的值。

答案 1 :(得分:0)

您的案例中的输入过滤器不会由服务经理实例化,因为您没有在那里拥有服务定位器的实例。要解决此问题,您可以手动创建过滤器对象,以便访问服务管理器。然后,您可以将其附加到表单中输入过滤器的过滤器链。

更新

我找到了解决问题的方法。您可以保持代码不变。如果您从控制器创建表单,则以下代码段将适合您(已测试)

    $form = new AuthForm();
    $filterChain = $form->getInputFilter()->get('portrait_file')->getFilterChain();
    $filterChain->getPluginManager()->setServiceLocator($this->getServiceLocator());

事实证明,过滤器链使用插件管理器管理其过滤器。通过defualt,插件管理器不会保留任何服务定位器,因此您可以像我在上面的示例中那样设置一个。在您的过滤器代码中,您可以访问主服务定位器,如下所示:

$mainServiceLocator = $this->getServiceLocator()->getServiceLocator();