3

对于我的 ZF2 表单验证,我需要自定义验证器中的实体管理器。我 Zend\ServiceManager\ServiceLocatorAwareInterface 在我的验证器中获取服务定位器,这样我就可以从那里获取实体管理器。

我的问题是这个 Zend\ServiceManager\ServiceLocatorAwareInterface::setServiceLocator 注入了“Zend\Validator\ValidatorPluginManager”而不是所需的 ServiceLocator。

有谁知道如何解决这个问题?

我的抽象控制器

abstract class AbstractController extends AbstractActionController
{    
    /**
     * This property contains the doctrine entitymanager.
     * @var Doctrine\ORM\EntityManager
     */
    protected $_entityManager;

    /**
     * This method returns the doctrine entitymanager.
     * @return \Doctrine\ORM\EntityManager
     */
    public function getEntityManager()
    {
        var_dump(spl_object_hash($this->getServiceLocator())); echo '<br>';
        if (null === $this->_entityManager)
            $this->_entityManager = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
        return $this->_entityManager;
    }
}

我的验证器:

class EntityUnique extends AbstractValidator implements ServiceLocatorAwareInterface
{
    protected $_serviceLocator;

    public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
    {
        var_dump(spl_object_hash($serviceLocator)); echo '<br>';
        $this->_serviceLocator = $serviceLocator;
    }

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

执行此操作时,两个 var_dumps 都会产生另一个对象哈希:

string(32) "000000005e4258390000000024a7f829"
string(32) "000000005e425a2d0000000024a7f829" 
4

2 回答 2

3

这样做的原因是在您的验证器中,您正在被Zend\Validator\ValidatorPluginManager注入。如果您希望getServiceLocatorZend\Validator\ValidatorPluginManager.

例如看someMethod()下面的方法:

class EntityUnique extends AbstractValidator implements ServiceLocatorAwareInterface
{
    protected $_serviceLocator;

    public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
    {
        var_dump(spl_object_hash($serviceLocator)); echo '<br>';
        $this->_serviceLocator = $serviceLocator;
    }

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

    public function someMethod() 
    {
        $validatorPluginManager = $this->getServiceLocator();
        $serviceLocator = $validatorPluginManager->getServiceLocator(); // HERE

    }
}

希望这可以帮助 :)

斯托扬

于 2013-04-11T15:00:49.910 回答
0

我最终让它工作了。我不知道这是否是最佳做法,所以如果您有任何反馈,请不要犹豫。我的验证器最终是这样的:

<?php
namespace Flex\Validator;

use Doctrine\ORM\EntityManager;
use Zend\Stdlib\ArrayUtils;
use Zend\ServiceManager\ServiceManager;
use Zend\Validator\AbstractValidator;

class EntityUnique extends AbstractValidator
{
    const EXISTS                = 'exists';

    protected $messageTemplates = array(
        self::EXISTS    => 'A %entity% entity allready exists.',
    );
    protected $messageVariables = array(
        'entity'        => '_entity',
    );

    protected $_context;
    protected $_entity;
    protected $_excludes        = array();
    protected $_filters         = array();
    protected $_property;
    protected $_serviceLocator;

