Дата обновления перевода 2021-06-08
Поле CollectionType¶
Этот тип поля исполльзуется для отображения “коллекции” некотрого поля или
формы. В самом простом смысле, он может быть массивом полей TextType
,
наполняющих значения массива emails
. В более сложных примерах, вы можете
встраивать целые формы, что полезно при создании форм, которые представляют
отношения один-ко-многим (например, продукт, откуда вы можете управлять многими
фото, свзяанными с продуктом).
Отображается как | зависит от опции entry_type |
Опции | |
Переопределенные опции | |
Наследуемые опции | |
Недопустимое сообщение по умолчанию | Коллекция недопустима. |
Наследуемое недопустимое сообщение | Значение {{ value }} недопустимо. |
Родительский тип | FormType |
Класс | CollectionType |
Tip
The full list of options defined and inherited by this form type is available running this command in your app:
1 2 | # replace 'FooType' by the class name of your form type
$ php bin/console debug:form FooType
|
Note
Если вы работаете с коллекцией сущностей Doctrine, обратите особое внимание на опции allow_add, allow_delete и by_reference. Вы также можете увидеть полный пример в статье How to Embed a Collection of Forms.
Базовое применение¶
Этот тип используется, когда вы хотите управлять коллекцией похожих объектов
в форме. Например, представьте, что у вас есть поле emails
, которое соответствует
массиву электронных адресов. В форме вам надо выразить каждый адрес в виде
собственного поля ввода:
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
// ...
$builder->add('emails', CollectionType::class, [
// каждая запись в массиве будет полем "email"
'entry_type' => EmailType::class,
// эти опции передаются каждому типу "email"
'entry_options' => array(
'attr' => ['class' => 'email-box'],
],
]);
Самый простой способ отобразить всё одномоментно:
1 | {{ form_row(form.emails) }}
|
Намного более гибкий метод будет выглядеть так:
1 2 3 4 5 6 7 8 9 10 11 | {{ form_label(form.emails) }}
{{ form_errors(form.emails) }}
<ul>
{% for emailField in form.emails %}
<li>
{{ form_errors(emailField) }}
{{ form_widget(emailField) }}
</li>
{% endfor %}
</ul>
|
В обоих случаях, поля ввода не будут отображены, разве что ваши массив данных
emails
уже не содержал некоторые электронные адреса.
В этом простом примере, всё ещё возможно добавлять новые адреса или удалять
существующие. Добавление новых адресов возможно с использованием опции
allow_add (и факультативно опции prototype) (см. пример ниже). Удаление
электронных адресов из массива emails
возможно с опцией allow_delete.
Добавление и удаление объектов¶
Если allow_add установлен, как true
, то при отправке любых неопознанных
объектов, они будут незаметно добавлены к массиву объектов. В теории это отлично,
но на практике требует немного больше усилий, чтобы получить правильный клиентский
JavaScript.
Следуя предыдущему примеру, представьте, что вы начинаете с двух адресов
в массиве данных emails
. В этом случае, будут отображены два поля ввода,
которые будут выглядеть как-то так (в зависимости от имени вашей формы):
1 2 | <input type="email" id="form_emails_0" name="form[emails][0]" value="[email protected]" />
<input type="email" id="form_emails_1" name="form[emails][1]" value="[email protected]" />
|
Чтобы разрешить вашему пользователю добавить ещё один адрес, просто установите
allow_add, как true
и через JavaScript отобразите другое поле с именем
form[emails][2]
(и так далее для большего количества полей).
Чтобы облегчить это, установка опции prototype, как true
позволяет
вам отобразить поле “шаблона”, которое вы потом можете использовать в вашем
JavaScript, чтобы помочь вам динамично создавать эти новые поля. Отображённое
поле прототипа будет выглядеть так:
1 2 3 4 5 | <input type="email"
id="form_emails___name__"
name="form[emails][__name__]"
value=""
/>
|
Заменив __name__
некоторым уникальным значением (например, 2
),
вы можете построить и вставить новые HTML-поля в вашу форму.
Используя jQuery, простой пример может выглядеть так. Если вы отображаете
все ваши поля коллекции одновременно (например, form_row(form.emails)
),
то всё ещё проще, так как атрибут data-prototype
отображается автоматически
для вас (с небольшим отличием - см. ниже), и всё, что вам нужно - это такой
код JavaScript:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // add-collection-widget.js
jQuery(document).ready(function () {
jQuery('.add-another-collection-widget').click(function (e) {
var list = jQuery(jQuery(this).attr('data-list-selector'));
// Попробуйте найти счётчик списка или используйте длину списка
var counter = list.data('widget-counter') || list.children().length;
// возьмите шаблон прототипа
var newWidget = list.attr('data-prototype');
// замените "__name__", используемое в id и названии прототипа
// числом, уникальным для ваших электронных адресов
// конечное имя атрибута выглядт как name="contact[emails][2]"
newWidget = newWidget.replace(/__name__/g, counter);
// Увеличьте счётчик
counter++;
// И сохраните его, длина не может быть использована, если разрешено удаление виджетов
list.data(' widget-counter', counter);
// создайте новый элемент списка и добавьте его в список
var newElem = jQuery(list.attr('data-widget-tags')).html(newWidget);
newElem.appendTo(list);
});
});
|
И обновите шаблон следующим образов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | {{ form_start(form) }}
{# ... #}
{# сохраните прототип в атрибуте data-prototype #}
<ul id="email-fields-list"
data-prototype="{{ form_widget(form.emails.vars.prototype)|e }}"
data-widget-tags="{{ '<li></li>'|e }}"
data-widget-counter="{{ form.emails|length }}">
{% for emailField in form.emails %}
<li>
{{ form_errors(emailField) }}
{{ form_widget(emailField) }}
</li>
{% endfor %}
</ul>
<button type="button"
class="add-another-collection-widget"
data-list-selector="#email-fields-list">Add another email</button>
{# ... #}
{{ form_end(form) }}
<script src="add-collection-widget.js"></script>
|
Tip
Если вы отображаете сразу целую коллекцию, то прототип автоматически
доступен в атрибуте data-prototype
элемента (например, div
или
table
), который окружает вашу коллекцию. Единственное отличие заключается
в том, что весь “ряд формы” отображается для вас, что означает, что вам
не нужно будет заключать его в какой-либо элемент контейнера, как было
сделано выше.
Опции поля¶
allow_add¶
тип: boolean
по умолчанию: false
Если установлена, как true
, то при отправке в коллекцию неопознанных объектов,
они будут добавлены в качестве новых. Окончательный массив будет содержать существующие
объекты,а также новый объект, который был в отправленных данных. См. пример выше,
чтобы узнать больше.
Опция prototype может быть использована, чтобы помочь отобразить объект прототипа, который может быть использован с JavaScript для динамичного создания новых объектов формы на клиентской стороне. Чтобы узнать больше, см. пример выше и Allowing “new” Tags with the “Prototype”.
Caution
Если вы встраиваете целые другие формы, чтобы отобразить отношение DB один-ко-многим, то вам может понадобиться вручную убедиться в том, что сторонний ключ этих новых объектов установлен правильно. Если вы используете Doctrine, то это не произойдёт автоматически. См. ссылку выше, чтобы узнать больше деталей.
allow_delete¶
тип: boolean
по умолчанию: false
Если установлена, как true
, то если существующий объект не содержится в
отправленных данных, он будет правильно отсутствовать в итоговом массиве объектов.
Это означает, что вы можете реализовать кнопку “удалить” через JavaScript, который
просто удалит элемент формы из DOM. Когда пользователь отправляет форму, её
отсутствие в отправленных данных будет означать, что она удалена из итогового
массива.
Чтобы получить больше информации, см. Allowing Tags to be Removed.
Caution
Будьте осторожны используя эту опцию, когда вы встраиваете коллекцию объектов. В этом случае, если удаляются любые встроенные формы, они будут правильно отсутствовать в итоговом массиве объектов. Однако, в зависимости от логики вашего приложения, когда один из этих объектов удаляется, вы можете захоеть удалить его или по крайней мере ссылку его стороннего ключа на главный объект. Ничего из этого не происходит автоматически. Чтобы узнать больше, см. Allowing Tags to be Removed.
delete_empty¶
тип: Boolean
или callable
по умолчанию: false
Если вы хотите ясно удалить абсолютно пустые записи коллекций из вашей формы,
то вам нужно установить эту опцию, как true
. Однако, существующие записи
коллекции будут удалены только, если у вас включена опция allow_delete. Иначе
пустые значения будут оставлены.
Caution
Опция delete_empty
удаляет только объекты, когда нормализованное значение
равно null
. Если встроенный entry_type - это сложный тип формы, то вы
должны либо установить опцию required
, как false
, либо установить опцию
empty_data
, как null
. Обе эти опции могут быть установлены внутри
entry_options. Прочтите об опции формы empty_data,
чтобы узнать, зачем это нужно.
Значение удаляется из коллекции только если нормализированные значение - null
.
Однако, вы также можете установить значение опции, как вызываемое, которое будет
выполнено для каждого значения в отправленной коллекции. Если вызываемое вернёт
true
, то значение удаляется из коллекции. Например:
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
// ...
$builder->add('users', CollectionType::class, [
// ...
'delete_empty' => function (User $user = null) {
return null === $user || empty($user->getFirstName());
},
]);
Использование вызываемого особенно полезно в случае со сложными типами форм, которые могут определять сложные условия для рассмотрения их, как пустых.
entry_options¶
тип: array
по умолчанию: []
Это массив, который передаётся типу формы, указанному в опции entry_type. Например, если мы использовали ChoiceType в качестве вашей опции entry_type (например, для коллекции выпадающих меню), то вам нужно хотя бы передать опцию ``choices``основоположному типу:
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
// ...
$builder->add('favorite_cities', CollectionType::class, array(
'entry_type' => ChoiceType::class,
'entry_options' => array(
'choices' => array(
'Nashville' => 'nashville',
'Paris' => 'paris',
'Berlin' => 'berlin',
'London' => 'london',
),
),
));
entry_type¶
тип: string
по умолчанию: 'Symfony\Component\Form\Extension\Core\Type\TextType'
Это тип поля для каждого объекта в этой коллекции (например, TextType
,
ChoiceType
, и т.л.). Например, если у вас есть массив адресов электронной
почты, то вы будете использовать EmailType.
Если вы хотите встроить коллекцию в какую-то другую форму, создайте новый
экземпляр вашего типа формы и передайте его в качестве опции.
prototype¶
тип: boolean
по умолчанию: true
Эта опция полезна при использовании опции allow_add. Если true
(и
если allow_add также true
), будет доступен специальный атрибут
“прототипа”, чтобы вы могли отобразить пример “Шаблона” того, как должен
выглядеть новый элемент на вашей странице. Атрибут name
данный этому
элементу - __name__
. Это позволяет вам добавлять кнопку “добавить ещё”
через JavaScript, который считывает прототип, заменяет __name__
некоторым
уникальным именем или числом и отображает его внутри вашей формы. При отправке,
он будет добавлен в ваш основоположный массив благодаря опции allow_add.
Поле прототипа может быть отображено через переменную prototype
в поле коллекции:
1 | {{ form_row(form.emails.vars.prototype) }}
|
Заметьте, что всё, что вам на самом деле нужно, это “виджет”, но в зависимости от того, как вы отображаете вашу форму, наличие целого “Ряда формы” может быть легче для вас.
Tip
Если вы отображает целую коллекцию полей одновременно, то прототип ряда формы
автоматически доступен в атрибуте data-prototype
элемента (например, div
или table
), который окружает вашу коллекцию.
Чтобы узнать детали о том, как действительно использовать эту опцию, см. пример выше, а также Allowing “new” Tags with the “Prototype”.
prototype_data¶
тип: mixed
по умолчанию: null
Позвляет вам определять конкретные данные для прототипа. Каждый новый добавленный ряд будет изначально содержать данные, установленые этой опцией. По умолчанию, будут использованы данные, сконфигурированные для всех записей с опцией entry_options:
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
// ...
$builder->add('tags', CollectionType::class, [
'entry_type' => TextType::class,
'allow_add' => true,
'prototype' => true,
'prototype_data' => 'New Tag Placeholder',
]);
prototype_name¶
тип: string
по умолчанию: __name__
Если у вас есть несколько коллекций в вашей форме, или даже хуже, вложенные коллекции, вы можете захотеть изменить заполнитель так, чтобы несвязанные заполнители не заменялись те же значением.
Переопределенные опции¶
invalid_message
¶
type: string
default: This value is not valid
This is the validation error message that’s used if the data entered into this field doesn’t make sense (i.e. fails validation).
This might happen, for example, if the user enters a nonsense string into
a TimeType field that cannot be converted
into a real time or if the user enters a string (e.g. apple
) into a
number field.
Normal (business logic) validation (such as when setting a minimum length for a field) should be set using validation messages with your validation rules (reference).
Наследуемые оцпии¶
Эти опции наследуются из FormType. Не все опции указаны здесь - только наиболее применимые к данному типу:
attr
¶
type: array
default: []
If you want to add extra attributes to an HTML field representation
you can use the attr
option. It’s an associative array with HTML attributes
as keys. This can be useful when you need to set a custom class for some widget:
$builder->add('body', TextareaType::class, [
'attr' => ['class' => 'tinymce'],
]);
See also
Use the row_attr
option if you want to add these attributes to
the form type row element.
by_reference
¶
type: boolean
default: true
In most cases, if you have an author
field, then you expect setAuthor()
to be called on the underlying object. In some cases, however, setAuthor()
may not be called. Setting by_reference
to false
ensures that the setter is
called in all cases.
To explain this further, here’s a simple example:
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
// ...
$builder = $this->createFormBuilder($article);
$builder
->add('title', TextType::class)
->add(
$builder->create('author', FormType::class, ['by_reference' => ?])
->add('name', TextType::class)
->add('email', EmailType::class)
)
If by_reference
is true, the following takes place behind the scenes
when you call submit()
(or handleRequest()
) on the form:
$article->setTitle('...');
$article->getAuthor()->setName('...');
$article->getAuthor()->setEmail('...');
Notice that setAuthor()
is not called. The author is modified by reference.
If you set by_reference
to false, submitting looks like this:
$article->setTitle('...');
$author = clone $article->getAuthor();
$author->setName('...');
$author->setEmail('...');
$article->setAuthor($author);
So, all that by_reference=false
really does is that it clones the object,
which enforces the framework to call the setter on the parent object.
Similarly, if you’re using the CollectionType
field where your underlying collection data is an object (like with
Doctrine’s ArrayCollection
), then by_reference
must be set to false
if you need the adder and remover (e.g. addAuthor()
and removeAuthor()
)
to be called.
Значение по умолчанию - []
(пустой массив).
error_bubbling¶
тип: boolean
по умолчанию: true
If true
, any errors for this field will be passed to the parent field
or form. For example, if set to true
on a normal field, any errors for
that field will be attached to the main form, not to the specific field.
error_mapping
¶
type: array
default: []
This option allows you to modify the target of a validation error.
Imagine you have a custom method named matchingCityAndZipCode()
that validates
whether the city and zip code match. Unfortunately, there is no matchingCityAndZipCode
field in your form, so all that Symfony can do is display the error on top
of the form.
With customized error mapping, you can do better: map the error to the city field so that it displays above it:
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'error_mapping' => [
'matchingCityAndZipCode' => 'city',
],
]);
}
Here are the rules for the left and the right side of the mapping:
- The left side contains property paths;
- If the violation is generated on a property or method of a class, its
path is the
propertyName
; - If the violation is generated on an entry of an
array
orArrayAccess
object, the property path is[indexName]
; - You can construct nested property paths by concatenating them, separating
properties by dots. For example:
addresses[work].matchingCityAndZipCode
; - The right side contains the names of fields in the form.
By default, errors for any property that is not mapped will bubble up to the
parent form. You can use the dot (.
) on the left side to map errors of all
unmapped properties to a particular field. For instance, to map all these
errors to the city
field, use:
$resolver->setDefaults([
'error_mapping' => [
'.' => 'city',
],
]);
help¶
type: string
or TranslatableMessage
default: null
Allows you to define a help message for the form field, which by default is rendered below the field:
use Symfony\Component\Translation\TranslatableMessage;
$builder
->add('zipCode', null, [
'help' => 'The ZIP/Postal code for your credit card\'s billing address.',
])
// ...
->add('status', null, [
'help' => new TranslatableMessage('order.status', ['%order_id%' => $order->getId()], 'store'),
])
;
help_attr¶
type: array
default: []
Sets the HTML attributes for the element used to display the help message of the form field. Its value is an associative array with HTML attribute names as keys. These attributes can also be set in the template:
1 2 3 | {{ form_help(form.name, 'Your name', {
'help_attr': {'class': 'CUSTOM_LABEL_CLASS'}
}) }}
|
help_html¶
type: boolean
default: false
By default, the contents of the help
option are escaped before rendering
them in the template. Set this option to true
to not escape them, which is
useful when the help contains HTML elements.
label
¶
type: string
default: The label is “guessed” from the field name
Sets the label that will be used when rendering the field. Setting to false
will suppress the label. The label can also be set in the template:
- Twig
1
{{ form_label(form.name, 'Your name') }}
- PHP
1 2 3 4
echo $view['form']->label( $form['name'], 'Your name' );
label_attr
¶
type: array
default: []
Sets the HTML attributes for the <label>
element, which will be used
when rendering the label for the field. It’s an associative array with HTML
attribute as a key. This attributes can also be directly set inside the
template:
- Twig
1 2 3
{{ form_label(form.name, 'Your name', { 'label_attr': {'class': 'CUSTOM_LABEL_CLASS'} }) }}
- PHP
1 2 3 4 5
echo $view['form']->label( $form['name'], 'Your name', ['label_attr' => ['class' => 'CUSTOM_LABEL_CLASS']] );
label_format
¶
type: string
default: null
Configures the string used as the label of the field, in case the label
option was not set. This is useful when using
keyword translation messages.
If you’re using keyword translation messages as labels, you often end up having
multiple keyword messages for the same label (e.g. profile_address_street
,
invoice_address_street
). This is because the label is built for each “path”
to a field. To avoid duplicated keyword messages, you can configure the label
format to a static value, like:
// ...
$profileFormBuilder->add('address', AddressType::class, [
'label_format' => 'form.address.%name%',
]);
$invoiceFormBuilder->add('invoice', AddressType::class, [
'label_format' => 'form.address.%name%',
]);
This option is inherited by the child types. With the code above, the label of
the street
field of both forms will use the form.address.street
keyword
message.
Two variables are available in the label format:
%id%
- A unique identifier for the field, consisting of the complete path to the
field and the field name (e.g.
profile_address_street
); %name%
- The field name (e.g.
street
).
The default value (null
) results in a
“humanized” version of the field name.
Note
The label_format
option is evaluated in the form theme. Make sure to
update your templates in case you
customized form theming.
mapped
¶
type: boolean
default: true
If you wish the field to be ignored when reading or writing to the object,
you can set the mapped
option to false
.
required
¶
type: boolean
default: true
If true, an HTML5 required attribute will be rendered. The corresponding
label
will also render with a required
class.
This is superficial and independent of validation. At best, if you let Symfony guess your field type, then the value of this option will be guessed from your validation information.
Note
The required option also affects how empty data for each field is handled. For more details, see the `empty_data`_ option.
row_attr¶
type: array
default: []
An associative array of the HTML attributes added to the element which is used to render the form type row:
$builder->add('body', TextareaType::class, [
'row_attr' => ['class' => 'text-editor', 'id' => '...'],
]);
See also
Use the attr
option if you want to add these attributes to
the form type widget element.
Переменные поля¶
Переменная | Тип | Использование |
---|---|---|
allow_add | boolean |
Значение опции allow_add. |
allow_delete | boolean |
Значение опции allow_delete. |
Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.