Listens to an event so auditable information can be set

1.0.3 2016-08-10 10:02 UTC



What is the Entity Blamable Component?

The Entity Blamable Component is a library that utilizes the Entity Tracker Component and lets you hook in to the entityChanged event.

This component lets you automatically update entities by implementing setUpdatedAt(), setUpdatedBy and setCreatedAt(). At the given time, you have to implement all of them. If you need only 1 or 2 of the methods, pull requests are always allowed. It's not implemented yet due to lack of requirements for our use-cases.


The blamable component requires a minimum of php 5.4 and runs on Doctrine2. For specific requirements, please check composer.json.


Installing is pretty easy, this package is available on packagist. You can register the package locked to a major as we follow Semantic Versioning 2.0.0.


    "require" : {
        "hostnet/entity-blamable-component" : "1.*"

Note: You can use dev-master if you want the latest changes, but this is not recommended for production code!


How does it work?

It works by putting the @Blamable annotation and BlamableInterface on your Entity and registering the listener on the entityChanged event, assuming you have already configured the Entity Tracker Component.

For a usage example, follow the setup below.


  • You have to add @Blamable to your entity
  • You have to add the BlamableInterface to your entity
  • You have to implement the BlamableProviderInterface on an object and pass it to the listener

Registering the events

Here's an example of a very basic setup. Setting this up will be a lot easier if you use a framework that has a Dependency Injection Container.

It might look a bit complicated to set up, but it's pretty much setting up the tracker component for the most part. If you use it in a framework, it's recommended to create a framework specific configuration package for this to automate this away.

Note: If you use Symfony2, you can take a look at the hostnet/entity-tracker-bundle. This bundle is designed to configure the services for you.

use Acme\Bundle\AcmeBundle\Service\AcmeBlamableProvider;
use Hostnet\Component\EntityBlamable\Listener\BlamableListener;
use Hostnet\Component\EntityBlamable\Resolver\BlamableResolver;
use Hostnet\Component\EntityTracker\Listener\EntityChangedListener;
use Hostnet\Component\EntityTracker\Provider\EntityAnnotationMetadataProvider;
use Hostnet\Component\EntityTracker\Provider\EntityMutationMetadataProvider;

/* @var $em \Doctrine\ORM\EntityManager */
$event_manager = $em->getEventManager();

// default doctrine annotation reader
$annotation_reader = new AnnotationReader();

// setup required providers
$mutation_metadata_provider   = new EntityMutationMetadataProvider($annotation_reader);
$annotation_metadata_provider = new EntityAnnotationMetadataProvider($annotation_reader);

// pre flush event listener that uses the @Tracked/@Blamable annotation
$entity_changed_listener = new EntityChangedListener(

// the resolver is used to find the correct annotation
$blamable_resolver = new BlamableResolver($annotation_metadata_provider);

// the object that will provide the username and date time. This is an 
// application specific implementation of the BlamableProviderInterface
$blamable_provider = new AcmeBlamableProvider();

// creating the blamable listener
$blamable_listener = new BlamableListener($blamable_resolver, $blamable_provider);

// register the events
$event_manager->addEventListener('preFlush', $entity_changed_listener);
$event_manager->addEventListener('entityChanged', $blamable_listener);

Creating a Provider for the Username and Timestamp

The provider is used to feed the required values to the BlamableListener, this is the only interface that requires a custom implementation in your project.

Note that in the example the username is defined as constructor param, but you might want to inject something that contains the currently logged in user and get that identifier.

namespace Acme\Bundle\AcmeBundle\Service;

use Hostnet\Component\EntityBlamable\Provider\BlamableProviderInterface;

class AcmeBlamableProvider implements BlamableProviderInterface
    private $username;

    public function __construct($username)
        $this->username = $username;

    public function getUpdatedBy()
        return $this->username;
    public function getChangedAt()
        return new \DateTime();

Configuring the Entity

All we have to do now is put the @Blamable annotation and BlamableInterface on our Entity.

use Doctrine\ORM\Mapping as ORM;
use Hostnet\Component\EntityBlamable\Blamable;
use Hostnet\Component\EntityBlamable\BlamableInterface;

 * @ORM\Entity
 * @Blamable
class MyEntity implements BlamableInterface
     * @ORM\...
    private $updated_by;
     * @ORM\...
    private $updated_at;
     * @ORM\...
    private $created_at;
    public function setUpdatedBy($by)
        $this->updated_by = $by;

    public function setUpdatedAt(\DateTime $at)
        $this->changed_at = $at;
    public function setCreatedAt(\DateTime $at)
        $this->created_at = $at

What's next?

// Voila, your updated_at and updated_by are filled in (and if it's new, created_at too).