То, что вы ищете, - это использование коллекций (которые вы связали) и Fieldsets .
Вы используете набор полей для представления сущности.В этом примере Fieldset имеет значение Location
, прикрепленное к School
.
Кроме того, School
как One To Many
отношение с Location
.
Таким образом, у вас будет класс SchoolFieldset
, для которого требуется элемент Collection
.
Ниже приведен очень упрощенный пример настройки.
Backend
LocationFieldset
class LocationFieldset
{
public function init()
{
parent::init();
$this->add([
'name' => 'name',
'required' => true,
'type' => Text::class,
'options' => [
'label' => _('Name'),
],
]);
// ... Add whatever for Location
}
}
SchoolFieldset
class SchoolFieldset
{
/**
* @var LocationFieldset
*/
protected $locationFieldset;
public function __construct(LocationFieldset $locationFieldset)
{
$this->locationFieldset($locationFieldset);
}
public function init()
{
parent::init();
$this->add([
'name' => 'name',
'required' => true,
'type' => Text::class,
'options' => [
'label' => _('Name'),
],
]);
$this->add([
'type' => Collection::class,
'required' => true,
'name' => 'locations',
'options' => [
'label' => _('Locations'),
'count' => 1, // Initial amount of Fieldsets on-load
'allow_add' => true, // Allows creation of 0/multiple
'allow_remove' => true, // Allows removal
'should_create_template' => true, // Creates template in the HTML in a <span data-template="the whole html here"></span> -> JavaScript this bit for duplication/removal
'target_element' => $this->locationFieldset, // A pre-loaded Fieldset must be passed here, not just the FQCN as you would for including a Fieldset not in a Collection
],
]);
// ... Add whatever
}
}
SchoolForm
class SchoolForm extends CustomAbstractForm
{
public function init()
{
$this->add([
'name' => 'school',
'type' => SchoolFieldset::class,
'options' => [
'use_as_base_fieldset' => true,
],
]);
//Call parent initializer. (Default for me it adds a submit button)
parent::init();
}
}
Front-end
В представлении формы я загружаю немного JavaScript.Он основан на демонстрационных данных, приведенных в документации Zend Framework.
Обратите внимание, что эти документы не учитывают удаление (поэтому, если у вас есть HTML-объекты с идентификаторами 0-1-2, и вы удаляете 1, он будет считаться, придет к 2 и создаст еще 2,давая вам 0-2-2 и, следовательно, перезаписать второй, который у вас уже был).
JavaScript, который у меня есть в проекте на данный момент, таков (извините, не могу дать вам все это, но это должно вас начать):
Кнопки
var $addButton = $('<button type="button" data-action="add-fieldset" class="btn btn-primary">Add another</button>');
var $removeButton = $('<button type="button" data-action="remove-fieldset" class="btn btn-danger">Remove</button>');
Использование
$('body').on('click', 'button[type="button"][data-action="add-fieldset"]', function () {
addCollectionFieldset(this);
});
$('body').on('click', 'button[type="button"][data-action="remove-fieldset"]', function () {
removeCollectionFieldset(this);
});
function addCollectionFieldset(element) {
var $element = $(element);
var $fieldsetDataSpan = $element.siblings('span[data-name="fieldset-data"]');
var fieldsetCount = $fieldsetDataSpan.data('fieldset-count');
var escapedTemplate = $element.siblings('span[data-template]').data('template');
var $replaced = $(escapedTemplate.replace(/__index__/g, fieldsetCount));
$replaced.append($removeButton.clone());
$($replaced).insertAfter($element.siblings('fieldset:last'));
$('<hr>').insertBefore($element.siblings('fieldset:last'));
$fieldsetDataSpan.data('fieldset-count', fieldsetCount + 1); // Up the count by one fieldset
}
function removeCollectionFieldset(element) {
$(element).parent().remove();
}
Примечание: кнопка «Удалить» размещена в каждом наборе полей в коллекции.Кнопка «Добавить еще» находится под коллекцией.
Как решить эту проблему, зависит от вас.
Просмотр
<?= $this->form($form) ?>
<?php $this->inlineScript()->prependFile($this->basePath('js/form.js')) ?>
Действие контроллера
public function addAction()
{
/** @var SchoolForm $form */
$form = $this->getSchoolForm();
/** @var Request $request */
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
/** @var School $school */
$school = $form->getObject();
$this->getObjectManager()->persist($school);
try {
$this->getObjectManager()->flush();
} catch (Exception $e) {
throw new Exception(
'Could not save. Error was thrown, details: ' . $e->getMessage(),
$e->getCode(),
$e->getPrevious()
);
}
return $this->redirectToRoute('schools/view', ['id' => $school->getId()]);
}
}
return [
'form' => $form,
'validationMessages' => $form->getMessages() ?: '',
];
}
ControllerFactory
class AddControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
/** @var ObjectManager $objectManager */
$objectManager = $container->get(EntityManager::class);
/** @var FormElementManagerV3Polyfill $formElementManager */
$formElementManager = $container->get('FormElementManager');
/** @var SchoolForm $schoolForm */
$schoolForm = $formElementManager->get(SchoolForm::class);
return new AddController($objectManager, $schoolForm);
}
}
FormFactory
class SchoolFormFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$objectManager = $container->get(EntityManager::class);
$translator = $container->get('MvcTranslator');
$inputFilterPluginManager = $container->get('InputFilterManager');
$inputFilter = $inputFilterPluginManager->get(SchoolFormInputFilter::class); // Did not show this one
/** @var SchoolForm $form */
$form = new SchoolForm();
$form->setObjectManager($objectManager);
$form->setTranslator($translator);
$form->setInputFilter($inputFilter);
return $form;
}
}
FormFactory
class SchoolFieldsetFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$objectManager = $container->get(EntityManager::class);
$translator = $container->get('MvcTranslator');
$fieldset = new SchoolFieldset();
$fieldsetObject = new School();
/** @var SchoolFieldset $fieldset */
$fieldset = new $fieldset($objectManager(), 'school');
$fieldset->setHydrator(
new DoctrineObject($objectManager())
);
$fieldset->setObject($fieldsetObject);
$fieldset->setTranslator($translator);
return $fieldset;
}
}
Если у вас есть несколько свободных моментов, я бы посоветовал вам посмотреть другие примеры в репозитории, который я создал, чтобы помочь быстро создавать формы в ZF и ZF с Doctrine.ReadMe с примерами находится здесь