4

I'm new to Symfony and following the Jobeet tutorial. I have three entities - Job, Category and User. I have the following service listener.

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);
        }
    }
}

I expected postLoad would be invoked only for the Job entity, but I found that it is also called for the other two entities Category and User. I only define setContainer in the Job entity. So, I got the undefined method for the other entities. My workaround is to check with method_exists.

Is there any way to run postLoad on a particular entity only?

Sithu
  • 4,752
  • 9
  • 64
  • 110
  • Have you tried this? http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#entity-listeners – BentCoder Jun 29 '15 at 19:39
  • I updated the answer below. I misunderstood in first place. That should be it this time. I tested it myself. – BentCoder Jul 01 '15 at 21:10

3 Answers3

3

UPDATED ANSWER

Your example is an event listener but, based on your description you need an entity listener so checkout example below.

  • An Entity Listener could be any class, by default it should be a class with a no-arg constructor.
  • Different from Implementing Event Listeners an Entity Listener is invoked just to the specified entity.
  • An entity listener method receives two arguments, the entity instance and the lifecycle event.

Entity

namespace Your\WhateverBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\EntityListeners({"Your\WhateverBundle\EntityListener\JobListener"})
 * @ORM\Table(name="job")
 */
class Job
{
   // Your properties, getters and setters
}

EntityListener

namespace Your\WhateverBundle\EntityListener;

use Your\WhateverBundle\Entity\Job;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Mapping as ORM;

class JobListener
{
    /** @ORM\PostLoad */
    public function postLoadHandler(Job $job, LifecycleEventArgs $args)
    {
        // Do whatever you want
    }
}
BentCoder
  • 12,257
  • 22
  • 93
  • 165
  • I think `$entity instanceof Job` is not different to `method_exists($entity, 'setContainer')`. The `postLoad` is run though for the unrelated entities. – Sithu Jun 02 '15 at 01:44
  • @Sithu - More example added and I've been using that logic without a problem. – BentCoder Jun 02 '15 at 08:35
  • I think `doctrine.event_listener` is running on every page load, to not a particular entity. According to your edit, if `entity.listener.user_persist:` is only attaching to the User entity. we don't need to check `$entity instanceof User`. I'm looking for such entity listener rather than event listener as @tetranz answered, but I could not achieve it yet. – Sithu Jun 09 '15 at 02:13
  • 1
    [Example](http://intelligentbee.com/blog/2015/02/18/symfony2-doctrine-2-entity-listeners/) and [Example](http://egeloen.fr/2013/12/01/symfony2-doctrine2-entity-listener-as-serice/) – BentCoder Jun 09 '15 at 07:17
2

If you're running Doctrine 2.4 or later, you can use an Entity listener rather than an event listener. That is triggered only by the entity or entities associated with it . I've used this successfully in Symfony although I don't have access to my code right now. Here's an example although it misses the vital part of how you associate the listener with the relevant entity. I think it just needs @EntityListeners({"listener service name"}) added to the @Entity annotation block (or XML or YAML if you're not using annotations) as per the Doctrine docs. I'll check my code later and correct if anything more was needed.

tetranz
  • 1,932
  • 3
  • 24
  • 32
  • This could be a possible right answer, but I was not able to make it working. I use YAML and `postLoad`, but `postLoad` is never called. – Sithu Jun 02 '15 at 04:57
1

Apparently the Listener class must be placed next to to Entity class. With a directory structure like this, and the following configuration it seems to work like a charm.

# src/vendor/yourBundle/Entity/JobListener.php
namespace VENDOR\YourBundle\Entity;

use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use VENDOR\YourBundle\Entity\Job;

/**
 * Class JobListener
 * @package VENDOR\YourBundle\Entity
 */
class JobListener {

    public function postLoad( Job $job, LifecycleEventArgs $args ) {
        .....
    }
}
# src/vendor/yourBundle/Resources/config/doctrine/Job.orm.yml
VENDOR\YourBundle\Entity\Job:
type: entity
...
entityListeners:
    VENDOR\YourBundle\Entity\JobListener: ~
...

or if your prefer annotations

# src/vendor/yourBundle/Entity/Job.php
/**
* @ORM\Entity
* @ORM\EntityListeners({"VENDOR\YourBundle\Entity\JobListener"})
*/
class Job {
    .....
}

If you have to inject some service into your Listener, you have to add some similar configuration.

# app/config/services.yml
# or
# src/vendor/yourBundle/Resources/config/services.yml
doctrine.job_listener:
    class: Vendor\YourBundle\Entity\JobListener
    arguments: ["Path\To\Your\Service"]
    tags:
        - { name: doctrine.orm.entity_listener }
Benjamin Wenzel
  • 101
  • 1
  • 8