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

Лучшие практики фреймфорка Symfony

Этв статья описывает лучшие практики для разработки веб-еприложений с Symfony, которые соответствуют философии, разработанной создателями Symfony.

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

Эта статья предполагает, что вы уже имеете опыт в разработке приложений Symfony. Если это не так, прочтите для начала раздел документации Начало работы.

Tip

Symfony предоставляет пробное приложение под названием Symfony Demo, которое следует всем этим лучшим практикам, так что вы можете испытать все наглядно.

Создание проекта

Используйте бинарность Symfony для создания приложений Symfony

Бинарность Symfony - это выполнимая команда, созданная на вашей машине, когда вы `скачиваете Symfony`_. Она предоставляет множество утилит, включая самый простой способ для создания новых приложений Symfony:

1
$ symfony new my_project_name

За кдром, эта команда бинарности Symfony выполняет необходимую команду Composer, чтобы создать новое приложение Symfony, основываясь на текущей стабильной версии.

Используйте структуру каталогов по умолчанию

Если ваш проект не следует практикам разработки, которые вынуждают к определенной структуре каталогрв, следуйие структуре каталогов Symfony по умолчанию. Она прямая, простая и не замыкается на Symfony:

 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
your_project/
├─ assets/
├─ bin/
│  └─ console
├─ config/
│  ├─ packages/
│  └─ services.yaml
├─ migrations/
├─ public/
│  ├─ build/
│  └─ index.php
├─ src/
│  ├─ Kernel.php
│  ├─ Command/
│  ├─ Controller/
│  ├─ DataFixtures/
│  ├─ Entity/
│  ├─ EventSubscriber/
│  ├─ Form/
│  ├─ Repository/
│  ├─ Security/
│  └─ Twig/
├─ templates/
├─ tests/
├─ translations/
├─ var/
│  ├─ cache/
│  └─ log/
└─ vendor/

Конфигурация

Используйте переменные окружения для конфигурации инфраструктуры

Значения эти опций отличаются от машины к машине (например, от вашей машины разработки к серверу производста), но они не изменяют поведение приложения.

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

Используйте секрет для конфиденциальной информации

Если в вашем приложении есть конфиденциальая конфигурация - вроде API-ключей - вам стоит хранить ее безопасно используя систему управления секретами Symfony.

Используйте параметры для конфигурации приложения

Это опции, используемые для изменения поведения приложения, такого как уведомлений об отправке писем или включение `кнопок-переключателей функций`_. Их значение не меняется в зависимости от машины, так что не определяйте их как переменные окружения.

Определяйте эти опции как параметры в файле config/services.yaml. Вы можете переопределить эти опции для каждого окружения в файлах config/services_dev.yaml и config/services_prod.yaml.

Используйте краткость и префиксы в названиях параметров

Рассмотрите использование app. в качестве префикса ваших парамеров, чтобы избежать коллизий с параметрами Symfony и сторонних пакетов/библиотек. Затем, используйте только одно или два слова для описания цели параметра:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# config/services.yaml
parameters:
    # не делайте так: 'dir' слишком общий и не несет никакого смысла
    app.dir: '...'
    # делайте так: краткие, но простые в понимании названия
    app.contents_dir: '...'
    # можно использовать точки, нижние подчеркивания. слеши или ничего, но всегда
    # будьте последовательны и используйте одинаковый формат для всех параметров
    app.dir.contents: '...'
    app.contents-dir: '...'

Используйте константы для определния опций, которые редко изменяются

Опции конфигурации как количество объектов для отображения в некотором списке редко изменяются, Вместо того, чтобы определять их как параметры сервис-контейнера, определите их как PHP-константы в связанных классах. Пример:

// src/Entity/Post.php
namespace App\Entity;

class Post
{
    public const NUMBER_OF_ITEMS = 10;

    // ...
}

Главное преимущество констант в том, что вы можете использовать их везде, включая шаблоны Twig и сущности Doctrine, в то время как параметры доступны только из мест с доступом к сервис-контейнеру.

Единственным заметным недостатком использования констант для таких значений конфигурации является сложное переопределение их значений в ваших тестах.

Бизнес-логика

