Форма с более чем одной коллекцией

Я хочу реализовать форму, которая довольно проста. Единственное, что усложняет ситуацию, — это то, что я использую две коллекции в своей форме. Отображение двух коллекций в представлении работает как шарм. Проблема заключается в валидации и связанной с этим гидратации связанной сущности формы. Если все проверено и ошибок не возникает, экземпляр формы пытается гидрировать связанный объект и в результате выдает исключение:

Zend \ Hydrator \ ArraySerializable :: hydrate ожидает, что предоставленный объект реализует exchangeArray () или populate ()

Но сначала пример кода …

Форма занятий

namespace Application\Form;

use Zend\Form\Element\Collection;
use Zend\Form\Element\Text;
use Zend\Form\Form;

class MyForm extends Form
{
public function __construct($name = '', $options = [])
{
parent::__construct($name, $options);

$this->setAttribute('method', 'post');
$this->setAttribute('id', 'my-form');
}

public function init()
{
$this->add([
'name' => 'my-text-field',
'type' => Text::class,
'attributes' => [
...
],
'options' => [
...
],
]);

// The first collection
$this->add([
'name' => 'first-collection',
'type' => Collection::class,
'options' => [
'count' => 2,
'should_create_template' => true,
'template_placeholder' => '__index__',
'allow_add' => true,
'allow_remove' => true,
'target_element' => [
'type' => FieldsetOne::class,
],
],
]);

// the second collection
$this->add([
'name' => 'second-collection',
'type' => Collection::class,
'options' => [
'count' => 2,
'should_create_template' => true,
'template_placeholder' => '__index__',
'allow_add' => true,
'allow_remove' => true,
'target_element' => [
'type' => FieldsetTwo::class,
],
],
]);
}
}

Указанные классы Fieldset, связанные с коллекциями, выглядят практически одинаково.

namespace Application\Form;

use Zend\Form\Element\Number;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;

class FieldsetOne extends Fieldset implements InputFilterProviderInterface
{
public function init()
{
$this->add([
'name' => 'my-number',
'type' => Number::class,
'options' => [
...
],
'attributes' => [
...
],
]);
}

public function getInputFilterSpecification()
{
return [
'my-number' => [
'required' => true,
'filters' => [
[
'name' => StripTags::class,
],
[
'name' => ToInt::class,
],
],
'validators' => [
[
'name' => NotEmpty::class,
],
[
'name' => IsInt::class,
'options' => [
'locale' => 'de_DE',
],
],
],
],
];
}
}

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

Класс входного фильтра

Форма фильтруется и проверяется с помощью следующего входного фильтра. Входной фильтр будет привязан к форме через фабрику. Фабрика будет показана позже.

class MyFormInputFilter extends InputFilter
{
public function init()
{
$this->add([
'name' => 'my-text-field',
'required' => true,
'filters' => [
[
'name' => StripTags::class,
],
[
'name' => StringTrim::class,
],
],
]);
}
}

Входной фильтр содержит только настройки для my-text-field элемент. Коллекции будут проверены с InputFilterProviderInterface в наборах полей, установленных в качестве целевых элементов. Класс входного фильтра создается на фабрике и записывается в input_filters раздел в module.config.php,

Форма объекта

Сущность будет привязана как объект к форме на фабрике, как это выглядит в следующем примере.

namespace Application\Entity;

class MyFormEntity
{
protected $myTextField;

protected $firstCollection;

protected $secondCollection;

public function getMyTextField()
{
return $this->myTextField;
}

public function setMyTextField($myTextField)
{
$this->myTextField = $myTextField;
return $this;
}

public function getFirstCollection()
{
return $this->firstCollection;
}

public function setFirstCollection(array $firstCollection)
{
$this->firstCollection = $firstCollection;
return $this;
}

public function getSecondCollection()
{
return $this->secondCollection;
}

public function setSecondCollection(array $secondCollection)
{
$this->secondCollection = $secondCollection;
return $this;
}
}

Эта сущность будет привязана как объект к форме. Форма будет гидратирована быть собственной Zend ClassMethods класс гидратор. Для коллекций две гидраторные стратегии добавляются в гидратор. Стратегии гидратора для коллекций выглядят следующим образом.

namespace Application\Hydrator\Strategy;

class FirstCollectionStrategy extends DefaultStrategy
{
public function hydrate($value)
{
$aEntities = [];
if (is_array($value)) {
foreach ($value as $key => $data) {
$aEntities[] = (new ClassMethods(false))->hydrate($data, new CollectionOneEntity());
}
}

return $aEntities;
}
}

Эта стратегия будет гидрировать данные из коллекции один в соответствующий объект.

Все на заводе

Это фабрика, которая создает экземпляр формы.

class MyFormFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$parentLocator = $serviceLocator->getServiceLocator();

$filter = $parentLocator->get('InputFilterManager')->get(MyFormInputFilter::class);
$hydrator = (new ClassMethods())
->addStrategy('first-collection', new FirstCollectionStrategy())
->addStrategy('second-collection', new SecondCollectionStrategy());
$object = new MyFormEntity();

$form = (new MyForm())
->setInputFilter($filter)
->setHydrator($hydrator)
->setObject($object);

return $form;
}
}

Эта фабрика упоминается в form_elements раздел в module.config.php файл.

Эта проблема

Все отлично работает Элемент ввода, а также коллекции отображаются в представлении. Если форма отправлена ​​и $form->isValid() метод вызывается в контроллере все заканчивается в BadMethodCallException,

Zend \ Hydrator \ ArraySerializable :: hydrate ожидает, что предоставленный объект реализует exchangeArray () или populate ()

Я не связывал сущности коллекции с формой в контроллере, потому что стратегии гидратора добавляются в гидратор формы, который должен гидратировать сущность формы. Это имеет смысл для меня, потому что зенд форма может связывать только один объект. Если я позвоню bind Метод дважды в контроллере, первый связанный объект будет перезаписан.

Можно ли добавить более одного объекта с bind метод формы, так что две коллекции могут быть обработаны? Как могут выглядеть альтернативы? Что я делаю не так?

0

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector