在symfony中,我可以通过call
选项(https://symfony.com/doc/current/service_container/calls.html)将setter注入用于服务
symfony文档中的示例:
class MessageGenerator
{
private $logger;
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
// ...
}
service.yml
services:
App\Service\MessageGenerator:
# ...
calls:
- method: setLogger
arguments:
- '@logger'
我的zend项目需要这种行为。我想将InputFilter
注入我的FormFieldSet
中。
我在zend文档中没有找到任何关于此的内容。我可以使用类似的方法还是在zend中解决我的问题的更好解决方案?
答案 0 :(得分:2)
基于这个问题以及您之前关于Forms,Fieldsets和InputFilters的问题,我认为您想实现与以下用例相似的功能。
您有
要管理位置,您需要:
要在ZF3中进行配置,您必须添加以下内容
'form_elements' => [
'factories' => [
AddressFieldset::class => AddressFieldsetFactory::class,
LocationForm::class => LocationFormFactory::class,
LocationFieldset::class => LocationFieldsetFactory::class,
],
],
'input_filters' => [
'factories' => [
AddressFieldsetInputFilter::class => AddressFieldsetInputFilterFactory::class,
LocationFormInputFilter::class => LocationFormInputFilterFactory::class,
LocationFieldsetInputFilter::class => LocationFieldsetInputFilterFactory::class,
],
],
在LocationForm
中,添加您的LocationFieldset
以及表单需要的其他内容,例如CSRF和提交按钮。
class LocationForm extends AbstractForm
{
public function init()
{
$this->add([
'name' => 'location',
'type' => LocationFieldset::class,
'options' => [
'use_as_base_fieldset' => true,
],
]);
//Call parent initializer. Adds CSRF & submit button
parent::init();
}
}
(注意:我的AbstractForm
的作用还多一些,我建议您看看here,例如删除空的(子字段集/集合)输入,这样就不会尝试在其中创建数据数据库)
在LocationFieldset
中,为位置添加add输入,例如名称和AddressFieldset
:
class LocationFieldset extends AbstractFieldset
{
public function init()
{
parent::init();
$this->add([
'name' => 'name',
'required' => true,
'type' => Text::class,
'options' => [
'label' => _('Name'),
],
]);
$this->add([
'type' => AddressFieldset::class,
'name' => 'address',
'required' => true,
'options' => [
'use_as_base_fieldset' => false,
'label' => _('Address'),
],
]);
}
}
在AddressFieldset
中,仅添加地址实体的输入。 (与上面相同,没有字段集类型输入)
要验证表单,您可以使其非常简单:
class LocationFormInputFilter extends AbstractFormInputFilter
{
/** @var LocationFieldsetInputFilter */
protected $locationFieldsetInputFilter;
public function __construct(LocationFieldsetInputFilter $filter)
{
$this->locationFieldsetInputFilter = $filter;
parent::__construct();
}
public function init()
{
$this->add($this->locationFieldsetInputFilter, 'location');
parent::init();
}
}
(AbstractFormInputFilter
添加了CSRF验证器)
请注意,我们只是->add()
LocationFieldsetInputFilter,但是给它起了一个名字(第二个参数)。此名称将在以后的完整结构中使用,因此保持简单和正确无误非常重要。最简单的方法是给它一个与应该验证的Fieldset对象一对一匹配的名称。
接下来,LocationFieldsetInputFilter
:
class LocationFieldsetInputFilter extends AbstractFieldsetInputFilter
{
/**
* @var AddressFieldsetInputFilter
*/
protected $addressFieldsetInputFilter;
public function __construct(AddressFieldsetInputFilter $addressFieldsetInputFilter)
{
$this->addressFieldsetInputFilter = $addressFieldsetInputFilter;
parent::__construct();
}
public function init()
{
parent::init();
$this->add($this->addressFieldsetInputFilter, 'address'); // Again, name is important
$this->add(
[
'name' => 'name',
'required' => true,
'filters' => [
['name' => StringTrim::class],
['name' => StripTags::class],
[
'name' => ToNull::class,
'options' => [
'type' => ToNull::TYPE_STRING,
],
],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'min' => 3,
'max' => 255,
],
],
],
]
);
}
}
现在,您必须将它们绑定在一起,这就是我认为关于Setter注入的问题所在。这发生在工厂中。
*FormFactory
将执行以下操作:
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$inputFilterPluginManager = $container->get('InputFilterManager');
$inputFilter = $inputFilterPluginManager->get(LocationFormInputFilter::class);
/** @var LocationForm $form */
$form = new LocationForm();
$form->setInputFilter($inputFilter); // The setter injection you're after
return $form;
}
*FieldsetFactory
将执行以下操作(对Location-和AddressFieldsets执行相同的操作):
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
/** @var LocationFieldset $fieldset */
// name matters! Match the object to keep it simple. Name is used from Form to match the InputFilter (with same name!)
$fieldset = new LocationFieldset('location');
// Zend Reflection Hydrator, could easily be something else, such as DoctrineObject hydrator.
$fieldset->setHydrator(new Reflection());
$fieldset->setObject(new Location());
return $fieldset;
}
*FormInputFilterFactory
将执行以下操作:
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$inputFilterPluginManager = $container->get('InputFilterManager');
/** @var LocationFieldsetInputFilter $locationFieldsetInputFilter */
$locationFieldsetInputFilter = $inputFilterPluginManager->get(LocationFieldsetInputFilter::class);
// Create Form InputFilter
$locationFormInputFilter = new LocationFormInputFilter(
$locationFieldsetInputFilter
);
return $locationFormInputFilter;
}
*FieldsetInputFilterFactory
将执行以下操作:
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
/** @var AddressFieldsetInputFilter $addressFieldsetInputFilter */
$addressFieldsetInputFilter = $this->getInputFilterManager()->get(AddressFieldsetInputFilter::class);
$addressFieldsetInputFilter->setRequired(true);
return new LocationFieldsetInputFilter(
$addressFieldsetInputFilter
);
}
注意:
我想我已经完整地介绍了所有内容。如果对此有任何疑问,请发表评论。
答案 1 :(得分:1)
您需要的是来自Zend Service Manager的初始化器。
初始化程序可以是在创建服务时调用的类。 在该类中,您需要检查所创建的服务的类型,以及是否合适的类型而不是注入任何所需的服务。
要注册一个初始化器,请在 service_manager 键下的config中添加:
'service_manager' => [
'initializers' => [
MyInitializer::class
],
]
然后创建该类
class MyInitializer implements InitializerInterface
{
public function __invoke(ContainerInterface $container, $instance)
{
// you need to check should you inject or not
if ($instance instanceof MessageGenerator) {
$instance->setLogger($container->get('logger'));
}
}
}
您还需要在zend-servicemanager中注册 MessageGenerator 。这样,当您尝试从SM检索 MessageGenerator 时,将在创建后调用MyInitializer。