Циркулярная ссылка при внедрении контекста безопасности в класс (Entity Listener) - PullRequest
12 голосов
/ 03 января 2012

Было 2 вопроса о том, что инъекция всего сервисного контейнера должна решить эту проблему.Но вопрос ... см. Ниже (обратите внимание на разницу между попыткой 2 и 3) ...

Попробуйте 1

public function __construct(SecurityContext $securityContext) {
    $this->securityContext = $securityContext);  
}  

Curcular Reference. Хорошо ...

Попробуйте 2

public function __construct(ContainerInterface $container) {
    $this->securityContext = $container->get('security.context');  
}  

Циркулярная ссылка ( Почему? , я впрыскиваю контейнеркак в попытке 3, за исключением того, что я получил только контекст безопасности)

Попробуйте 3

public function __construct(ContainerInterface $container) {
    $this->container = $container;  
}  

Работает.

Ответы [ 4 ]

24 голосов
/ 03 января 2012

Это происходит потому, что ваш контекст безопасности зависит от этого слушателя, возможно, через менеджера сущностей, внедряемого в провайдера пользователя. Лучшее решение - внедрить контейнер в слушатель и лениво получить доступ к контексту безопасности.

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

8 голосов
/ 24 сентября 2014

Начиная с Symfony 2.6 эта проблема должна быть исправлена. Запрос на извлечение только что принят в мастер. Ваша проблема описана здесь. https://github.com/symfony/symfony/pull/11690

Начиная с Symfony 2.6, вы можете добавить security.token_storage в ваш слушатель. Этот сервис будет содержать токен, используемый SecurityContext в <= 2.5. В 3.0 этот сервис заменит <code>SecurityContext::getToken() в целом. Вы можете увидеть список основных изменений здесь: http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements#deprecated-the-security-context-service

Пример использования в 2.6:

Ваша конфигурация:

services:
    my.listener:
        class: EntityListener
        arguments:
            - "@security.token_storage"
        tags:
            - { name: doctrine.event_listener, event: prePersist }


Ваш слушатель

use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class EntityListener
{
    private $token_storage;

    public function __construct(TokenStorageInterface $token_storage)
    {
        $this->token_storage = $token_storage;
    }

    public function prePersist(LifeCycleEventArgs $args)
    {
        $entity = $args->getEntity();
        $entity->setCreatedBy($this->token_storage->getToken()->getUsername());
    }
}
1 голос
/ 18 марта 2015

Вы всегда должны стараться избегать внедрения контейнера непосредственно в ваши сервисы.

Я думаю, что наилучшее возможное решение проблемы «круговой ссылки», а также возможных проблем с производительностью, было бы использовать « Lazy Services »функция доступна начиная с Symfony 2.3.

Просто пометьте свою зависимость как lazy в конфигурации своего контейнера служб и установите ProxyManager Bridge (подробности см. В документации Lazy Services выше).

Надеюсь, это поможет, ура.

1 голос
/ 20 сентября 2012

Причина, по которой «2» завершается ошибкой, а «3» - нет, заключается в том, что в варианте 2 вы пытаетесь получить доступ к контексту безопасности непосредственно из контейнера, когда он, вероятно, еще не заполнен.

Как я могу сказать, Symfony2 анализирует конфигурацию и создает службу один за другим, а затем переходит к обработке остальной части запроса.

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

Вы можете увидеть эффект от этого, сделав две услуги.A и B. A передается B, а B передается A. Теперь у вас есть циклическая ссылка.Если вы вместо этого передали контейнер в A и B, вы не могли бы получить доступ к A из B и B из A без проблем.

...