Tracking New Member Origination with Symfony2 post

Posted on 2013-07-08 by jwage


NOTE The example code in this blog post has been simplified to make things more concise and easy to read.

At OpenSky it is important for us to track how a member joined so that we can determine causation between joining and any subsequent actions taken on the site, like an order.

To get started create a new class called OriginationManager with a method named updateHistory. It will accept a Request object and record the query parameters from the current URL in the session.

<?php class OriginationManager
{
    public function updateHistory(Request $request)
    {
        $session = $request->getSession();

        $history = $session->get('user_origination_history', array());
        $history[] = $request->query->all();

        $session->set('user_origination_history', $history);
    }
}

Now create an OriginationListener class that will listen to the kernel.request event and make use of the OriginationManager::updateHistory() API we just created.

<?php class OriginationListener
{
    // ...

    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();

        // Only process non-logged-in users
        if ($token = $this->securityContext->getToken()) {
            $user = $token->getUser();
            if ($user instanceof User) {
                return;
            }
        }

        $this->originationManager->updateHistory($request);
    }
}

Now we are tracking the query parameters of logged out users on every request. We can make use of this information later when a user joins. First, lets define a new model that can be used to store the origination information along with the user (assume the model is persisted with Doctrine). Each new User gets an Originator record when they join. It allows us to essentially see how a new user entered the site and what clicks they made before joining.

<?php class Originator
{
    /**
     * The raw array of request query data.
     *
     * @var array
     */
    protected $history = array();

    /**
     * The user that originated this member.
     *
     * @var User
     */
    protected $user;

    /**
     * The value of the first osky_origin parameter we encounter.
     *
     * @var string
     */
    protected $origin;

    /**
     * The value of the first osky_source parameter we encounter.
     *
     * @var string
     */
    protected $source;

    // ...
}

Assume your application already notifies an event named user.created. In OriginationListener create an onUserCreated method that will listen to the user.created event.

<?php

class OriginationListener
{
    // ...

    public function onUserCreated(UserCreatedEvent $event)
    {
        $user = $event->getUser();
        $request = $event->getRequest();

        $this->originationManager->assignUserOrigination($user, $request);
    }
}

Next, create the OriginationManager::assignUserOrigination() method. It will utilize the request query parameters we saved on the session earlier to create a new Originator record.

<?php class OriginationManager
{
    public function assignUserOrigination(User $user, Request $request)
    {
        $session = $request->getSession();

        $history = $session->get('user_origination_history', array());

        $originator = new Originator();
        $originator->setHistory($history);

        foreach ($history as $query) {
            if (!$originator->getOrigin() && isset($query['osky_origin'])) {
                $originator->setOrigin($query['osky_origin']);

                if ($user = $this->userRepository->find($query['osky_origin'])) {
                    $originator->setUser($user);
                }
            }

            if (!$originator->getSource() && isset($query['osky_source'])) {
                $originator->setSource($query['osky_source']);
            }
        }

        $user->setOriginator($originator);

        $session->remove('user_origination_history');
    }
}

Now if a logged out user were to visit OpenSky with a parameter named osky_origin in the URL with another users id as the value, the Originator record that gets created for the new member will have a reference to that User. We can then utilize that information do whatever we want. In our case we give the user that got the new User to join a credit and a thank you. The osky_source parameter can be used as an arbitrary reporting variable to help with identifying marketing campaigns.

Keep in mind that this is an example implementation and it omits many details specific to OpenSky for the sake of simplicity. The real implementation we use has dozens of other parameters that can be used to associate records together and assist with reporting. You can add to this implementation and check for custom parameters and standard ones like utm_source, utm_campaign, etc.

Categories: articles