Как использовать службы для набора полей формы в Zend Framework 2?

У меня есть форма (Zend\Form\Form) с некоторыми вложенными наборы полей (Zend\Form\Fieldset). Конструкция очень похожа на описанную в руководстве по коллекциям форм. .

Storage\Form\MyForm
|_'Storage\Form\Fieldset\FooFieldset'
  |_'Storage\Form\Fieldset\BarFieldset'
|_'Storage\Form\Fieldset\BazFieldset'
...

MyForm

class MyForm {
    public function __construct()
    {
        ...
        $this->add(
            [
                'type' => 'Storage\Form\Fieldset\FooFieldset',
                'options' => [
                    'use_as_base_fieldset' => true
                ]
            ]
        );
    }
}

FooFieldset

class FooFieldset extends Fieldset implements InputFilterProviderInterface
{
    public function __construct()
    {
        parent::__construct('foo');
        $this->setHydrator(new ClassMethodsHydrator())->setObject(new Foo()); // dependencies!
    }
}

Это работает, но в классе fieldset есть две зависимости. Я хочу ввести их. Для этого я создал FooFieldsetFactory и расширил /module/MyModule/config/module.config.php:

'service_manager' => [
    'factories' => [
        'Storage\Form\Fieldset\FooFieldset' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
    ],
],

Завод просто игнорируется. Я предполагаю, что сервис-локатор сначала пытается найти класс по пространству имен, и только если ничего не найдено, смотрит в invokables и factories. OK. Затем я создал псевдоним:

'service_manager' => [
    'factories' => [
        'Storage\Form\Fieldset\FooFieldset' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
    ],
],
'aliases' => [
    'Storage\Form\Fieldset\Foo' => 'Storage\Form\Fieldset\FooFieldset',
],

... и попытался использовать его вместо Storage\Form\Fieldset\FooFieldset в моем классе формы. Но теперь я получаю исключение:

Zend\Form\FormElementManager::get не удалось получить или создать экземпляр для Storage\Form\Fieldset\Foo

Я также пробовал это напрямую:

'service_manager' => [
    'factories' => [
        'Storage\Form\Fieldset\Foo' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
    ],
],

Никакого эффекта, та же ошибка.

И это тоже не сработало (та же ошибка):

'form_elements' => [
    'factories' => [
        'Storage\Form\Fieldset\Foo' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
    ],
],

Таким образом, ссылка на службу для набора полей, похоже, не работает. Или я что-то не так делаю?

Как использовать службы для наборов полей формы?


ОБНОВЛЕНИЕ

С некоторой отладкой я обнаружил, что мою Foo фабрику наборов полей невозможно найти, потому что она не добавлена ​​в список factories Zend\Form\FormElementManager. Вот место в Zend\Form\Factory:

введите здесь описание изображения

Итак, мой конфиг

'form_elements' => [
    'factories' => [
        'Storage\Form\Fieldset\Foo' => 'Storage\Form\Fieldset\Factory\FooFieldsetFactory',
    ],
],

игнорируется. Как это исправить?


ОБНОВЛЕНИЕ Дополнительная информация о том, как я создаю свой объект Form.

/module/Foo/config/module.config.php

return [
    'controllers' => [
        'factories' => [
            'Foo\Controller\My' => 'Foo\Controller\Factory\MyControllerFactory'
        ]
    ],
    'service_manager' => [
        'factories' => [
            'Foo\Form\MyForm' => 'Foo\Form\Factory\MyFormFactory',
        ],
    ],
];

/module/Foo/src/Foo/Form/Factory/MyFormFactory.php

namespace Foo\Form\Factory;

use ...;

class MyFormFactory implements FactoryInterface
{

    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $form = new MyForm();
        $form->setAttribute('method', 'post')
            ->setHydrator(new ClassMethods())
            ->setInputFilter(new InputFilter());
        return $form;
    }
}

/module/Foo/src/Foo/Controller/Factory/MyControllerFactory.php

namespace Foo\Controller\Factory;

use ...;

class MyControllerFactory implements FactoryInterface
{

    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $fooPrototype = new Foo();
        $realServiceLocator = $serviceLocator->getServiceLocator();
        // $myForm = $realServiceLocator->get('Foo\Form\MyForm'); <-- This doesn't work correctly for this case. The FormElementManager should be used instead.
        $formElementManager = $realServiceLocator->get('FormElementManager');                        
        $myForm = $formElementManager->get('Foo\Form\MyForm');
        return new MyController($myForm, $fooPrototype);
    }
}

person automatix    schedule 10.03.2016    source источник


Ответы (1)


Эта проблема связана с тем, что вы добавляете элементы формы в методе формы __construct(), а не init() как предлагается в документации.