Не создавайте пакетов для организации логики вашего приложения

Когда была выпущена Symfony 2.0, приложения использовали пакеты для разделения своего кода на логические функции: UserBundle, ProductBundle, InvoiceBundle, и т.д. Однако, пакет должен быть чем-то, что можно использовать повторно в качестве отдельного ПО.

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

Используйте автомонтирование для автоматизации конфигурации сервисов приложения

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

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

Сервисы должны быть приватными всегда, когда это возможно

Делайте сервисы приватными, чтобы предотвратить доступ к ним через $container->get(). Вместо этого вам нужно будет использовать соответствующее внедрение зависимости.

Используйте формат YAML для конфигурации собственных сервисов

Если вы используете конфигурацию services.yaml по умолчанию, большинство сервисов будут сконфигурированы автоматически. Однако, в некоторых случаях, вам нужно будет конфигурировать сервисы (или их части) вручную.

YAML - это рекомендуемый формат для конфигурации сервисов, так как он дружелюбен по отношению к новичкам и емкий, но Symfony также поддерживают конфигурацию XML и PHP.

Используйте аннотации для определения отображения сущностей Doctrine

Cущности Doctrine - это простые PHP-объекты, которые вы храните в некоторой “базе данных”. Doctrine знает о ваших сущностях только через отображение метаданных, сконфигурированных для ваших классов моделей.

Doctrine поддерживает несколько форматов метаданных, но рекомендуется использовать аннотации, так как они наиболее удобные и быстрые в настройке и поиске информации отображения.

Контроллеры

Сделайте так, чтобы ваш контроллер расширял базовый контроллер AbstractController

Symfony предоставляет a базовый контроллер, который включает в себя сокрашения для наиболее распространенных нужд вроде отображения шаблонов или проверки разрешений безопасности.

Расширение ваших контроллеров из этого базового контроллера cвязует ваше приложение с Symfony. Cвязанность обычно не поощряется, но в этом случае может быть хороша, так как контроллеры не должны содержать никакой бизнес-логики. Контроллеры должны содержать только несколько строк связующего кода, чтобы вы не связывали важные части вашего приложения.

Используйте атрибуты или аннотации для конфигурации машрутизации, кеширования и безопасности

Использование атрибутов или аннотаций для маршрутизации, кеширования и безопасности упрощает конфигурацию. Вам не нужно искать несколько файлов, созданных в разных форматах (YAML, XML, PHP): вся конфигурация будет именно там, где вам нужно, и использовать только один формат.

Не используйте аннотации для конфигурации шаблона контроллера

Аннотация @Template полезна, но также включает в себя некоторую магию. Более того, в большинстве случаев @Template используется без параметров, что затрудняет понимание того, какой шаблон отображается. Она также скрывает тот факт, что контроллер должен всегда возвращать объект Response.

Используйте внедрение зависимости для получения сервисов

Если вы расширите базовый AbstractController, вы сможете получить доступ только к наиболее распространенным сервисам (например, twig, router, doctrine, и т.д.), прямо из контейнера через $this->container->get() или $this->get(). Вместо этого, вы должны использовать внедрение зависимости для получения сервисов путем подсказок аргументов методов действия или аргументов конструктора.

Используйте ParamConverters, если они удобны

Если вы используете Doctrine, то вы можете по желанию использовать ParamConverter для автоматического запроса сущности и передачи ее в качестве аргумента вашему контроллеру. Он также будет отображать страницу 404, если сущность не может быть найдена.

Если логика получения сущности из переменной маршрута более сложная, вместо конфигурации ParamConverter, лучше сделать так, чтобы Doctrine делала запрос внутри контроллера (например, путем вызова метода хранилища Doctrine).

Шаблоны

Используйте змеиный регистр для названий и переменных шаблонов

Используйте нижнестрочный snake_case для названий, каталогов и переменных шаблонов (например, user_profile вместо userProfile и product/edit_form.html.twig вместо Product/EditForm.html.twig).

Добавляйте к фрагментам шаблонов префикс нижнего подчеркивания

