坚持从内核侦听器抛出EntityManager已关闭

时间:2017-11-15 22:46:06

标签: symfony doctrine-orm

每次请求或提供URL时,我都要保留一个Log实体(ip,url,path,user,date ...)。我正在关闭" EntityManager已关闭"错误。 如果可能的话,我正在寻找一个简单的解决方案。 我的代码如下:

首先是YAML     服务:

kernelListener:
    class: AppBundle\EventListener\KernelListener
    arguments: [ '@my_logger']
    tags:
        - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
        - { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }
        - { name: kernel.event_listener, event: kernel.terminate, method: onKernelTerminate }
        - { name: kernel.event_listener, event: kernel.controller, method: onKernelController }

my_logger:
    class:        AppBundle\Service\Logger
    arguments: ['@doctrine.orm.entity_manager', '@security.token_storage' ]
    scope: prototype  

这是我的Logger服务:

 namespace AppBundle\Service;
use AppBundle\Entity\Log;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;

class Logger {

private $em, $token;
public function __construct(EntityManager $em, TokenStorage $token) {
    $this->em = $em;
    $this->token_storage = $token;
}

public function persist($r) {

    $log = new Log();
    $log->setAjax($r->isXmlHttpRequest());
    $log->setIp($r->getClientIp());
    $log->setMethod($r->getMethod());
    $log->setPath($path);
    $log->setUrl($r->getUri());

    if ($this->token_storage->getToken()) {
        $user = $this->token_storage->getToken()->getUser();
        if ($user instanceof \AppBundle\Entity\User) {
            $log->setUser($user);
        }
    }

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

}

和KernelListener.php

 namespace AppBundle\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use AppBundle\Service\Logger;

class KernelListener {

protected $logger;

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

public function onKernelRequest(GetResponseEvent $event) {

}

public function onKernelResponse(FilterResponseEvent $event) {
}

 public function onKernelController(FilterControllerEvent $event) {
    $r = $event->getRequest();
    $this->logger->persist($r);
}

public function onKernelTerminate(PostResponseEvent $event) {        
}
}

如果我删除了flush(),则不会抛出任何错误,但逻辑上没有登录数据库。

2 个答案:

答案 0 :(得分:1)

出现错误 EntityManager已关闭,因为存在一些数据库级异常。因此,实体管理器的逻辑如下:如果存在异常,EntityManager将切换到“已关闭”状态,您将无法再使用它。

如果您想使用EntityManager - 您需要创建一个新的。例如,如https://stackoverflow.com/a/19077050/6699227中所述:

if (!$this->entityManager->isOpen()) {
    $this->entityManager = $this->entityManager->create(
        $this->entityManager->getConnection(),
        $this->entityManager->getConfiguration()
    );
}

您的案例的另一个解决方案可能是create another entity manager,仅用于编写日志。应用程序本身不会使用它,因此它永远不会被关闭。

答案 1 :(得分:0)

Maksym是对的。谢谢!事实上我几个月前用过这种技术来记录服务日志错误,但是当你运行很多任务时内存很弱: - (

所以我的工作记录器是:

    public function persist($r) {
    if (!$this->em->isOpen()) {
        $this->em = $this->em->create(
                $this->em->getConnection(), $this->em->getConfiguration()
        );
    }

    $path = $r->get('_route');
    if (!$path || in_array($path, $this->paths)) {
        return null;
    }

    $log = new Log();
    $log->setAjax($r->isXmlHttpRequest());
    $log->setIp($r->getClientIp());
    $log->setMethod($r->getMethod());
    $log->setPath($path);
    $log->setUrl($r->getUri());

    if ($this->token_storage->getToken()) {
        $user = $this->token_storage->getToken()->getUser();
        if ($user instanceof \AppBundle\Entity\User) {
            $user2 = $this->em->getReference('AppBundle:User', $user->getId());
            $log->setUser($user2);
        }
    }

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