Кращі практики фреймворку Symfony

Дата оновлення перекладу 2022-12-06

Кращі практики фреймворку Symfony

Ця стаття описує кращі практики для розробки веб-додатків з Symfony, які відповідають філософії, що була розроблена творцями Symfony.

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

Ця стаття передбачає, що ви вже маєете досвід у розробці додатків Symfony. Якщо це не так, прочитайте спочатку розділ документації Початок роботи.

Tip

Symfony надає пробний застосунок під назвою Symfony Demo, який слідує усім цим кращим практикам, тому ви можете випробувати все наочно.

Створення проекту

Використовуйте бінарність Symfony для створення додатків Symfony

Бінарність Symfony - це здійсненна команда, створена на вашій машині, коли ви Завантажити Symfony. Вона надає безліч утиліт, включно з найпростішим способом для створення нових додатків Symfony:

1
$ symfony new my_project_directory

За лаштунками, ця команда бінарності 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.

Використовуйте параметри для конфігурації додатку

Це опції, що використовуються для зміни поведінки додатку, такої як повідомлень про відправку листів або включення feature toggles. Їх значення не змінюється в залежності від машини, тому не визначайте їх як змінні середовища.

Визначайте ці опції як параметри в файлі 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-константи у пов'язаних класах. Наприклад:

1
2
3
4
5
6
7
8
9
// 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

Сутності Doctrine - це прості PHP-об'єкти, які ви зберігаєте в деякій "базі даних". Doctrine знає про ваші сутності лише через відображення метаданих, сконфігурованих для ваших класів моделей.

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

Якщо ваша PHP-версія ще не підтримує атрибути, використовуйте анотації, що схоже за своєю суттю, але потребує встановлення деяких додаткових залежностей у вашому проекті.

Контролери

Зробіть так, щоб ваш контролер розширював базовий контролер AbstractController

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

Розширення ваших контролерів з цього базового контролеру пов'язує ваш застосунок з Symfony. Пов'язаність зазвичай не схвалюється, але в даному випадку може бути корисною, так як контролери не мають містити ніякої бізнес-логіки. Контролери мають містити лише декілька рядків сполучного коду, щоб ви не пов'язували важливі частини вашого додатку.

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

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

Використовуйте впровадження залежності для отримання сервісів

Якщо ви розширите базовий AbstractController, ви зможете отримати доступ лише до найбільш розповсюджених сервісів (наприклад, twig, router, doctrine, і т.д.), прямо з контейнеру через $this->container->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-установки. На даний момент, авто-хешування за замочуванням - bcrypt.

Використовуйте виборців для реалізації точних обмежень безпеки

Якщо ваша логіка безпеки складна, вам варто створити користувацьких виборців безпеки замість визначення довгих виразів всередині атрибуту #[Security].

Веб-ресурси

Використовуйте Webpack Encore для обробки веб-ресурсів

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

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

Тести

Тестуйте працездатність ваших URL

В програмуванні, smoke testing складається з "попереднього тестування для виявлення маленьких помилок, достатніх для відміни релізу перспективного ПЗ". Використовуючи постачальників даних PHPUnit, ви можете визначити функціональний тест, який перевірить, щоб усі URL додатку завантажувались успішно:

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
// 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 в тестах замість їхнього створення з маршрутів. Кожен раз при зміні маршруту, тести будуть видавати помилку, і ви знатимете, що вам необхідно налаштувати перенаправлення.