Zend ServiceManager, использующий инъекцию сеттера - PullRequest
0 голосов
/ 14 ноября 2018

в 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?

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Что вам нужно: Инициализаторы из Zend Service Manager .

Инициализатором может быть класс, который вызывается при создании службы.В этом классе вам нужно проверить тип создаваемой службы и, если это соответствующий тип, внедрить то, что вы хотите.

Чтобы зарегистрировать один инициализатор, добавьте в конфигурацию под service_manager ключ:

'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'));
        }
    }
}

. Вам нужнозарегистрировали MessageGenerator также в Zend-ServiceManager.Таким образом, когда вы пытаетесь извлечь MessageGenerator из SM, после создания вызывается MyInitializer.

0 голосов
/ 15 ноября 2018

Исходя из этого вопроса и вашего предыдущего вопроса о Forms, Fieldsets и InputFilters, я думаю, вы хотите добиться чего-то похожего на следующий вариант использования.

Вариант использования

У вас естьa

  • Местоположение объекта
  • Адрес объекта
  • Местоположение имеет OneToOne к адресу (обязательно, однонаправлено)

Требования

Для управления местоположением вам потребуется:

  • LocationForm (-Factory)
  • LocationFormInputFilter (-Factory)
  • LocationFieldset (-Factory)
  • LocationFieldsetInputFilter (-Factory)
  • AddressFieldset (-Factory)
  • AddressFieldsetInputFilter (-Factory)

Конфигурация

Чтобы настроить это в 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,
    ],
],

Forms & Fieldsets

В 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 делает немного больше, я бы посоветовал вам посмотреть здесь , например, удалить пустые (дочерние поля / наборы) Входы, чтобы данные былине пытались создать в БД)

В 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)

InputFilters

Чтобы проверить форму, вы можете сделать ее очень простой:

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, но мы даем ему имя (2-й параметр).Это имя используется позже в полной структуре, поэтому важно как сохранить его простым, так и правильным.Проще всего дать ему имя, которое один на один соответствует объекту набора полей, который он должен проверять.

Далее 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,
                        ],
                    ],
                ],
            ]
        );
    }
}

Фабрики

Теперь вы должны связать их вместе, откуда, как мне кажется, возникает ваш вопрос об инъекции сеттера.Это происходит на фабрике.

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 как (не обязательного) - это то, что я добавил здесь
  • Если ваш InputFilter (такой как AddressFieldsetInputFilter) не имеет дочернего InputFilter, вы можетепропустите получение ребенка и сразу же верните новый InputFilter.

Я думаю, что я все это покрыл для полной картины.Если у вас есть какие-либо вопросы по этому поводу, пожалуйста, прокомментируйте.

...