2016-07-29 50 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