Вы можете использовать фабрику вместо вызываемого, чтобы обрабатывать зависимости в ваших элементах/полях/формах.

И вот первый улов.

Если вы создаете свой класс формы, расширяя Zend\Form\Form, вы не должны добавлять пользовательский элемент в __construct-or (как мы сделали в предыдущем примере, где мы использовали FQCN пользовательского элемента), а скорее в метод init():

Причина заключается в том, что фабрика новой формы (которая используется для создания новых элементов с помощью add()) должна иметь менеджер элементов формы приложения вводится после вызова конструктора формы Этот экземпляр диспетчера элементов формы содержит все ссылки на ваш элементы пользовательских форм, зарегистрированные под ключом конфигурации form_elements.

При вызове add() в форме __construct фабрика форм будет лениво загружать новый экземпляр менеджера элементов формы; который сможет создавать все элементы формы по умолчанию, но не будет знать ваш пользовательский элемент формы.

person AlexP    schedule 10.03.2016
comment
Большое спасибо за ваш подробный ответ! Я перенес логику инициализации из конструктора в файл init(). Но это не работает. Для Form элементы просто не найдены, например: No element by the name of [foo] found in form. Для FieldSets: Та же ошибка, что и раньше: Zend\Form\FormElementManager::get was unable to fetch or create an instance for Order\Form\Fieldset\Foo. - person automatix; 11.03.2016
comment
Я также проанализировал состояние FormElementManager в init(). Поскольку init() вызывается после конструктора, я ожидал найти свои фабрики с пользовательскими наборами полей в MyFieldset->factory->formElementManager->factories. Но свойство содержит только стандартные фабрики Zend. - person automatix; 11.03.2016
comment
@automatix Вы создаете форму, используя $formElementManager->get('Order\Form\Fieldset\Foo);` с Order\Form\Fieldset\Foo, зарегистрированным в form_elements? Ваш вопрос показывает только Storage\Form\Fieldset\Foo. - person AlexP; 11.03.2016
comment
Только что обновил вопрос. Я создавал Form непосредственно из ServiceManager. Просто изменил это на создание через FormElementManager. Теперь я могу создать поля в Form#init(), и мой FooFieldsetFactory наконец-то извлекается! Большое спасибо за Вашу помощь! - person automatix; 12.03.2016
comment
@automatix, как вы решили проблему с элементом «Нет» по имени ..? Я делаю $form->get('employee'), что дает мне исключение. Мой EmployeeFieldSet делает в конструкторе: parent::__construct('employee'); В функции инициализации я делаю все эти add вызовы. В конструкторе формы я делаю так: parent::__construct('create_employee'); и в методе init() добавляю EmployeeFieldSet. в config.module.php у меня есть form_elements, но он не будет вызываться - person rogaa; 26.08.2016
comment
@rogaa Я ничего не делаю с конструктором, а использую только ключ конфигурации form_elements (например, 'form_elements'.'factories'.'My\Fieldset\Bar' => 'My\Fieldset\Factory\BarFactory') и добавляю Fieldset в init() (например, $this->add(['name' => 'bar', 'type' => 'My\Fieldset\Bar', ...])). В сценарии представлений я вызываю Fieldset следующим образом: $fooFieldset->get('bar'). О, теперь, когда я пишу этот комментарий, мне кажется, я вижу твою ошибку. Это параметр name — вы должны передать его своему родителю Fieldset. В противном случае get(...) из FooFieldset не знает, что искать. - person automatix; 26.08.2016
comment
@automatix ваш обновленный вопрос все еще верен с вашей FormFactory? Извините, что снова беспокою вас - я удалил свои методы построения () из моих EmployeeForm и моих EmployeeFieldset, но я все еще получаю тот же вывод об ошибке. Что вы имеете в виду под параметром name? В моем EmployeeForm->init() я делаю следующее: $this->add([ 'name' => 'employee', 'type' => EmployeeFieldset::class, 'options' => [ 'use_as_base_fieldset' => true, ], ]); - person rogaa; 29.08.2016
comment
Хорошо, я понял .. боже мой .. Я добавил DI в свой IndexController, чтобы получить форму через FormElementManagerIndexControllerFactory). Но я забыл изменить вызов формы в своем действии - я все же создал объект формы через new EmployeeForm(); вместо вызова this->form из-за DI ... В любом случае, спасибо @automatix! - person rogaa; 29.08.2016
comment
@rogaa Я не вижу никакой разницы между твоим add(...) звонком и моим. Но я только что обновил свой вопрос и исправил способ получения объекта MyForm из файла ServiceManager. О, ты поторопился со своим комментарием... :) Хорошо, теперь работает? - person automatix; 29.08.2016