Делюсь решением, может кому надо.
Задачка: фиксировать коды рефералов.
Мы каким-то образом мотивируем пользователей завлекать новых пользователей. Понадобится реферальный код в аккаунте пользователя. Точнее два:
- собственный код, который мы вставим в реферальную ссылку для копирования (user.ref)
- код по которому этот пользователь зарегался (user.follow_ref)
Очевидно что ссылка с кодом будет приводить клиента не на страницу регистрации, а на "вкусную" страничку. Со временем число таких страничек будет прирастать. А код должен сохраниться от прихода пользователя до заведения нового акка. Очевидно в сессии.
Я это реализовал с учетом специфики Symfony 2 — через механизм Listener. "Пользователь" это моё расширение популярного FOSUserBundle, добавляется несколько полей. Внутри класса User с полями можно работать напрямую, а извне через геттеры/сеттеры (слава фабьену, доктрина умеет их генерить).
Собственный реферальный код пользователя генерится как производная от uniqid() в момент создания объекта.
Класс Пользователь:
<?php
namespace Acme\AcmeUserBundle\Entity;
use Another\DescendantOfFOSUserBundle\Entity\BaseUser;
class User extends BaseUser
{
/** @var string $ref This User's reference code */
protected $ref;
/** @var string $followRef Who do this user follow? */
protected $followRef;
public function __construct()
{
parent::__construct();
$this->ref = md5(uniqid());
}
// ... другие поля + сеттеры/геттеры
}
Чтобы не программировать каждую лендинг-страницу, я создаю "слушателя", который тупо проверяет get-параметры. Еще раз, параметр может быть по любому маршруту! Если волшебное имя есть — слушатель пишет значение в сессию.
Дальше я собирался сохранять это значение в классе событии сущности User::prePersist, но не получилось, т.к. оттуда проблематично достать класс сессии. Доктриновские сущности не ContainerAware. В итоге завел еще одного слушателя, который перехватывает событие prePersist.
Класс на оба события у меня один, назвал ReferralListener. Оформил как сервис:
Конфиг сервиса:
services:
sandbox_init_cms.request_listener:
class: Acme\AcmeBundle\EventListener\ReferralListener
arguments: [ @service_container ]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
- { name: doctrine.event_listener, event: prePersist }
Класс Слушатель:
<?php
namespace Acme\AcmeBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Application\Acme\UserBundle\Entity\User;
class ReferralListener
{
/**
* @var ContainerInterface
*/
protected $container;
/**
* Instantiate listener. Inject container to access services
*
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* Intercept all HTTP requests, and keep track referral
*
* @param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if ($request->query->has('_ref')) {
// It should be done for guests only
$securityContext = $this->container->get('security.context');
$isLoggedIn = ($securityContext->getToken() &&
($securityContext->isGranted('IS_AUTHENTICATED_FULLY') ||
$securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')));
if (!$isLoggedIn) {
$code = $request->query->get('_ref');
$session = $this->container->get('session');
$session->set('_ref', $code);
}
}
}
/**
* Intercept all ORM prePersist events, and store referral if applicable
*
* @param LifecycleEventArgs $args
*/
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if ($entity instanceof User) {
$session = $this->container->get('session');
if ($session->has('_ref')) {
$entity->setFollowRef($session->get('_ref'));
}
}
}
}
Реквестую на каменты.
ЁМАНА! ФОРУМ ПРОГРАММИСТОВ!
Cорцы в "CODE" нифига не выглядят как сорцы. Попробую сохранить как markdown…
EDITED!!!: освоил разметку. я люблю маркдаун.
короче вот gist
<?php | |
namespace Application\Me\MyUserBundle\EventListener; | |
use Symfony\Component\HttpKernel\Event\GetResponseEvent; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\DependencyInjection\ContainerInterface; | |
use Doctrine\ORM\Event\LifecycleEventArgs; | |
use Application\Me\MyUserBundle\Entity\User; | |
/** | |
* This service helps to save partner referral code for new registered user | |
* It should be registered as: | |
* | |
* @author artoodetoo | |
*/ | |
class ReferralListener | |
{ | |
/** | |
* @var ContainerInterface | |
*/ | |
protected $container; | |
/** | |
* Instantiate listener. Inject container to access services | |
* | |
* @param ContainerInterface $container | |
*/ | |
public function __construct(ContainerInterface $container) | |
{ | |
$this->container = $container; | |
} | |
/** | |
* Intercept all HTTP requests, and keep track referral | |
* | |
* @param GetResponseEvent $event | |
*/ | |
public function onKernelRequest(GetResponseEvent $event) | |
{ | |
$request = $event->getRequest(); | |
if ($request->query->has('_ref')) { | |
// It should be done for guests only | |
$securityContext = $this->container->get('security.context'); | |
$isLoggedIn = ($securityContext->getToken() && | |
($securityContext->isGranted('IS_AUTHENTICATED_FULLY') || | |
$securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED'))); | |
if (!$isLoggedIn) { | |
$code = $request->query->get('_ref'); | |
$session = $this->container->get('session'); | |
$session->set('_ref', $code); | |
} | |
} | |
} | |
/** | |
* Intercept all ORM prePersist events, and store referral if applicable | |
* | |
* @param LifecycleEventArgs $args | |
*/ | |
public function prePersist(LifecycleEventArgs $args) | |
{ | |
$entity = $args->getEntity(); | |
if ($entity instanceof User) { | |
$session = $this->container->get('session'); | |
if ($session->has('_ref')) { | |
$entity->setFollowRef($session->get('_ref')); | |
} | |
} | |
} | |
} |
services: | |
# Create Request & ORM Listener | |
sandbox_init_cms.request_listener: | |
class: Application\Me\MyUserBundle\EventListener\ReferralListener | |
arguments: [ @service_container ] | |
tags: | |
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest } | |
- { name: doctrine.event_listener, event: prePersist } |
<?php | |
namespace Application\Me\MyUserBundle\Entity; | |
use Another\FOSUserDescendant\Entity\BaseUser; | |
class User extends BaseUser | |
{ | |
/** | |
* @var string $ref This User's reference code | |
*/ | |
protected $ref; | |
/** | |
* @var string $followRef Who do this user follow? | |
*/ | |
protected $followRef; | |
public function __construct() | |
{ | |
parent::__construct(); | |
$this->ref = md5(uniqid()); | |
} | |
// more fields plus setters/getters ... | |
} |