2016-07-29 36 views
0

我在项目的不同位置使用了服务。在我的控制器中它完美地工作。如何让服务在生命周期事件侦听器中工作

我在prePersist生命周期事件侦听器中需要它,但在那里调用服务不想工作。当我尝试它时,我得到以下错误;

试图调用方法 “获得” 上 类 “XX \的xxx \ XxxxBundle \ LIB \ yyyy年\ OrderUserListener”。

我认为我明白我必须在我的事件监听器中注入我的服务,但我不明白该怎么做。

这就是OrderUserListenerOrderLogger服务分别在service.yml文件中的样子;

bss.pmod.current_user_id: 
    class: Xx\Xxx\XxxxBundle\Lib\Yyyy\OrderUserListener 
    calls: 
    - [ setServiceContainer, [@service_container] ] 
    tags: 
     - { name: doctrine.event_listener, event: prePersist } 

bss.pmod.order_logger: 
    class: Xx\Xxx\XxxxBundle\Lib\Yyyy\OrderLogger 
    arguments: [ "@doctrine.orm.entity_manager", "@security.token_storage" ] 

这是我的OrderLogger函数,我想在我的服务中注入;

class OrderLogger { 

    private $em; 
    private $tokenStorage; 

    /** 
    * Constructor. 
    * 
    * @param EntityManager $em 
    * @param TokenStorage $securityTokenStorage 
    */ 
    public function __construct(EntityManager $em, TokenStorage $securityTokenStorage) 
    { 
     $this->em = $em; 
     $this->tokenStorage = $securityTokenStorage; 
    } 

    /** 
    * Log an order action. 
    * 
    * @param string $text 
    */ 
    public function log($order, $action) 
    { 
     $logRecord = new PmodLog(); 
     if (is_object($this->tokenStorage->getToken())) { 
      $user = $this->tokenStorage->getToken()->getUser(); 
      if (is_object($user)) { 
       $logRecord->setUser($user); 
      } 
     } 
     $logRecord->setOrder($order); 
     $logRecord->setAction($action); 
     $logRecord->setTime(new \DateTime()); 

     $this->em->persist($logRecord); 
     $this->em->flush(); 
    } 

} 

我的事件监听器看起来像这样;

class OrderUserListener 
{ 

    /** 
    * Service container 
    * @var type 
    */ 
    private $serviceContainer; 

    /** 
    * Performs tasks before destruction 
    * @ORM\PrePersist 
    */ 
    public function prePersist(LifecycleEventArgs $args) 
    { 
     $order = $args->getEntity(); 

     if ($order instanceof PmodOrder) { 
      $user = $this->serviceContainer->get('security.token_storage')->getToken()->getUser(); 

      if ($user) { 
       $order->setCreatedBy($user); 
       $order->setCreatedAt(new \DateTime(date('Y-m-d H:i:s'))); 
       $order->setDepartment($user->getDepartment()); 
       $order->setStatus(PmodOrder::STATUS_AWAITING_APPROVAL); 

       //$this->get('bss.pmod.order_logger')->log($order, 'Order Created');  // This is then clearly wrong. 
      } 
     } 
    } 

    /** 
    * Sets the sales order exporter object 
    * @param type $serviceContainer 
    */ 
    public function setServiceContainer($serviceContainer) 
    { 
     $this->serviceContainer = $serviceContainer; 
    } 
} 

如果有人可以向我解释如何用我的示例做到这一点,我会非常感激它。

+0

您可以提供您的'OrderUserListener'定义吗?就像你对'OrderLogger'所做的那样' –

+0

你是指'OrderUserListener'的service.yml文件中的服务定义? – Mentos93

+0

是的,你似乎需要将'bss.pmod.order_logger'作为参数添加到它。 –

回答

1

首先,看起来像你混合ContainerAwareInterface使用的Symfony的Controller基类(现赞成不赞成ContainerAwareTrait)。 ContainerAwareInterface旨在区分需要注入服务容器的类,并且控制器会自动注入它。由于看起来这条线是有效的:

$user = $this->serviceContainer->get('security.token_storage')->getToken()->getUser(); 

,看起来容器被正确注入。

您尝试从服务中调用的get()方法实际上让我想起Controller's get() method。但是你的班级不是Controller的后裔,而且我也不能说它实际上是一个控制者。

你应该做的,而不是调用get()这里什么是调用容器:

$logger = $this->serviceContainer->get('bss.pmod.order_logger')->log($order, 'Order Created'); 

然而,ContainerAware服务通常被认为是不好的做法。您可以改为直接通过构造函数注入服务:

# Service definition 
bss.pmod.current_user_id: 
    class: Xx\Xxx\XxxxBundle\Lib\Yyyy\OrderUserListener 
    arguments: 
     - "@security.token_storage" 
     - "@bss.pmod.order_logger" 
    tags: 
     - { name: doctrine.event_listener, event: prePersist } 

class OrderUserListener 
{ 
    private $tokenStorage; 

    private $logger; 

    public function __construct($tokenStorage, $logger) 
    { 
     $this->tokenStorage = $tokenStorage; 
     $this->logger = $logger; 
    } 

    // ... 

    public function prePersist(LifecycleEventArgs $args) 
    { 
     // ... 

     // Here you can call the injected services: 
     $user = $this->tokenStorage->getToken()->getUser(); 

     $this->logger->log($order, 'Created'); 
    } 
} 
+0

这是正确的轨道,但会导致循环依赖性错误。教义实体管理器服务实际上依赖于它的实体监听器。在你的例子中,实体监听器依次依赖于自身依赖于实体管理器的记录器。繁荣!注射容器是解决这个问题的一种方法。更好的方法是从$ args中提取实体管理器并将其传递给记录器。 – Cerad

+0

谢谢你的答案kix,我会试试看。但你是什么意思@Cerad?你能否详细说明一下,帮助我以正确的方式工作?或者是'$ logger = $ this-> serviceContainer-> get('bss.pmod.order_logger') - > log($ order,'Order Created')这行';'kix提到的足够了吗?因为这与下面的投票答案相同,并导致另一个错误。我想我会然后就这个错误工作,并提出一个新的问题... – Mentos93

+0

我会期望downvoted的答案,至少让你通过发布的错误消息。任何时候当你试图从监听器更新数据库($ em-> flush())时,你可能会遇到问题。但看看会发生什么。 – Cerad

-1

变化$this->get('bss.pmod.order_logger')->log($order, 'Order Created');$this->serviceContainer->get('bss.pmod.order_logger')->log($order, 'Order Created');

+0

如果你把-1然后解释​​原因 –

+0

我不是一个投下来的人,但我记得我其实是这样试过的。我得到了另一个错误。我现在无法检查,但我会在星期一早上回到你身边。 – Mentos93