Исходя из этого вопроса и вашего предыдущего вопроса о 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.
Я думаю, что я все это покрыл для полной картины.Если у вас есть какие-либо вопросы по этому поводу, пожалуйста, прокомментируйте.