There are basically 3 parts to this listener – service config, service file and the Activity Log entity. We’ll start with the config.
This is the simple one. We define the name of the listener, the class, arguments (we pass the service container so we have access to the Entity Manager, session and security context) and then we, lastly, define doctrine event listeners. We call it on adding, editing, before and after deleting.
This is a fairly straightforward entity. We have a primary ID, entity_id which is not a foreign key but will point to the record ID that is being updated, entity class been updated, setting a user ID and date that it has been updated. My use of setting a user ID requires the option of setting it to 0, so I don’t setup a relationship. Next up, the big one, is the service itself.
So, a lot is going on up there, we’ll go section by section. Since we are injecting the service container in the service config, we set a protected variable and inject it in the construct method. The first time we use the container is with the _em() method, a convinience method so we can easily call the entity manager on a specific repo or when persisting/flushing.
The first important area to look at is the $_validClasses private variable, this is an array of Entity classes that the Activity Log will pickup and keep track of. If your entity is not in this list, it will always be ignored. By default we log a fake Article entity and User entity. Both postPersist (a created record) and postUpdate (updating record) call the _checkLog function if the current user ID is not null (meaning non logged-in user), we’ll start there next.
Going down to the _checkLog function, it gets passed either a passed in an entity or in the case of the postPersist/postUpdate, the LifecycleEventArgs contains the entity by calling the getEntity method on it. We then skip the log check if the updated entity is the ActivityLog and check to see if the entity class matches the array of valid classes as mentioned before. The way this listener is setup, we keep one record per ID/class record, but this can easily be changed.
A lookup is made to the ActivityLog entity and if a record is not found, we add one and if no delete flag is set, we update it. (otherwise, delete it) Moving back up to preRemove/postRemove, the easiest way to explain this is if you call a remove and flush in preRemove, it will screw up with whatever normal record is being added/updated. So our nifty function sets a session with the entity and in postRemove checks the session and if it has a value, calls the _checkLog function with a delete flag which calls the _removeLog function.
The rest is straightforward, _addLog sets the entity class/id/user and creates a brand new record. _updateLog updates the specified ActivityLog entity record with the current user_id and date (the date is set because I experienced issues with gedmo doing this on it’s own). Lastly, _removeLog simply calls a remove and flush on the specified entity.
We hope you enjoyed this tutorial. There are a lot of ways to alter how this works, from tracking all records and flushing every so often/per records or logging what data the entity contained at the time of update/insertion, lots of options. Have fun!
Our next line of tutorials continues with Symfony 2. This tutorial will describe how to build an Activity Log listener which will track who/when a record is updated for whichever entity you define. Continue on to see how it works.
Another option for faster centralized logging is Papertrail.