如何确保加载 Synfony 实体以触发 postLoad 事件?

How to make sure Synfony Entity is loaded to trigger postLoad event?

我是 Symfony 的新手,正在关注 the Jobeet tutorial. I'm trying to inject Container into Entity using service listener and postLoad. The purpose is to use LiipImagineBundle@ORM\PostPersist 中编写缩略图。问题是 Job 实体没有加载到某些路由上,然后 postLoad 没有被触发。因此,@ORM\PostPersist 中使用的容器不可用。

根据教程,有两条路线 PostPersist 运行:

我发现 Job 实体没有加载并且 postLoad 没有在 job/create 上触发。路由 job/update 没问题,注入了容器。我发布了

的一些代码片段

src/Ibw/JobeetBundle/Resources/config/services.yml

services:
    ibw.jobeet.entity.job.container_aware:
        class: Ibw\JobeetBundle\Doctrine\Event\Listener\JobListener
        calls:
            - [setContainer, ["@service_container"]]
        tags:
            - { name: doctrine.event_listener, event: postLoad }

src/Ibw/JobeetBundle/Doctrine/Event/Listener/JobListener.php

<?php
namespace Ibw\JobeetBundle\Doctrine\Event\Listener;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;

class JobListener
{
    /** @var ContainerInterface */
    protected $container;

    /**
    * @param ContainerInterface @container
    */
    public function setContainer(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * @ORM\PostLoad
     */
    public function postLoad(LifecycleEventArgs $eventArgs)
    {
        $entity = $eventArgs->getEntity();
        if (method_exists($entity, 'setContainer')) {
            $entity->setContainer($this->container);
        }
    }
}

src/Ibw/JobeetBundle/Entity/Job.php

// ....
/**
 * @ORM\PostPersist
 */
public function upload()
{
    if (null === $this->getFile()) {
        return;
    }

    if ($this->getFile()->move($this->getUploadTmpRootDir(), $this->logo)) {
        $this->moveAndResizeImage();
    }

    $this->file = null;
}

private function moveAndResizeImage($filter = 'primary')
{
    $path = $this->getWebTmpPath();
    $target = $this->getAbsolutePath();

    if (file_exists($path)) {
        $container = $this->container; 
        // got problem here. $container is null for /job/create
        // but it is okay for /job/update
        $dataManager = $container->get('liip_imagine.data.manager');
        $filterManager = $container->get('liip_imagine.filter.manager');

        $image = $dataManager->find($filter, $path); 
        $thumb = $filterManager->applyFilter($image, $filter);

        file_put_contents($target, $thumb->getContent());

        unlink($path); // remove the temp client original image
    }
}
// ...

src/Ibw/JobeetBundle/Controller/JobController.php

// ...
public function createAction(Request $request)
{
    $entity = new Job();
    $form = $this->createCreateForm($entity);
    $form->handleRequest($request);

    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($entity);
        $em->flush();

        return $this->redirect($this->generateUrl('ibw_job_preview', array(
            'company' => $entity->getCompanySlug(),
            'location' => $entity->getLocationSlug(),
            'token' => $entity->getToken(),
            'position' => $entity->getPositionSlug()
        )));
    }

    return $this->render('IbwJobeetBundle:Job:new.html.twig', array(
        'entity' => $entity,
        'form'   => $form->createView(),
    ));
}
// ....

有什么办法可以解决这个问题吗?

我通过在事件侦听器 class 中添加 prePersist 来解决这个问题,以确保容器在持久化之前可用。 LiipImagineBundle 服务在 postPersist 上调用。我将 JobListener 重命名为 MyListener。这是我的工作代码:

src/Ibw/JobeetBundle/Resources/config/services.yml

ibw.jobeet.event.mylistener:
    class: Ibw\JobeetBundle\Listener\Event\MyListener
    calls:
        - [setContainer, ["@service_container"]]
    tags:
        - { name: doctrine.event_listener, event: postLoad }
        - { name: doctrine.event_listener, event: prePersist }

src/Ibw/JobeetBundle/Listener/Event/MyListener.php

<?php
namespace Ibw\JobeetBundle\Listener\Event;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;

use Ibw\JobeetBundle\Entity\Job;

class MyListener
{
    /** @var ContainerInterface */
    protected $container;

    /**
    * @param ContainerInterface @container
    */
    public function setContainer(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * @param LifecycleEventArgs $eventArgs
     */
    public function postLoad(LifecycleEventArgs $eventArgs)
    {
        $this->injectContainer($eventArgs);
    }

    /**
     * @param LifecycleEventArgs $eventArgs
     */
    public function prePersist(LifecycleEventArgs $eventArgs)
    {
        $this->injectContainer($eventArgs);
    }

    protected function injectContainer(LifecycleEventArgs $eventArgs)
    {
        $entity = $eventArgs->getEntity();
        if ($entity instanceof Job) {
            $entity->setContainer($this->container);
        }

        $em = $eventArgs->getEntityManager();
        $repository = $em->getRepository('IbwJobeetBundle:Job');
        if ($entity instanceof Job) {
            $repository->setContainer($this->container);
        }
    }
}

可能有更好的方法来处理这个问题。如果有,请 post 回答。我知道注入整个容器是一种不好的做法。我稍后会更新以注入所需的服务。