Как использовать форму без класса данных
Дата обновления перевода 2025-08-25
Как использовать форму без класса данных
В большинстве случаев, форма привязана к объекту, а поля формы получают и сохраняют своди данные в свойствах этого объекта. Это именно то, о чём идёт речь в главной статье о формах.
Но иногда вам может быть нужно использовать форму без класса, и получить
обратно массив отправленных данных. Это на самом деле очень просто. Метод
getData()
позволяет вам делать именно это:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
// src/Controller/ContactController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
// ...
class ContactController extends AbstractController
{
public function contact(Request $request): Response
{
$defaultData = ['message' => 'Type your message here'];
$form = $this->createFormBuilder($defaultData)
->add('name', TextType::class)
->add('email', EmailType::class)
->add('message', TextareaType::class)
->add('send', SubmitType::class)
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// данные - это массив с ключами "name", "email" и "message"
$data = $form->getData();
}
// ... отобразить форму
}
}
По умолчанию, форма на самом деле предполагает, что вы хотите работать с массивами данных, вместо объекта. Существует ровно два способа, чтобы изменить это поведение и привязать форму к объекту:
- Передать объект при создании форму (в качестве первого аргумента
createFormBuilder()
или второго аргументаcreateForm()
); - Объявить в вашей форме опцию
data_class
.
Если вы не сделаете ничего из этого, тогда форма будет возвращать данные
в виде массива. В этом примере, так как $defaultData
- это не объект
(и не установлена опция data_class
), $form->getData()
в конечном счёте
возвращает массив.
Tip
Вы также можете получить доступ к значениям POST (в этом случае - "name") напрямую через объект запроса, вот так:
1
$request->request->get('name');
Но имейте в виду, что в большинстве случаев, использование метода getData()
будет лучше, так как он возвращает данные (обычно в виде объекта) после того,
как они были преобразованы компонентом формы.
Добавление валидации
Единственным недостающим элементом является валидация. Обычно, когда вы вызываете
$form->handleRequest($request)
, объект валидируется путём считывания ограничений, которые вы
применили к этому классу. Если ваша форма привязана к объекту (т.е. вы используете
опцию data_class
или передаёте объект в вашу форму), то вы захотите использовать
этот подход практически всегда. Смотрите Валидация, чтобы узнать больше.
Но если форма не привязана к объекту, и вы хотите получить простой массив ваших отправленных данных, как вы можете добавить ограничения к данным вашей формы?
Ограничения на уровне поля
Один из вариантов - установить ограничения самостоятельно и присоединить их к индивидуальным полям. Этот подход детальнее рассматривается в этой статье о валидации, но вот краткий пример:
use SymfonyComponentFormExtensionCoreTypeTextType; use SymfonyComponentFormFormBuilderInterface; use SymfonyComponentValidatorConstraintsLength; use SymfonyComponentValidatorConstraintsNotBlank;
public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('firstName', TextType::class, [ 'constraints' => new Length(min: 3), ]) ->add('lastName', TextType::class, [ 'constraints' => [ new NotBlank(), new Length(min: 3), ], ]) ; }
Tip
Если вы используете группы валидации, вам нужно либо сослаться на
группу Default
при создании формы, или установить правильную группу
в ограничении, которое вы добавляете:
1
new NotBlank(['groups' => ['create', 'update']]);
Tip
Если форма не привязана к объекту, то каждый объект в вашем массиве
отправленных данных валидируется с использованием ограничения
Symfony
, кроме случаев, когда
вы отключаете валидацию.
Warning
Когда форма отправлена только частично (например, в запросе HTTP PATCH), будут оцениваться только ограничения из представленных полей формы.
Ограничения на уровне класса
Другой вариант - добавить ограничения на уровне класса. Это можно сделать,
установив опцию constraints
в методе configureOptions()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('firstName', TextType::class)
->add('lastName', TextType::class);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => null,
'constraints' => new Collection([
'firstName' => new Length(min: 3),
'lastName' => [
new NotBlank(),
new Length(min: 3),
],
]),
]);
}
Это означает, что вы также можете сделать это при использовании метода
createFormBuilder()
в вашем контроллере:
1 2 3 4 5 6 7 8 9 10 11 12
$form = $this->createFormBuilder($defaultData, [
'constraints' => [
'firstName' => new Length(['min' => 3]),
'lastName' => [
new NotBlank(),
new Length(['min' => 3]),
],
],
])
->add('firstName', TextType::class)
->add('lastName', TextType::class)
->getForm();
Обусловленные ограничения
Возможно определить ограничения поля, которые зависят от значений других полей
(например, поле должно быть не пустым, если другое поле имеет какое-то значение).
Чтобы достичь этого, используйте опцию expression
в
ограничении When, чтобы сослаться на другое поле:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
$builder
->add('how_did_you_hear', ChoiceType::class, [
'required' => true,
'label' => 'How did you hear about us?',
'choices' => [
'Search engine' => 'search_engine',
'Friends' => 'friends',
'Other' => 'other',
],
'expanded' => true,
'constraints' => [
new Assert\NotBlank(),
]
])
// это поле является обязательным только если значение поля 'how_did_you_hear' - 'other'
->add('other_text', TextType::class, [
'required' => false,
'label' => 'Please specify',
'constraints' => [
new Assert\When(
expression: 'this.getParent().get("how_did_you_hear").getData() == "other"',
constraints: [
new Assert\NotBlank(),
],
)
],
])
;