Как добраться до сервиса из Entity?

В Symfony фреймворке существует несколько способов получить доступ к сервисам из Entity класса, в зависимости от вашей архитектуры и требований проекта.

Первым способом является использование Dependency Injection (DI) контейнера. В DI контейнере вы можете зарегистрировать ваш сервис и затем использовать его в любом месте вашего приложения, включая Entity классы. Для этого вам понадобится следующие шаги:

1. Зарегистрируйте ваш сервис в файле services.yaml или в конфигурационном файле services.xml (в зависимости от используемого формата). Пример регистрации сервиса в файле services.yaml выглядит следующим образом:

services:
    AppServiceMyService:
        arguments: []

2. В вашем Entity классе создайте конструктор и передайте аргументом сервис контейнер:

use AppServiceMyService;
use SymfonyComponentDependencyInjectionContainerInterface;

class MyEntity
{
    private $myService;

    public function __construct(ContainerInterface $container, MyService $myService)
    {
        $this->myService = $container->get(MyService::class);
        $this->myService = $myService;
    }
}

Обратите внимание, что я включил и ContainerInterface, и MyService в список аргументов конструктора. ContainerInterface является частью Symfony Dependency Injection контейнера, и внедрение его в конструктор позволяет получить доступ к контейнеру.

3. Вам также необходимо обновить вашу конфигурацию Doctrine, чтобы передавать экземпляр сервиса при создании экземпляра Entity. Вам потребуется настроить "фабрику" (factory) в вашем конфигурационном файле config/packages/doctrine.yaml:

doctrine:
    orm:
        mappings:
            App:
                is_bundle: false
                type: annotation
                dir: '%kernel.project_dir%/src/Entity'
                prefix: 'AppEntity'
                alias: App
        entity_factories:
            AppEntityMyEntity:
                AppServiceMyEntityFactory

4. Создайте фабрику класс для вашего Entity в AppServiceMyEntityFactory, где будет создаваться экземпляр вашего Entity с передачей нужного сервиса:

use DoctrineORMEntityManagerInterface;
use SymfonyComponentDependencyInjectionContainerInterface;
use AppServiceMyService;
use AppEntityMyEntity;

class MyEntityFactory
{
    private $container;
    private $entityManager;
    private $myService;

    public function __construct(ContainerInterface $container, EntityManagerInterface $entityManager, MyService $myService)
    {
        $this->container = $container;
        $this->entityManager = $entityManager;
        $this->myService = $myService;
    }

    public function __invoke()
    {
        return new MyEntity($this->container, $this->myService);
    }
}

Теперь, когда вызывается MyEntity с Doctrine API, будет использоваться наша фабрика.

Второй способ использовать сервисы в Entity классе - это использование Resolver. Resolver в Symfony позволяет инжектировать зависимости без явного использования DI контейнера.

Давайте рассмотрим пример:

1. Первым шагом является создание Resolver класса, который позволяет инжектировать нужные сервисы в Entity:

// src/Service/EntityResolver.php
namespace AppService;

use DoctrineORMEntityManagerInterface;
use DoctrinePersistenceObjectRepository;

class EntityResolver
{
    private $entityManager;
    private $repository;

    public function __construct(EntityManagerInterface $entityManager, string $entityClass)
    {
        $this->entityManager = $entityManager;
        $this->repository = $entityManager->getRepository($entityClass);
    }

    public function __get($name)
    {
        return $this->repository->{$name};
    }
}

2. Создайте Resolver сервис и зарегистрируйте его в DI контейнере:

# config/services.yaml
services:
    AppServiceEntityResolver:
        arguments:
            - '@doctrine.orm.entity_manager'
            - 'AppEntityMyEntity'

3. В вашем Entity классе просто добавьте объявление свойств сервисов, которые вы хотите использовать:

namespace AppEntity;

use AppServiceEntityResolver;

class MyEntity
{
    private $entityResolver;

    public function __construct(EntityResolver $entityResolver)
    {
        $this->entityResolver = $entityResolver;
    }

    public function myMethod()
    {
        // Ваш код
        $myService = $this->entityResolver->myService; // Обращаемся к сервису из Resolver
        // Ваш код
    }
}

Теперь вы сможете использовать сервисы в Entity классе, обращаясь к ним через объявленное свойство $this->entityResolver->{serviceName}.

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