    public function __construct($options = null)
    {
        if ($options instanceof \Traversable)
            $options            = ArrayUtils::iteratorToArray($options);

        if (is_array($options))
        {
            if (isset($options['entity']))
                $this->_entity        = $options['entity'];
            if (isset($options['exclude']))
            {
                if (!is_array($options['exclude']))
                    throw new \Exception('Exclude option should be an array');
                if (isset($options['exclude']['property']) && isset($options['exclude']['value']))
                    $this->_excludes[] = array(
                        'property'    => $options['exclude']['property'],
                        'value'       => $options['exclude']['value'],
                    );
                else
                    foreach ($options['exclude'] as $exclude)
                    {
                        if (!isset($exclude['property']) || !isset($exclude['value']))
                            throw new \Exception('exclude shoud contain a property and value');
                            $this->_excludes[] = array(
                                    'property'    => $exclude['property'],
                                    'value'       => $exclude['value'],
                            );
                    }

            }
            if (isset($options['filter']))
            {
                if (!is_array($options['filter']))
                    throw new \Exception('Filter option should be an array');
                if (isset($options['filter']['property']) && isset($options['filter']['value']))
                    $this->_filters[] = array(
                        'property'    => $options['filter']['property'],
                        'value'       => $options['filter']['value'],
                    );
                else
                    foreach ($options['filter'] as $filter)
                    {
                        if (!isset($filter['property']) || !isset($filter['value']))
                            throw new \Exception('Filters shoud contain a property and value');
                            $this->_filters[] = array(
                                    'property'    => $filter['property'],
                                    'value'       => $filter['value'],
                            );
                    }

            }
            if (isset($options['property']))
                $this->_property          = $options['property']; 
            if (!isset($options['serviceLocator']))
                throw new \InvalidArgumentException(__CLASS__ . ' requires the option serviceLocator.');
            $ServiceManagerInstance   = 'Zend\ServiceManager\ServiceManager';
            if (!($options['serviceLocator'] instanceof $ServiceManagerInstance))
                throw new \InvalidArgumentException(__CLASS__ . ' expects the option serviceLocator to be an instance of Zend\ServiceManager\ServiceManager.');

            $this->_serviceLocator    = $options['serviceLocator']; 
        }
        parent::__construct(is_array($options) ? $options : null);
    }
    public function isValid($value, $context = null)
    {
        $this->setValue($value);
        $this->_context  = $context;

        $entityManager   = $this->_serviceLocator->get('doctrine.entitymanager.orm_default');
        $queryBuilder    = $entityManager->createQueryBuilder();
        $queryBuilder->from($this->_entity, 'e')
            ->select('COUNT(e)')
            ->where('e.' . $this->_property . ' = :value')
            ->setParameter('value', $this->getValue());

        if (count($this->_excludes))
            foreach ($this->_excludes as $key => $exclude)
            {
                $exclude['value']        = $this->parse($exclude['value']);
                if ($exclude['value'] !== null)
                    $queryBuilder->andWhere('e.' . $exclude['property'] . ' = :exclude_' . $key)
                        ->setParameter('exclude_' . $key, $exclude['value']);
                else
                    $queryBuilder->andWhere('e.' . $exclude['property'] . ' IS NULL');
            }

        if (count($this->_filters))
            foreach ($this->_filters as $key => $filter)
            {
                $filter['value']        = $this->parse($filter['value']);
                if ($filter['value'] !== null)
                    $queryBuilder->andWhere('e.' . $filter['property'] . ' = :filter_' . $key)
                        ->setParameter('filter_' . $key, $filter['value']);
                else
                    $queryBuilder->andWhere('e.' . $filter['property'] . ' IS NULL');
            }

        $query = $queryBuilder->getQuery();
        if ((integer) $query->getSingleScalarResult() !== 0)
        {
            $this->error(self::EXISTS);
            return false;
        }
        return true;
    }
    private function parse($input, $current = null)
    {
        if (!is_array($input))
            return $input;
        if ($current === null)
            $current     = $this;

        $currentKey      = array_shift($input);
        if (is_object($current) && property_exists($current, $currentKey))
            $current     = $current->$currentKey;
        elseif (is_array($current) && array_key_exists($currentKey, $current))
            $current     = $current[$currentKey];
        else
            return null;

        if (count($input))
            return $this->parse($input, $current);

        if (strlen($current) == 0)
            return null;

        return $current;
    }
}

这是这样加载的:

<?php
namespace FlexCategories\Form;

use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;

class CategoryForm extends Form implements InputFilterProviderInterface, ServiceManagerAwareInterface
{
    private $_serviceManager;

    public function init()
    {
        /* */
    }
    public function getInputFilterSpecification()
    {
        return array(
            'name'         => array(
                'filters'     => array(
                    array(
                        'name'    => 'Zend\Filter\StringTrim'
                    ),
                ),
                'required'    => true,
                'validators'  => array(
                    array(
                        'name'    => 'Flex\Validator\EntityUnique',
                        'options' => array(
                            'entity'    => 'FlexCategories\Entity\Category',
                            'filter'   => array(
                                array('property'     => 'parent',
                                      'value'        => array('_context', 'parent')),
                            ),
                            'property'  => 'name',
                            'serviceLocator'    => $this->_serviceManager,
                        ),
                    ),
                ),
            ),
        );
    }

    public function setServiceManager(ServiceManager $serviceManager)
    {
        $this->_serviceManager = $serviceManager;
        $this->init();

        return $this;
    }
}

该表单在 service_manager => invokables 中注册,并通过服务管理器在控制器中检索。

这对我有用。我希望有人能够从中学习或给我反馈以创建更好的解决方案。

于 2013-04-16T21:55:32.373 回答