【问题标题】:ZF2 injecting InputFilter into Fieldset not working automaticallyZF2 将 InputFilter 注入 Fieldset 无法自动工作
【发布时间】:2016-11-19 23:34:37
【问题描述】:

我正在使用 ZF2 和 Doctrine2 构建一个小型应用程序。以具有大量可重用代码和技术的方式进行设置。然而,我的InputFilter 并没有自动注入到它应该关联的Fieldset 中,这让我感到困惑。

我已经确认使用Fieldset 的表单可以正常工作(没有InputFilter)。 InputFilter 在调试期间也可见。

然后的问题是,我做错了什么以及如何解决在 ZF2 中将 InputFilterFieldset 耦合的问题?


旁注:

1 - 我知道通过使用InputFilterInterface,我可以在Fieldset 类中使用InputFiltergetInputFilterSpecification() 函数。但是,由于我试图使其保持干燥和可重复使用,因此如果我要创建一个需要使用 EntityInputFilter 但只能使用后者的 API,则不必复制它加上Fieldset

2 - 使用了很多抽象类,我将在 sn-ps 中指出它们的相关内容

3 - 问题行在CustomerFieldsetFactory.php

================================================ ============================

实体:Customer.php

/**
 * Class Customer
 * @package Customer\Entity
 *
 * @ORM\Entity
 * @ORM\Table(name="customers")
 */
class Customer extends AbstractEntity //Contains $id
{
    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=255, nullable=false)
     */
    protected $name;
}

表格:CustomerForm.php

class CustomerForm extends AbstractForm
{
    public function __construct($name = null, array $options)
    {
        parent::__construct($name, $options); // Adds CSRF
    }

    public function init()
    {
        $this->add([
            'name' => 'customer',
            'type' => CustomerFieldset::class,
            'options' => [
                'use_as_base_fieldset' => true,
            ],
        ]);

        //Call parent initializer. Check in parent what it does.
        parent::init(); //Adds submit button if not in form
    }
}

字段集:CustomerFieldset.php

class CustomerFieldset extends AbstractFieldset //Contains EntityManager property and constructor requirement (as we're managing Doctrine Entities here)
{
    public function init()
    {
        $this->add([ //For now id field is here, until InputFilter injection works
            'name' => 'id',
            'type' => Hidden::class,
            'attributes' => [
                'id' => 'entityId',
            ],
        ]);

        $this->add([
            'name' => 'name',
            'type' => Text::class,
            'options' => [
                'label' => _('Name'),
            ],
        ]);
    }
}

输入过滤器:CustomerInputFilter.php

class CustomerInputFilter extends AbstractInputFilter
{
    public function init()
    {
        parent::init();

        $this->add([
            'name' => 'name',
            'required' => true,
            'filters' => [
                ['name' => StringTrim::class],
                ['name' => StripTags::class],
            ],
            'validators' => [
                [
                    'name' => StringLength::class,
                    'options' => [
                        'min' => 3,
                        'max' => 255,
                    ],
                ],
            ],
        ]);
    }
}

高于类。工厂下面

FormFactory:CustomerFormFactory.php

class CustomerFormFactory implements FactoryInterface, MutableCreationOptionsInterface
{
    /**
     * @var array
     */
    protected $options;

    /**
     * @param array $options
     */
    public function setCreationOptions(array $options)
    {
        //Arguments checking removed
        $this->options = $options;
    }

    /**
     * @param ServiceLocatorInterface|ControllerManager $serviceLocator
     * @return CustomerForm
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $serviceManager = $serviceLocator->getServiceLocator();

        $form = new CustomerForm($this->options['name'], $this->options['options']);

        $form->setTranslator($serviceManager->get('translator'));

        return $form;
    }
}

FieldsetFactory:CustomerFieldsetFactory.php

class CustomerFieldsetFactory implements FactoryInterface, MutableCreationOptionsInterface
{
    /**
     * @var string
     */
    protected $name;

    public function setCreationOptions(array $options)
    {
        //Argument checking removed

        $this->name = $options['name'];
    }

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

        $fieldset = new CustomerFieldset($serviceManager->get('Doctrine\ORM\EntityManager'), $this->name);

        $fieldset->setHydrator(new DoctrineObject($serviceManager->get('doctrine.entitymanager.orm_default'), false));
        $fieldset->setObject(new Customer());
        $fieldset->setInputFilter($serviceManager->get('InputFilterManager')->get(CustomerInputFilter::class));

        //RIGHT HERE! THE LINE ABOVE IS THE ONE THAT DOES NOT WORK!!!

        return $fieldset;
    }
}

输入过滤器工厂:CustomerInputFilterFactory.php

class CustomerInputFilterFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $repository = $serviceLocator->getServiceLocator()
            ->get('Doctrine\ORM\EntityManager')
                ->getRepository(Customer::class);

        return new CustomerInputFilter($repository);
    }
}

配置:module.config.php

