Как использовать форму без класса данных

Дата обновления перевода 2024-07-19

Как использовать форму без класса данных

В большинстве случаев, форма привязана к объекту, а поля формы получают и сохраняют своди данные в свойствах этого объекта. Это именно то, о чём идёт речь в главной статье о формах.

Но иногда вам может быть нужно использовать форму без класса, и получить обратно массив отправленных данных. Это на самом деле очень просто. Метод 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();
        }

        // ... отобразить форму
    }
}

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

  1. Передать объект при создании форму (в качестве первого аргумента createFormBuilder() или второго аргумента createForm());
  2. Объявить в вашей форме опцию 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\Component\Validator\Constraints\Valid, кроме случаев, когда вы отключаете валидацию.

Caution

Когда форма отправлена только частично (например, в запросе 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
28
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
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
{
    $constraints = [
        'firstName' => new Length(['min' => 3]),
        'lastName' => [
            new NotBlank(),
            new Length(['min' => 3]),
        ],
    ];

    $resolver->setDefaults([
        'data_class' => null,
        'constraints' => $constraints,
    ]);
}

Это означает, что вы также можете сделать это при использовании метода 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();