В 11-ом уроке мы разберем одну из ключевых концепций Symfony, это сервисы и Dependency Injection (DI). Если вы дошли до этого урока, то уже знакомы с основами Symfony: «routing, контроллеры, шаблоны». Теперь пришло время углубиться в архитектуру приложений и понять, как Symfony управляет зависимостями.
Этот урок будет насыщен практикой, мы создадим кастомные сервисы, настроим автовайринг и разберемся с конфигурацией сервисов. В конце вас ждут задания для закрепления материала.
Что такое сервисный контейнер?
Сервисный контейнер это «мозг» Symfony, который управляет всеми объектами (сервисами) вашего приложения. Представьте его как фабрику: вы говорите, какой сервис нужен, а контейнер создает его, автоматически подставляя все зависимости.
Основные принципы:
- Сервис это любой PHP-объект, выполняющий определенную задачу (например, отправка email или работа с базой данных).
- Dependency Injection (DI) это паттерн, при котором зависимости (другие сервисы) передаются объекту извне, а не создаются внутри него.
- Контейнер хранит инструкции по созданию сервисов и их зависимостей.
Пример без DI:
class NotificationController extends AbstractController { public function send(): Response { // Плохо: зависимость создается внутри класса $emailSender = new EmailSender(); $emailSender->send('Hello!'); return new Response('OK'); } }
Если EmailSender потребует изменения (например другой способ отправки), придется править все места, где он создается.
Пример с DI:
class NotificationController extends AbstractController { public function send(EmailSender $emailSender): Response { // Хорошо: зависимость внедряется извне $emailSender->send('Hello!'); return new Response('OK'); } }
Теперь EmailSender управляется контейнером и его легко заменить или модифицировать.
Создание кастомных сервисов
Давайте создадим свой сервис для работы с уведомлениями.
Шаг 1: Создаем класс сервиса
Создайте файл src/Service/NotificationService.php:
namespace App\Service; class NotificationService { public function sendNotification(string $message): string { // Логика отправки уведомления return "Уведомление отправлено: $message"; } }
Шаг 2: Регистрация сервиса в контейнере
Symfony автоматически регистрирует классы в директории src/ как сервисы благодаря настройкам в config/services.yaml:
services: _defaults: autowire: true autoconfigure: true App\: resource: '../src/' exclude: - '../src/DependencyInjection/' - '../src/Entity/' - '../src/Kernel.php'
Теперь сервис можно внедрить в контроллер:
use App\Service\NotificationService; class NotificationController extends AbstractController { public function send(NotificationService $notificationService): Response { $result = $notificationService->sendNotification('Новое сообщение'); return new Response($result); } }
Автовайринг (Autowiring)
Автовайринг это механизм Symfony, который автоматически определяет зависимости сервиса по типам аргументов.
Пример с зависимостями
Допустим, наш NotificationService требует логгер. Создадим сервис LoggerService:
// src/Service/LoggerService.php namespace App\Service; class LoggerService { public function log(string $message): void { file_put_contents('log.txt', $message . PHP_EOL, FILE_APPEND); } }
Модифицируем NotificationService:
use App\Service\LoggerService; class NotificationService { private LoggerService $logger; public function __construct(LoggerService $logger) { $this->logger = $logger; } public function sendNotification(string $message): string { $this->logger->log("Отправлено: $message"); return "Уведомление отправлено: $message"; } }
Как это работает:
- Symfony видит, что
NotificationServiceтребуетLoggerServiceв конструкторе. - Контейнер находит сервис
LoggerService(он уже зарегистрирован благодаря автовайрингу). LoggerServiceавтоматически передается вNotificationService.
Конфигурация сервисов в services.yaml
Файл config/services.yaml позволяет тонко настраивать сервисы.
Базовая структура:
services: _defaults: autowire: true # Включить автовайринг autoconfigure: true # Автоматически назначать теги App\: resource: '../src/' exclude: - '../src/Entity/'
Ручная регистрация сервиса
Иногда нужно явно указать сервис. Например, для стороннего класса:
services: app.custom_service: class: App\Service\CustomService arguments: - '@app.another_service' # Внедрение зависимости по ID
Аргументы сервиса
Можно передавать параметры:
parameters: app.notification_email: 'admin@example.com' services: App\Service\NotificationService: arguments: $email: '%app.notification_email%'
Изменим конструктор NotificationService:
class NotificationService { private string $email; public function __construct(LoggerService $logger, string $email) { $this->logger = $logger; $this->email = $email; } }
Практические задачи
Задача 1: Создание сервиса для генерации случайных чисел
- Создайте класс
RandomNumberGeneratorвsrc/Service/. - Добавьте метод
generate(int $min, int $max): int. - Внедрите сервис в контроллер и выведите случайное число.
Решение:
// src/Service/RandomNumberGenerator.php namespace App\Service; class RandomNumberGenerator { public function generate(int $min, int $max): int { return rand($min, $max); } }
// В контроллере public function number(RandomNumberGenerator $generator): Response { $number = $generator->generate(1, 100); return new Response("Случайное число: $number"); }
Задача 2: Настройка сервиса с параметрами
- Объявите параметр
app.default_minиapp.default_maxвservices.yaml. - Передайте их в
RandomNumberGenerator. - Используйте значения по умолчанию, если аргументы не переданы.
Решение:
parameters: app.default_min: 1 app.default_max: 100 services: App\Service\RandomNumberGenerator: arguments: $min: '%app.default_min%' $max: '%app.default_max%'
class RandomNumberGenerator { private int $min; private int $max; public function __construct(int $min, int $max) { $this->min = $min; $this->max = $max; } public function generate(?int $min = null, ?int $max = null): int { $min ??= $this->min; $max ??= $this->max; return rand($min, $max); } }
Задача 3: Тегирование сервисов
Создайте сервис, который автоматически добавляется в цепочку обработки событий.
- Добавьте интерфейс
MessageHandlerInterface. - Зарегистрируйте сервис с тегом
kernel.event_listener.
services: App\EventListener\CustomListener: tags: - { name: kernel.event_listener, event: kernel.request }
Сегодня вы узнали:
- Как работает сервисный контейнер.
- Как создавать и использовать кастомные сервисы.
- Что такое автовайринг и как он упрощает разработку.
- Как конфигурировать сервисы через
services.yaml.
Главный плюс Symfony, это гибкость. Вы можете комбинировать автоматическую и ручную настройку, чтобы создавать мощные приложения без лишнего кода.
Хотите изучить Symfony от А до Я? Полный курс по Symfony для начинающих
Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.


