Пишем первый контроллер на Symfony и работаем с шаблонами

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

Контроллер можно создать вручную, а можно воспользоваться компонентом maker для генерирования контроллера через терминал. Для этого выполните следующую команду в корне проекта (прим.: все команды надо выполнять в корне проекта):

php bin/console make:controller PostsController

Когда вы выполните эту команду, в папке Controller у вас появится класс PostsController.php с одним единственным методом (action) index. Также в папке templates появится папка posts с шаблоном index.html.twig. Использование команд удобно для генерирования простых контроллеров, сущностей, связей между ними, запуска миграций и многого другого.

Когда вы откроете файл PostsController.php, вы увидите, что он наследуется от AbstractController.php. Это базовый контроллер, дающий нам множество полезных методов вроде рендеринга шаблона, редиректа, создания форм и т.д.

В целом наш класс выглядит следующим образом:

<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class PostsController extends AbstractController
{
 /**
 * @Route("/posts", name="posts")
 */
 public function index()
 {
     return $this->render('post/index.html.twig', [
         'controller_name' => 'PostsController',
     ]);
 }
}

Что здесь необычного? Например, аннотация над методом index(). Дело в том, что именно так (чаще всего) настраиваются маршруты в Symfony. Route первым аргументом принимает маршрут, он может быть вида /posts, /posts/{id}, где id  - плейсхолдер, то есть принимает различные значения. Второй аргумент  -  это название маршрута. Раньше в ссылках вы привыкли писать полный путь до файла-обработчика, в Symfony вы используете имя маршрута, что обеспечивает код гибкостью, так как независимо от того, где будет находиться файл-обработчик, ссылка всегда будет указывать на него правильно.

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

$posts = [
     'post_1' => [
     'title' => 'Заголовок первого поста',
     'body' => 'Тело первого поста'
    ],
     'post_2' => [
     'title' => 'Заголовок второго поста',
     'body' => 'Тело второго поста'
    ]
  ];
   return $this->render('posts/index.html.twig', [
     'posts' => $posts,
 ]);

Передавая данные через шаблон, вы должны помнить, что именно 'posts', а не $posts, шаблонизатор Twig будете видеть в качестве переменной. Таким образом, вы должны давать осмысленное имя и во множественном числе, если работаете с массивом данных.

Перейдите в шаблон templates/posts/index.html.twig. Вы увидите там несложную вёрстку вперемешку со стилями. Удалите всё, что между тегами {% block body %} и {% endblock %}, чтобы ваш файл выглядел так:

{% extends 'base.html.twig' %}
{% block title %} {% endblock %}
{% block body %}
{% endblock %}

Шаблонизатор Twig предлагает всего два типа тегов: {% %} и {{ }}. Первый тег означает, что мы что-то делаем, второй  -  что-то выводим. Когда мы получаем массив, обычно мы проходимся по нему foreach, чтобы вывести его в понятной форме. В twig нет foreach, но есть цикл for. Чтобы вывести название каждого поста, пройдёмся циклом for так, как это можно делать в twig:

{% for post in posts %}
 {{ post.title }}
{% endfor %}

Вы можете обрамлять твиговские теги тегами простого html, например:

<p>{{ post.title }} </p>

Цикл twig выглядит иначе: если в обычном php мы из множественного числа превращаем в единственное (foreach $posts as $post), то тут мы как бы говорим, что для каждого поста в массиве постов (for post in posts) нужно сделать следующее  - {{ post.title }}. Через точку мы получаем доступ к ключу ассоциативного массива.

Также в начале файла вы могли заметить следующую строку:

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

А вот и главная особенность шаблонизаторов: они позволяют наследоваться друг от друга! Другими словами, в шаблоне base.html.twig вы можете определить нужные стили и javascript скрипты, а также блоки, которые унаследуют все остальные шаблоны. Это удобно, ведь позволяет не писать в каждом шаблоне все ненужные html теги, а сосредоточиться только на выводе информации. В base.html.twig вы могли увидеть блоки {% block title %}, {% block body %} и другие блоки. Глубже с twig мы познакомимся несколько позже, а сейчас вам достаточно знать, что блоки можно переопределять, но писать код нужно только внутри них.

Итак, если вы всё сделали правильно, можете перейти по маршруту /posts и увидеть, что названия двух наших постов вывелись. Таким образом, вы увидели, как работает основной жизненный цикл Symfony: пользователь переходит по конкретному маршруту, который обрабатывает определённый action, он же и отдаёт шаблон с данными для пользователя.

В следующем уроке мы глубже познакомимся с работой контроллеров, создадим первую сущность, наполним базу фейковыми данными, выведем список постов и сделаем красивые ЧПУ по slug!

loader
Логические задачи с собеседований