Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Сервисы и внедрение зависимостей

Сервис — объект, который выполняет полезную работу: отправляет письма, пишет в журнал, считает цены. Все сервисы живут в контейнере, и Symfony сам передаёт их туда, где они запрошены по типу. Так устроен весь фреймворк: EntityManagerInterface и репозитории из прошлых уроков — тоже сервисы.

Свой сервис

Любой класс в папке src уже сервис — регистрировать его не нужно:

// src/Service/PriceCalculator.php
namespace App\Service;
 
class PriceCalculator
{
    public function withTax(float $price): float
    {
        return round($price * 1.2, 2);
    }
}

Получение сервиса

Достаточно указать тип в аргументах метода контроллера или в конструкторе:

use App\Service\PriceCalculator;
 
#[Route('/cart', name: 'cart')]
public function cart(PriceCalculator $calculator): Response
{
    return new Response('Итого: ' . $calculator->withTax(1000));
}

Зависимости сервиса

Сервис получает другие сервисы через конструктор:

namespace App\Service;
 
use Psr\Log\LoggerInterface;
use Symfony\Component\Mailer\MailerInterface;
 
class OrderNotifier
{
    public function __construct(
        private MailerInterface $mailer,
        private LoggerInterface $logger,
    ) {}
 
    public function notify(Order $order): void
    {
        $this->logger->info('Заказ оформлен', ['id' => $order->getId()]);
        // отправка письма
    }
}

Это называют автоматическим связыванием: Symfony смотрит на типы и подставляет объекты.

Какие сервисы есть

php bin/console debug:autowiring
php bin/console debug:autowiring mailer

Команда показывает типы, которые можно запросить.

Настройки и переменные окружения

Строку или число по типу не подставить — их указывают атрибутом #[Autowire]:

use Symfony\Component\DependencyInjection\Attribute\Autowire;
 
class PaymentGateway
{
    public function __construct(
        #[Autowire(env: 'STRIPE_SECRET')]
        private string $secret,
 
        #[Autowire('%kernel.project_dir%/var/invoices')]
        private string $invoiceDir,
    ) {}
}

Переменные окружения записывают в .env, а секретные значения своего компьютера — в .env.local, который не попадает в репозиторий.

Интерфейс и реализация

Если интерфейс реализует один класс, Symfony подставит его сам. Когда реализаций несколько, нужную выбирают в config/services.yaml:

services:
    App\Payment\PaymentGatewayInterface: '@App\Payment\StripeGateway'

Частая ошибка

Сервис создаётся один раз и используется повторно. Если хранить в его свойствах данные конкретного запроса — текущего пользователя, накопленный список, — они «утекут» в следующий вызов. Сервис должен хранить только зависимости и настройки, а данные получать аргументами методов.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.