Zend ServiceManager 使用 setter 注入
Zend ServiceManager using setter injection
在 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 中的问题吗?
根据这个问题和你之前关于表单、字段集和输入过滤器的问题,我认为你想要实现类似于以下用例的东西。
用例
你有一个
- 位置实体
- 地址实体
- Location 具有 OneToOne 到 Address(必需,单向)
要求
要管理位置,您需要:
- LocationForm(-工厂)
- LocationFormInputFilter(-工厂)
- LocationFieldset(-工厂)
- LocationFieldsetInputFilter(-工厂)
- 地址字段集(-工厂)
- AddressFieldsetInputFilter(-工厂)
配置
要在 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,比如remove empty (child fieldsets/collections) Inputs so data is not attempted将在数据库中创建)
在 LocationFieldset
中,为位置添加输入,例如名称,以及 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
中只需添加地址实体的输入。 (同上,不带Fieldset类型Input)
输入过滤器
要验证表单,您可以使其非常简单:
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 注入的问题的来源。这发生在工厂里。
A *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;
}
A *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;
}
A *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;
}
A *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
);
}
注:
- 将 InputFilter 设置为(不是)必需的是我添加的东西 here
- 如果您的 InputFilter(例如 AddressFieldsetInputFilter)没有子 InputFilter,您可以跳过获取子 InputFilter 并直接 return 新的 InputFilter。
我想我已经涵盖了所有内容以获得完整的图片。如果您对此有任何疑问,请发表评论。
您需要的是 Zend Service Manager 中的 Initializers。
初始化程序可以是 class,只要创建服务就会调用它。
在那个 class 中,您需要检查创建的服务类型,如果它是合适的类型,那么就注入您想要的任何内容。
要在 service_manager 键下的配置中注册一个初始化程序:
'service_manager' => [
'initializers' => [
MyInitializer::class
],
]
然后创建 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。
在 symfony 中,我可以通过 call
选项 (https://symfony.com/doc/current/service_container/calls.html)
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 中的问题吗?
根据这个问题和你之前关于表单、字段集和输入过滤器的问题,我认为你想要实现类似于以下用例的东西。
用例
你有一个
- 位置实体
- 地址实体
- Location 具有 OneToOne 到 Address(必需,单向)
要求
要管理位置,您需要:
- LocationForm(-工厂)
- LocationFormInputFilter(-工厂)
- LocationFieldset(-工厂)
- LocationFieldsetInputFilter(-工厂)
- 地址字段集(-工厂)
- AddressFieldsetInputFilter(-工厂)
配置
要在 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,比如remove empty (child fieldsets/collections) Inputs so data is not attempted将在数据库中创建)
在 LocationFieldset
中,为位置添加输入,例如名称,以及 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
中只需添加地址实体的输入。 (同上,不带Fieldset类型Input)
输入过滤器
要验证表单,您可以使其非常简单:
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 注入的问题的来源。这发生在工厂里。
A *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;
}
A *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;
}
A *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;
}
A *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
);
}
注:
- 将 InputFilter 设置为(不是)必需的是我添加的东西 here
- 如果您的 InputFilter(例如 AddressFieldsetInputFilter)没有子 InputFilter,您可以跳过获取子 InputFilter 并直接 return 新的 InputFilter。
我想我已经涵盖了所有内容以获得完整的图片。如果您对此有任何疑问,请发表评论。
您需要的是 Zend Service Manager 中的 Initializers。
初始化程序可以是 class,只要创建服务就会调用它。 在那个 class 中,您需要检查创建的服务类型,如果它是合适的类型,那么就注入您想要的任何内容。
要在 service_manager 键下的配置中注册一个初始化程序:
'service_manager' => [
'initializers' => [
MyInitializer::class
],
]
然后创建 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。