Организация вашей бизнес-логики

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

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

Для большинства проектов вам стоит хранить весь ваш код в каталоге src/. Там, вы можете создать любые желаемые вами каталоги для систематизации вещей:

1
2
3
4
5
6
7
8
9
symfony-project/
├─ config/
├─ public/
├─ src/
│  └─ Utils/
│     └─ MyClass.php
├─ tests/
├─ var/
└─ vendor/

Сервисы: Именование и конфигурация

Best Practice

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

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

Приложению блога необходима утилита, которая может преобразовать заголовок поста (например, "Привет, мир") в заготовку (slug) (наример, "hello-world"), чтобы включить его в качестве части URL поста. Давайте создадим новый класс Slugger внутри src/Utils/:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// src/Utils/Slugger.php
namespace App\Utils;

class Slugger
{
    public function slugify(string $value): string
    {
        // ...
    }
}

Если вы используете конфигурацию services.yaml по умолчанию, то этот класс автоматически регистрируется, как сервис, ID которого App\Utils\Slugger (или просто Slugger::class, если класс уже импортирован в ваш код).

Best Practice

Id сервисов вашего приложения должны совпадать с их именем класса, кроме сдучаев, когда у вас сконфигурировано несколько сервисов для одного класса (в этом случае, используйте id "змеиной кожи".

Теперь вы можете использовать пользовательский слаггер в любом другом сервисе или классе контроллера, вроде AdminController:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use App\Utils\Slugger;

public function create(Request $request, Slugger $slugger)
{
    // ...

    if ($form->isSubmitted() && $form->isValid()) {
        $slug = $slugger->slugify($post->getTitle());
        $post->setSlug($slug);

        // ...
    }
}

Сервисы также могут быть публичными или приватными. Если вы используете конфигурацию services.yaml по умолчанию, то все сервисы приватные по умолчанию.

Best Practice

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

Формат сервиса: YAML

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

Best Practice

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

Это спорно, и по нашему опыту, использование YAML и XML распределяется среди разработчиков одинаково, с небольшим преимуществом в пользу YAML. Оба формата имеют одинаковую производительность, так что обычно это вопрос личного предпочтения.

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

Использование уровня хранения

Symfony - это HTTP-фреймворк, который занят только генерированием HTTP-ответа для каждого HTTP-запроса. Поэтому Symfony не предоставляет способ общения с уровнем хранения (например, БД, внешним API). Вы можете выбрать любую билиотеку или стратегию, которую хотите.

На практике, многие приложения Symfony полагаются на независимый проект Doctrine, чтобы определять их модель, используя сущности и хранилища. Так же, как и с бизнес- логикой, мы рекомендуем хранить сущности Doctrine в каталоге src/Entity/.

Три сущности, определённые нашим пробным приложением блога, являются хорошим примером:

1
2
3
4
5
6
7
symfony-project/
├─ ...
└─ src/
   └─ Entity/
      ├─ Comment.php
      ├─ Post.php
      └─ User.php

Информация маршрутизации Doctrine

Сущности Doctrine являются простыми PHP-объектами, который вы храните в некоторой "БД". Doctrine знает о ваших сущностях только через метаданные маршрутизации, сконфигурированные для ваших классов моделей. Doctrine поддерживает четыре формата метаданных: YAML, XML, PHP и аннотации.

Best Practice

Используйте аннотации, чтобы определить информацию маршрутизации сущностей Doctrine.

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

 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity
 */
class Post
{
    const NUMBER_OF_ITEMS = 10;

    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string")
     */
    private $title;

    /**
     * @ORM\Column(type="string")
     */
    private $slug;

    /**
     * @ORM\Column(type="text")
     */
    private $content;

    /**
     * @ORM\Column(type="string")
     */
    private $authorEmail;

    /**
     * @ORM\Column(type="datetime")
     */
    private $publishedAt;

    /**
     * @ORM\OneToMany(
     *      targetEntity="Comment",
     *      mappedBy="post",
     *      orphanRemoval=true
     * )
     * @ORM\OrderBy({"publishedAt"="ASC"})
     */
    private $comments;

    public function __construct()
    {
        $this->publishedAt = new \DateTime();
        $this->comments = new ArrayCollection();
    }

    // геттеры и сеттеры ...
}

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

Фиксаторы данных

Так как поддержка фиксаторов не включена в Symfony по умолчанию, вам нужно выполнить следующую команду, чтобы установить пакет фиксаторов Doctrine:

1
$ composer require "doctrine/doctrine-fixtures-bundle"

Далее этот пакет включается автоматически, но только для окружений dev и test:

1
2
3
4
5
6
// config/bundles.php

return [
    // ...
    Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
];

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

Предполагая, что у вас есть как минимум один класс фиксторов, и что доступ к БД сконфигурирован правильно, вы можете загрузить ваши фиксаторы, выполнив следующую команду:

1
2
3
4
5
$ php bin/console doctrine:fixtures:load

Осторожно, БД будет очищена. Вы хотите продолжить Да/Нет? Да
  > очистка БД
  > загрузка App\DataFixtures\ORM\LoadFixtures

Стандарты кодировки

Исходный код Symfony следует стандартам кодировки PSR-1 и PSR-2, которые были определены PHP-сообществом. Вы можете узнать больше о стандартах кодировки Symfony и даже использовать PHP-CS-Fixer - утилиту комнадной строки, которая может исправлять стандарты кодировки целой кодовой базы за считанные секунды.


Далее: Controllers

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