Фрагменты шаблонов, также называемые “частичными шаблонами”, позволяют повторно использовать содержание шаблонов. Добавьте к их названием префикс нижнего подчеркивания для лучшей дифференциации их от полных шаблонов (например, _user_metadata.html.twig или _caution_message.html.twig).

Формы

Определяйте свои формы как PHP-классы

Создание форм в классах позволяет повторно использовать их в разных частях приложения. Кроме того, создание форм не в контроллерах, упрощает код и содержание контроллеров.

Добавляйте кнопки форм в шаблонах

Классы формы должны быть независимы от того, где они будут использованы. Например, кнопка формы, используемая для создания и редактирования объектов, должна меняться с “Добавить” на “Сохранить изменения” в зависимости от того, где она используется.

Вместо добавления кнопок в классах форм или контроллерах, рекомендуется добавлять кнопки в шаблонах. Это также улучшает разделение тем, так как стиль кнопок (CSS-класс и другие атрибуты) определяется в шаблоне, а не в PHP-классе.

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

Определяйте ограничения валидации в подлежащем объекте

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

Используйте одно действие для отображения и обработки формы

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

Интернационализация

Используйте формат XLIFF для ваших файлов перевода

Из всех форматов перевода, поддерживаемых Symfony (PHP, Qt, .po, .mo, JSON, CSV, INI, и т.д.), XLIFF и gettext имеют лучшую поддержку в инструментах, используемых профессиональными переводчиками. И так как он основывается на XML, вы можете валидировать содержание файла XLIFF при написании.

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

Используйте для переводов ключи вместо строк содержания

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

Ключи должны всегда описать свою цель, а не локацию. Например, если форма имеет поле с ярлыком “Username”, то хорошим ключом будет label.username, а не edit_form.label.username.

Безопасность

Определяйте один файерволл

Разве что у вас нет двух абсолютно разных систем аутентификации и пользователей (например. форма входа в систему на главный сайт и токен-система только для вашего API), рекомендуется иметь только один файерволл, чтобы не усложнять.

Кроме того, вы должны использовать ключ anonymous под вашим файерволлом. Если вы требуете, чтобы пользователи возли в систему в разных разделах вашего сайта, используйте опцию access_control.

Используйте хеширование паролей auto

Авто-хеширование паролей автоматически выбирает лучшие возможные кодирования/хеширования в зависимости от вашей PHP-установки. Начиная с Symfony 5.3, авто-хеширование по умолчанию - bcrypt.

Используйте избирателей для реализации точных ограничений безопасности

Если ваша логика безопасности сложная, вам стоит создать пользовательских избирателей безопасности вместо определения длинных выражений внутри аннотации @Security.

Веб-ресурсы

Используйте Webpack Encore для обработки веб-ресурсов

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

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

Тесты

Тестируйте работоспособность ваших URL

В программировании, smoke testing состоит из “предварительного тестирования для обнаружения маленьких ошибок достаточных для отмены релиза перспективного ПО”. Используя поставшики данных PHPUnit, вы можете определить функциональный тест, который проверит, чтобы все URL приложения загружались успешно:

// tests/ApplicationAvailabilityFunctionalTest.php
namespace App\Tests;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class ApplicationAvailabilityFunctionalTest extends WebTestCase
{
    /**
     * @dataProvider urlProvider
     */
    public function testPageIsSuccessful($url)
    {
        $client = self::createClient();
        $client->request('GET', $url);

        $this->assertResponseIsSuccessful();
    }

    public function urlProvider()
    {
        yield ['/'];
        yield ['/posts'];
        yield ['/post/fixture-post-1'];
        yield ['/blog/category/fixture-category'];
        yield ['/archives'];
        // ...
    }
}

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

Жестко кодируйте URL в функциональном тесте

В приложениях Symfony рекомендуется генерировать URL используя маршруты для автоматического обновления всех ссылок при изменении URL. Однако, если изменяется публичный URL, пользователи не смогут перейти по нему, если вы не настроите перенаправление по новому URL.

Поэтому рекомендует использовать чистые URL в тестах вместо создания их из маршрутов. Каждый раз при изменении маршрута, тесты будут выдавать ошибку и вы будете знать, что вам нужно настроить перенаправление.

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.