Работаем с шаблонизатором Symfony

18.01.2022 в 22:18
7369
+404

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

Для начала создадим action posts(), который будет выводить все посты. Нам понадобится класс PostRepository, отвечающий за работу с таблицей постов. Однако в Symfony есть как минимум два способа работы с репозиторием. Поскольку мы унаследовались от AbstractController, мы можем достать репозиторий следующим образом и это будет первый способ:

$repo = $this->getDoctrine()->getRepository(Post::class);

То есть мы передаём в getRepository() в качестве переменной ссылку на сущность Post. Теперь $repo является объектом класса PostRepository, ему доступны все методы, предоставляемые стандартными репозиториями. Есть и другой способ, знакомый вам из курса по ООП, - внедрение зависимостей через конструктор. Я предпочитаю именно этот способ, однако вы вольны выбрать любой. На данном этапе класс контроллера выглядит так:

<?php

declare(strict_types=1);

namespace App\Controller;

use App\Entity\Post;
use App\Repository\PostRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

class PostsController extends AbstractController
{
    /** @var PostRepository $postRepository */
    private $postRepository;

    public function __construct(PostRepository $postRepository)
    {
        $this->postRepository = $postRepository;
    }

Пока ничего нового, обычное ООП. Но приступим же к первому экшену posts. Что нам нужно? Получить все записи и отправить их в шаблон. Создаём переменную $posts, в которой будем хранить результата выполнения запроса $this->postRepository->findAll(). Проще говоря, это массив записей, по которым мы пройдёмся в цикле в нашем шаблоне и достанем только названия статей. Наш action становится действительно полезным, а самое главное - ничего лишнего:

   /**
     * @Route("/posts", name="blog_posts")
     */
    public function posts()
    {
        $posts = $this->postRepository->findAll();

        return $this->render('posts/index.html.twig', [
           'posts' => $posts
        ]);
    }

И не забудьте настроить маршрут, по которому мы будем видеть все посты. Думаю, этот экшен вы поймёте без проблем: получаем записи из репозитория и отправляем их в шаблон, который создался после создания контроллера.
Работать с циклами twig мы уже научились, так что делаем то же самое, что и на первом уроке:

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

{% block title %} {% endblock %}

{% block body %}
    {% for post in posts %}
        <a href="#">{{ post.title }}</a><br><br>
    {% endfor %}
{% endblock %}

Запускаем сервер командой php bin/console server:start и... видим ошибку. Да, Symfony ругается на то, что ему незнаком сервис Slugify из нашего прошлого урока. И правда, такого сервиса во фреймворке не существует. Но Symfony не был бы таким популярным, если бы легко не решал эту проблему. Для этого найдите файл config/services.yaml. В самом низу, на уровне с этой строкой App\Controller\ напишите следующее:

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

Это значит, что мы прямо указали, что класс AppFixtures принимает переменную $slugify, которая является объектом класса Cocur\Slugify\Slugify. Кто не понял, как это должно выглядеть, прилагаю скриншот:

Теперь запустите сервер ещё раз и перейдите по адресу, указанному в терминале, по маршруту /posts. Вы должны увидеть список наших постов. Теперь мы должны сделать возможность нажимать на ссылки и переходить к отдельной записи по её slug, чтобы увидеть контент. На самом деле, это делается даже легче, чем первый экшен, серьёзно. Я приведу его реализацию и объясню, что происходит:

   /**
     * @Route("/posts/{slug}", name="blog_show")
     */
    public function post(Post $post)
    {
        return $this->render('posts/show.html.twig', [
            'post' => $post
        ]);
    }

В первую очередь обратите внимание на роутинг. Я уже упоминал в одном из первых уроков, что значения, заключенные в фигурные скобки, называются плейсхолдерами - они меняются в зависимости от запроса пользователя. А ещё, Symfony достаточно умный, чтобы понимать, по какому критерию вы достаёте данные, так что если вы напишите вместо slug id, title или body, он отдаст вам нужные данные конкретного поста! Всё это происходит благодаря аннотации ParamConverter, которую в нашем случае использовать необязательно.

Теперь переходим шаблон и пишем следующее:

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

{% block body %}
    <p>{{ post.body }}</p>
{% endblock %}

Осталось только починить наши ссылки, чтобы можно было по ним переходить. Возвращаемся в шаблон posts/index.html.twig и меняем ссылку на новую:

<a href="{{ path('blog_show', {'slug': post.slug}) }}">{{ post.title }}</a>

Обратите внимание на href, мы использовали хелпер twig path(), который принимает в качестве аргументов имя маршрута (это те самые имена, которые мы указывали в аннотации Route, помните?) и второй аргумент для генерации динамического урла. В нашем случае это slug. В фигурных скобках мы пишем, что наш slug равен post.slug. Слишком много фигурных скобок, да, но вы к этому привыкните. Теперь вы можете обновить страницу и кликнуть на любую из ссылок. Если вы всё сделали правильно, вы увидите тело статьи.

Ну что же, мы сделали почти всё, чтобы написать своё первое CRUD приложение. Осталось познакомиться с формами, что мы и сделаем на следующем уроке.

loader
18.01.2022 в 22:18
7369
+404
Логические задачи с собеседований