Как сопоставить данные формы в Symfony с объектом только тогда, когда форма действительна?

Представьте себе форму примера в Symfony:

public function buildForm(FormBuilderInterface $builder)
{
    $builder
        ->add('email', EmailType::class, [
            'constraints' => 
                new NotBlank(),
                new IsUnique(),
            ],
        ])
        ->add('password', PasswordType::class, [
            'constraints' => 
                new NotBlank(),
                new IsStrongEnough(),
            ],
        ])
}

Теперь, когда я отправляю форму и удостоверяюсь, что она действительна, я хочу, чтобы $form->getData() возвращал мой DTO с именем CreateAccountCommand:

final class CreateAccountCommand
{
    private $email;
    private $password;

    public function __construct(string $email, string $password)
    {
        $this->email = $email;
        $this->password = $password;
    }

    public function getEmail(): string
    {
        return $this->email;
    }

    public function getPassword(): string
    {
        return $this->password;
    }
}

Пример контроллера:

$form = $this->formFactory->create(CreateAccountForm::class);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
    $this->commandBus->dispatch($form->getData());

    return new JsonResponse([]);
}

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

Я пробовал использовать метод Картограф данных, но перед проверкой вызывается метод mapFormsToData.

Это вообще возможно? Или я должен получить данные в виде массива и создать объект вне формы?

в модели некоторые поля (например, электронная почта) могут быть пустыми, но вы хотите, чтобы они были обязательными по форме?

Cid 03.09.2018 16:53

Я обновил свой вопрос.

emix 03.09.2018 16:54
->add('email', EmailType::class, array('required' => true))
Cid 03.09.2018 16:54

required => true - это просто проверка html5 (и в любом случае это значение по умолчанию), не знаю, зачем вы это написали. Как я уже сказал в вопросе, часть проверки работает нормально.

emix 03.09.2018 16:55

Таким образом, вы можете переопределить поля модели, допускающие значение NULL, для проверки формы. Мне пришлось использовать этот трюк наоборот, в моей модели были свойства, не допускающие значения NULL, и мне пришлось использовать 'required' => false для принудительной проверки

Cid 03.09.2018 16:57

У меня нет проблем с проверкой. Вопрос в том, как вернуть экземпляр CreateAccountCommand с getData() формы. Если у вас есть рабочее решение, опубликуйте его в качестве ответа.

emix 03.09.2018 16:58

Да, я опубликую способ управления формами с помощью Symfony 4

Cid 03.09.2018 17:09
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
7
1 797
2

Ответы 2

Вот как я привык обрабатывать формы в Symfony 4.1

Предположим, вам нужна простая форма добавления

Форма

class CreateAccountForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder)
    {
        //same method than yours
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array('data_class' => 'App\Entity\CreateAccountCommand'));
    }
}

Контроллер

public function add(Request $request)
{
    $createAccountCommand = new CreateAccountCommand();

    //The object is "injected" to the form, that way, it's mapped when submitted
    $form = $this->get('form.factory')->create(CreateAccountForm::class, $createAccountCommand);

    //Form was submitted
    if ($request->isMethod('POST') && $form->handleRequest($request)->isValid())
    {
        //no need to getData(), the object $createAccountCommand is directly mapped to the form and can be used as is
        $em = $this->getDoctrine()->getManager();
        //persist, convert to json, or whatever
        $em->persist($createAccountCommand);
        $em->flush();

        return ($this->redirectToRoute('someRoute'));
    }

    //form not submitted, or has been submit with errors (isValid() == false)
    return ($this->render('account/add.html.twig',
            array('form' => $form->createView())));
}

Способ есть, но он слишком сложен для того, чем вы хотите заниматься. Для этого вам нужно использовать Datamapper.

Я знаю, что это не лучшее решение, но самым простым решением было бы (на мой взгляд) добавление сеттеров к вашему классу модели и разрешение нулевых значений (РЕШЕНИЕ1). Другое решение - использовать другой объект для формы и построить свою команду после отправки (и вам не нужно изменять свою команду - РЕШЕНИЕ2).

public function handleForm(Request $request)
{
    /// SOLUTION 1
    $form = $this->createForm(RegisterFormType::class, new CreateAccountCommand());
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $command = $form->getData();
        // do whatever you want
    }
    // ... 

    // SOLUTION 2
    $obj = new \stdClass();
    $obj->login = '';
    $obj->password = '';
    $form = $this->createForm(LoginFormType::class, $obj);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $data = $form->getData();
        $command = new CreateAccountCommand($data->login, $data->password);
        // do whatever you want
    }
}

С этой моделью:

final class CreateAccountCommand
{
    //// ... 
    /**
     * @param string $email
     */
    public function setEmail(string $email): void
    {
        $this->email = $email;
    }

    /**
     * @param string $password
     */
    public function setPassword(string $password): void
    {
        $this->password = $password;
    }

}

Другие вопросы по теме