Recording ‘Last Activity’ for Users in Symfony2 + FOSUserBundle

by James Halsall on January 27, 2012

Whilst working on a personal project based on the Symfony2 framework I wanted to record when a user was last active so that I could emulate a list of currently online users. I had already plugged in the amazing FOSUserBundle extension, so this was just about tweaking what was already there. If you’re in a similar situation and already have the Symfony2 standard distribution installed with FOSUserBundle then this walkthrough should be pretty straightforward.

Utilising Symfony2′s Event Model

Symfony2 has a great plethora of internal events that you can ‘hook’ into. In our case, we want to hook into a kernel event called which will fire whenever a request is made by the client browser. In your User bundle, create a services.yml file (e.g. src/App/UserBundle/Resources/config/services.yml) as follows:

services:
  activity_listener:
    class: App\UserBundle\Listener\Activity
    arguments: [@security.context, @doctrine]
    tags:
      - { name: kernel.event_listener, event: kernel.controller, method: onCoreController }

This is quite straightforward, this is just telling our application that when the kernel.controller event is fired we want our kernel to also call the onCoreController() method in our App\UserBundle\Listener\Activity class (we’ll create that in a minute). Before we move on to creating our event handler class we need to register our services.yml file with our main kernel. We can do this by editing app/config/config.yml and adding an import directive at the top of the file…

imports:
    - { resource: @AppUserBundle/Resources/config/services.yml }

The Event Handler Class

Now we have to create our event handler class, which will do most of the work. I decided to call my class Activity, but you can call it whatever you like as long as you make sure to amend your services.yml file to contain the same name. Our file will reside at src/UserBundle/Listener/Activity.php

namespace App\UserBundle\Listener;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Bundle\DoctrineBundle\Registry as Doctrine;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use DateTime;
use App\UserBundle\Entity\User;
class Activity
{
    protected $context;
    protected $em;
    public function __construct(SecurityContext $context, Doctrine $doctrine)
    {
        $this->context = $context;
        $this->em = $doctrine->getEntityManager();
    }
    /**
     * On each request we want to update the user's last activity datetime
     *
     * @param \Symfony\Component\HttpKernel\Event\FilterControllerEvent $event
     * @return void
     */
    public function onCoreController(FilterControllerEvent $event)
    {
        $user = $this->context->getToken()->getUser();
        if($user instanceof User)
        {
            //here we can update the user as necessary
            $user->setLastActivity(new DateTime());
            $this->em->persist($user);
            $this->em->flush($user);
        }
    }
}

Be sure to update your User class with the correct properties (e.g. last_activity) in order for the setters to work and you’re away.

4 comments

Need change onCoreController method, because in dev environment profiler debug bar has error “Call to a member function getUser() on a non-object”.
I add this clause
if ($this->context->getToken())
{
$user = $this->context->getToken()->getUser();
//…
}

Sorry for my english.

by pivasyk on February 1, 2012 at 8:52 pm. Reply #

Excellent spot! The token only becomes active after we have logged in, so calling getUser() before logging in would indeed cause an error.

by James Halsall on February 1, 2012 at 10:51 pm. Reply #

I have new changes :)
If you use partial controller like {% render %} you call onCoreController each time and have many sql inserts, need add this clouse

if ($event->getRequestType() !== \Symfony\Component\HttpKernel\HttpKernel::MASTER_REQUEST) {
return;
};

by pivasyk on February 7, 2012 at 12:05 am. Reply #

[...] Recording ‘Last Activity’ for Users in Symfony2 + FOSUserBundle [...]

by A week of symfony #265 (23->29 January 2012) « We are php on February 21, 2012 at 9:14 am. Reply #

Leave your comment

Required.

Required. Not published.

If you have one.