Циркулярная ссылка при вводе контекста безопасности в (слушатель субъекта) класса
Здесь было 2 вопроса, которые должны были решить проблему инъекции всего контейнера. Но вопрос... см. Ниже (разница примечаний между попытками 2 и 3)...
Попробуйте 1
public function __construct(SecurityContext $securityContext) {
$this->securityContext = $securityContext);
}
Справочник по куркулам. Хорошо...
Попробуйте 2
public function __construct(ContainerInterface $container) {
$this->securityContext = $container->get('security.context');
}
Циркулярная ссылка ( Почему?, я вставляю контейнер, как в try 3, за исключением того, что я получил только контекст безопасности)
Попробуйте 3
public function __construct(ContainerInterface $container) {
$this->container = $container;
}
Works.
Ответы
Ответ 1
Это происходит потому, что ваш контекст безопасности зависит от этого слушателя, возможно, через диспетчер объектов, который вводится в провайдер пользователя. Лучшим решением является инжекция контейнера в слушатель и ленивый доступ к контексту безопасности.
Мне обычно не нравится вводить весь контейнер в службу, но делайте исключение с слушателями Doctrine, потому что они с нетерпением загружены и поэтому должны быть как можно более ленивыми.
Ответ 2
Начиная с Symfony 2.6 эта проблема должна быть исправлена. Запрос на растяжение только что был принят в мастер. Ваша проблема описана здесь.
https://github.com/symfony/symfony/pull/11690
Как и в Symfony 2.6, вы можете ввести security.token_storage
в ваш слушатель. Эта служба будет содержать токен, используемый SecurityContext
в <= 2.5. В 3.0 эта служба заменит 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());
}
}
Ответ 3
Причина "2" терпит неудачу, а "3" - нет, потому что в варианте 2 вы пытаетесь получить доступ к контексту безопасности непосредственно из контейнера, когда он, вероятно, еще не заполнен.
Насколько я могу судить, Symfony2 анализирует конфигурацию и создает службу один за другим, а затем переходит к обработке остальной части запроса.
Это означает, что вы не можете получить доступ к различным частям контейнера, потому что это может быть загрузка их в другом порядке. Таким образом, у вас есть указатель на память в контейнере, и сохраните его, но затем позвольте структуре закончить создание полного контейнера, прежде чем пытаться получить доступ к его частям. Заметным исключением является то, что вы непосредственно вводите услугу в другую услугу, после чего контейнер уверен, что она сначала загрузила этот сервис.
Вы можете увидеть последствия этого, выполнив две службы. A и B. A передается B, а B передается A. Теперь у вас есть круговая ссылка. Если вы вместо этого передали контейнер в и B, вы не смогли бы получить доступ к A из B и B из A без проблем.
Ответ 4
Вы всегда должны стараться избегать инъекции контейнера непосредственно в свои службы.
Я думаю, что наилучшим решением проблемы "круговой ссылки", а также возможными проблемами производительности будет использование "" Lazy Services "", Начиная с Symfony 2.3.
Просто укажите свою зависимость как lazy
в конфигурации вашего сервисного контейнера и установите мост ProxyManager (см. подробности в документации Lazy Services выше).
Я надеюсь, что это поможет, приветствует.