Формы в Symfony

20.01.2019 в 21:36
7438
+425

В предыдущем уроке мы наконец-то сделали кое-что полезное, а именно - заполнили базу фейковыми данными, вывели все записи и одну запись по её slug. Но давайте приблизимся к реальности ещё на шаг - и создадим форму добавления записи, как это происходит в реальных приложениях.

В Symfony каждая форма принадлежит конкретной сущности, что даёт нам удобство её обработки. Формы, как и многое другое во фреймворке, можно создавать через консоль. Название формы должно состоять из названия сущности и слова Type (т.е. PostType, UserType). Форму можно создать с помощью компонента maker следующей командой:

php bin/console make:form 

Symfony попросит вас ввести имя сущности, с которой связана форма. Введите Post. Дальше у вас сгенерируется папка Form, где вы найдёте класс PostType. Перейдя в него, вы должны увидеть следующий код:

В методе configureOptions() Symfony указывает, какая сущность будет источником данных для нашей формы. На данный момент нас интересует метод buildForm, который, собственно, и строит нашу форму из полей нашей сущности. Мы смело можем удалить поля slug и created_at, поскольку они будут генерироваться автоматически. Кроме того, нам доступны некоторые настройки для нашей формы: так, например, метод в add() мы можем указать, какой тип данных будет принимать то или иное поле - TextType или TextareaType, а также установить label, если он нужен. Вот так будет выглядеть наш метод теперь:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('title', TextType::class, [
               'label' => ''
           ])
        ->add('body', TextareaType::class, [
                'label' => ' '
           ])
        ;
}

Обратите внимание на то, что классы TextType и TextareaType должны быть загружены по следующим неймспейсам:

use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;

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

Итак, закончив, с созданием формы (да, на этом всё), используем же её для создания первой нашей публикации! Для этого перейдите в класс PostsController.php и создайте метод addPost(). Также не забудьте назначить маршрут нашему методу в аннотации Route и добавить имя.

Для начала нужно понять, где хранятся данные, передаваемые пользователем через форму. Как я уже упоминал в одной из предыдущих статьях, Symfony - HTTP фреймворк, данные он получает из класса Request и отдаёт в виде Response. Поэтому одним из параметров нашего метода будет объект $request, а вторым - знакомый нам класс Slugify. Прежде чем начнём писать наш метод, нам надо зарегистрировать класс Slugify как сервис в файле config/services.yaml. Вот как это будет выглядеть:

Строка autowire: true позволяет переложить на Symfony автоматическую загрузку нашего сервиса.

Кстати говоря, эту запись можно убрать

App\DataFixtures\AppFixtures:
        $slugify: 'Cocur\Slugify\Slugify'

Теперь мы зарегистрировали класс как сервис, а не как аргумент другого класса, в результате чего он стал нам доступен глобально.

Итак, приступим к созданию нашего метода.
Сначала мы создаём объект нашей сущности, которую и будем сохранять.

$post = new Post();

Дальше мы создаём форму с помощью метода createForm, который обязательным параметром принимает наш класс PostType и объект класса Post.

$form = $this->createForm(PostType::class, $post);

Далее мы обрабатываем наш $request с помощью метода handleRequest, который стал нам доступен после создания инстанса формы.

$form->handleRequest($request);

Теперь мы проверяем, нажата ли кнопка под формой и является ли она валидной в соответствии с теми правилами валидации, которые мы применили к нашей сущности. Если оба этих условия выполняются, мы устанавливаем slug с помощью уже известного нам метода slugify, куда передаём title статьи. Время для поля created_at берём текущее, которое возвращает нам объект класса DateTime() по умолчанию.

if ($form->isSubmitted() && $form->isValid()) {
            $post->setSlug($slugify->slugify($post->getTitle()));
            $post->setCreatedAt(new \DateTime());

Вызываем наш Manager, подготавливаем (persist) и сохраняем пост (flush). После сохранения редиректим пользователя на страницу со всеми постами.

$em = $this->getDoctrine()->getManager();
            $em->persist($post);
            $em->flush();

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

А сам метод addPosts() возвращает шаблон, куда в качестве аргумента передаём $form->createView(). Этот метод создаст саму форму в нашей вёрстке.

return $this->render('posts/new.html.twig', [
            'form' => $form->createView()
        ]);

Вот как полностью выглядит наш метод:

/**
     * @Route("/posts/new", name="new_blog_post")
     */
    public function addPost(Request $request, Slugify $slugify)
    {
        $post = new Post();
        $form = $this->createForm(PostType::class, $post);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $post->setSlug($slugify->slugify($post->getTitle()));
            $post->setCreatedAt(new \DateTime());

            $em = $this->getDoctrine()->getManager();
            $em->persist($post);
            $em->flush();

            return $this->redirectToRoute('blog_posts');
        }
        return $this->render('posts/new.html.twig', [
            'form' => $form->createView()
        ]);
    }

Теперь создайте шаблон в папке templates/posts под именем new.html.twig. Всё, что он будет делать, это рендерить форму. Вот как будет выглядеть наш шаблон:

{% extends 'base.html.twig' %}

{% block body %}
    {{ form_start(form) }}
        {{ form_row(form.title) }}
        {{ form_row(form.body, { 'attr': {
            'rows' : '10',
            'cols' : '10' }}) }}
    <button class="btn btn-default" type="submit">Сохранить</button>
    {{ form_end(form) }}
{% endblock %}

Twig позволяет по-разному рендерить форму: можно сразу вывести всю форму с помощью такой записи {{ form(form) }}, однако в этом случае будет сложнее управлять каждым полем отдельно - его стилями или названием. Поэтому мы используем теги form_start и form_end, между которыми выводим каждое поле в отдельности. Записи form.title и form.body соответствует реальным полям в нашей таблице и в свойствах нашей сущности. Также мы добавляем rows и cols для form.body (вы же помните, как мы указали, что это TextareaType?), чтобы форма не выглядела маленькой. До тегов form_end вы должны не забыть добавить кнопку. Теперь наша форма и метод-handler, которые её обрабатывает, готовы. Вы можете перейти по указанному адресу и попробовать написать и сохранить первую запись.

Ну что же, на этом уроке мы освоили очередной важный компонент фреймворка - формы. На следующем уроке мы напишем первое CRUD приложение, в результате чего вы научитесь создавать, редактировать и удалять записи.

loader
20.01.2019 в 21:36
7438
+425
Логические задачи с собеседований