The other day I had to add some logic right after an user was saved to the database. I ended up using events to get this done without cluttering the entity.
I first added the logic in the User-entity but I then realised this logic was not really related to the user entity itself. Or the application, for that matters.
In order to seperate concerns, I decided to create a hook after the User save-action. That would allow me to add logic at that particular time without cluttering the User entity with nonrelevant code.
This has a downside though. When you want to debug what the heck happens after the User is saved to the database, you won’t find it in the User entity. This might send you down a long code-hunt. But you will see the dispatching of the event though. So if you’re new to this, remind yourself that there can be a whole different world behind the dispatching of an event.
Setting it up consists of 3 steps:
The event is nothing more than a class.
The event is the object which is passed around. Therefore you want to populate the event with all the information the subscriber(s) need.
For that purpose I create a setter and a getter. The code which will dispatch the event will use the setter, the event subscriber will use the getter.
# file src/AppBundle/Event/UserCreatedEvent.php
<?php
namespace AppBundle\Event;
use AppBundle\Entity\User;
use Symfony\Component\EventDispatcher\Event;
class UserCreatedEvent extends Event
{
private $user;
public function setUser(User $user)
{
$this->user = $user;
}
public function getUser()
{
return $this->user;
}
}
Now we decide in what moment of time we’ll dispatch (fire) the event. In our example this will be right after the user is saved to the database.
# file src/AppBundle/Entity/User.php
/* other code */
public function createUser(User $user)
{
/* more code */
// save the user to the database
$this->em->persist($user);
$this->em->flush();
// dispatch an event where others can work with the newly created user
$eventDispatcher = $this->container->get('event_dispatcher');
$event = new UserCreatedEvent();
$event->setUser($user);
$eventDispatcher->dispatch('user.event.created', $event);
return $user;
}
The subscriber is the class with the method which gets called once the event gets dispatched.
# file src/AppBundle/EventSubscriber/UserCreatedSubscriber.php
<?php
namespace AppBundle\EventSubscriber;
class UserCreatedSubscriber
{
public function newUserCreated(UserCreatedEvent $event)
{
var_dump($event->getUser());
}
}
Now that we have the code for the subscriber, we need to actually subscribe the subcriber to the event. This is the glue between dispatching an event and acting upon it.
You do this in services.yml as you register it as a service.
# create a listener for the UserCreatedEvent
valuation.event.created:
class: AppBundle\EventSubscriber\UserCreatedSubscriber
tags:
- { name: kernel.event_listener, event: valuation.event.created, method: newUserCreated }
That’s it!
If you want to read more:
In order to benefit from the Symfony3 translations in javascript files, I use BazingaJsTranslationBundle from
I had the weirdest thing: whenever I logged out, cleared my cache, went to the login page and logged in, I got redirected to /translations which showed me the generated JSON file with the translations.
Cause: the login page tried to load the JSON translation because of my <script src="{{ url('bazinga_jstranslation_js', {}) }}?locales=en,nl"></script>
script import. But that failed as security did not allow anonymous access to that URI.
Symfony registered the failed URL and, as I’m on the login page, redirected me to the url that first failed once I was logged in.
Solution: allow anonymous access to URI /translations.
In file app/config/security.yml
I added the following:
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/translations, roles: IS_AUTHENTICATED_ANONYMOUSLY }