'controllers' => [
    'factories' => [
        CustomerController::class => CustomerControllerFactory::class,
    ],
],
'form_elements' => [
    'factories' => [
        CustomerForm::class => CustomerFormFactory::class,
        CustomerFieldset::class => CustomerFieldsetFactory::class,
    ],
],
'input_filters' => [
    'factories' => [
        CustomerInputFilter::class => CustomerInputFilterFactory::class,
    ],
],
'service_manager' => [
    'invokables' => [
        CustomerControllerService::class => CustomerControllerService::class,
    ],
],

我希望你们中的一个可以在这里帮助我。


编辑:更新实际错误

CustomerFieldset.php(上面)中的以下行会触发错误。

$fieldset->setInputFilter($serviceManager->get('InputFilterManager')->get(CustomerInputFilter::class));

错误:

Fatal error: Call to undefined method Customer\Fieldset\CustomerFieldset::setInputFilter() in D:\htdocs\server-manager\module\Customer\src\Customer\Factory\CustomerFieldsetFactory.php on line 57

从上面的 sn-p 中可以看出,InputFilter(和它的工厂)被称为 InputFilterManager

错误表明它不知道 Fieldset 上的 getInputFilter() 函数。这在某种程度上是正确的,它不存在。那么问题是,如何让函数存在以便注入 InputFilter 起作用,或者如何将此 InputFilter 绑定到 Fieldset?


编辑 2:根据威尔特的回答更新use InputFilterAwareTrait 添加到抽象类AbstractInputFilter 以创建以下内容(来自答案):

use Zend\InputFilter\InputFilterAwareTrait;

abstract class AbstractFieldset extends Fieldset
{
    use InputFilterAwareTrait;
    // ... Other code
}

原来我在上面的(原始)代码中还有另一个错误: 在文件module.config.php 中,input_filters 应该是input_filter_specs。这是(使用 Trait 之后)Invalid Factory registered 错误(标题为 ServiceNotCreatedException)。

以下内容可能对某人有用,使用 Hydrator、Object 和 Inputfilter 创建 Fieldset 的工厂具有以下 createService() 函数:

public function createService(ServiceLocatorInterface $serviceLocator)
{
    /** @var ServiceLocator $serviceManager */
    $serviceManager = $serviceLocator->getServiceLocator();
    /** @var CustomerRepository $customerRepository */
    $customerRepository = $serviceManager->get('Doctrine\ORM\EntityManager')->getRepository(Customer::class);

    $fieldset = new CustomerFieldset($serviceManager->get('Doctrine\ORM\EntityManager'), $this->name);
    $fieldset->setHydrator(new DoctrineObject($serviceManager->get('doctrine.entitymanager.orm_default'), false));
    $fieldset->setObject(new Customer());
    $fieldset->setInputFilter($serviceManager->get('InputFilterManager')->get(CustomerInputFilter::class, $customerRepository));

    return $fieldset;
}

【问题讨论】:

  • 你搞定了吗?
  • 您提供了很多信息,但没有真正解释实际的错误。输入过滤器管理器是否找到CustomerInputFilter?你执行CustomerInputFilterFactory中的任何代码吗?
  • @PurpleHexagon 还没有。当前在字段集上使用InputFilterProviderInterface 解决它,并在getInputFilterSpecification 函数中具有规范。
  • @AlexP 我已经更新了问题(在底部;))以清楚地说明问题发生在哪里,我已经调试了变量并理解了错误。只是不知道如何修复错误;) InputFilter 和 InputFilterFactory 都在管理器中注册。但是在 InputFilterFactory 中没有执行任何代码。 ->get('CustomerInputFilter') 实际返回:An exception was raised while creating "Customer\Fieldset\CustomerFieldset"; no instance returned

标签: php dependency-injection doctrine-orm zend-framework2


【解决方案1】:

您的问题中添加了很多信息。我建议您将来尝试缩小您的问题范围。在此处阅读有关好问题指南的更多信息:How to create a Minimal, Complete, and Verifiable example

Zend 框架提供InputFilterAwareTraitsetInputFiltergetInputFilter 方法。您可以在 CustomerFieldset 类中轻松实现/使用此 trait:

use Zend\InputFilter\InputFilterAwareTrait;

class CustomerFieldset extends AbstractFieldset
{
    use InputFilterAwareTrait;

    //...remaining code

}

如果您希望在扩展您的抽象 AbstractFieldset 类的所有类中使用输入过滤器,您也可以决定在那里添加特征:

use Zend\InputFilter\InputFilterAwareTrait;

class AbstractFieldset
{
    use InputFilterAwareTrait;

    //...remaining code

}

【讨论】:

  • 谢谢!那成功了!由于配置不正确,出现了一些后续错误。编辑问题以显示这些(因为它们来自我提供的所有代码)。还为后面的人提供了完整的createService() 功能。不知道InputFilterAwareTrait,谢谢!
【解决方案2】:

请参阅以下问题:How to validate nested fieldsets。字段集不包含 InputFilters,但您应该扩展表单的基本 InputFilter。

为每个 Fieldset 创建一个 InputFilter,并将它们与您的 fieldset 同名添加到表单的 InputFilter。从我链接的另一个问题的答案中可以看出。

如果您不想这样做,可以考虑使用 InputSpecification。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-28
    • 2015-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多