ФорумПрограммированиеPHP для идиотовSymfony → Реферальные коды на симфони

Реферальные коды на симфони

  • artoodetoo

    Сообщения: 5139 Репутация: N Группа: в ухо

    Spritz 30 января 2014 г. 22:01

    Делюсь решением, может кому надо.

    Задачка: фиксировать коды рефералов.

    Мы каким-то образом мотивируем пользователей завлекать новых пользователей. Понадобится реферальный код в аккаунте пользователя. Точнее два:
    - собственный код, который мы вставим в реферальную ссылку для копирования (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'));
                }
            }
        }
    }
    

    Реквестую на каменты.

    Спустя 57 сек.

    ЁМАНА! ФОРУМ ПРОГРАММИСТОВ!
    Cорцы в "CODE" нифига не выглядят как сорцы. Попробую сохранить как markdown…

    EDITED!!!: освоил разметку. я люблю маркдаун.

    короче вот gist
    https://gist.github.com/artoodetoo/2f6253db03ad2d71856f

    ιιlllιlllι унц-унц
  • adw0rd

    Сообщения: 22905 Репутация: N Группа: в ухо

    Spritz 30 января 2014 г. 22:21, спустя 19 минут 34 секунды

    Ты знаешь правило - возьми и сделай

    adw/0
  • artoodetoo

    Сообщения: 5139 Репутация: N Группа: в ухо

    Spritz 31 января 2014 г. 0:20, спустя 1 час 59 минут 46 секунд

    питон же? не все такие крутые
    ιιlllιlllι унц-унц
  • Абырвалг

    Сообщения: 6476 Репутация: N Группа: Джедаи

    Spritz 31 января 2014 г. 1:16, спустя 55 минут 26 секунд

    Ну нормуль. Мы примерно так и делали, только не заморачивались с prePersist а в контроллере регистрации напрямую считывали.

     if ($entity instanceof User) {

    сам понимаешь, выглядит хреново. Когда у тебя 50 таких слушателей будет - наверно еще и не особо производительно, на каждый чих любой сущности все их запускать. Посмотри в сторону http://docs.doctrine-project.org/en/latest/reference/events.html#entity-listeners , правда я не использовал это еще ни разу
  • artoodetoo

    Сообщения: 5139 Репутация: N Группа: в ухо

    Spritz 31 января 2014 г. 6:21, спустя 5 часов 4 минуты 44 секунды

    я так понимаю в недрах проекта и без меня всяких слушателей до едрени фени )))

    теоретически я согласен, слушатель конкретной таблицы был бы логичней. но тут опять беда:
    "An Entity Listener could be any class, by default it should be a class with a no-arg constructor."
    то есть контейнер в него при создании не заинжектить, опять та же проблема, что и с классом User — сессию не добыть!
    ιιlllιlllι унц-унц
  • Абырвалг

    Сообщения: 6476 Репутация: N Группа: Джедаи

    Spritz 31 января 2014 г. 6:27, спустя 6 минут 50 секунд

    хе-хе, нужно просто немного проскроллить документацию http://docs.doctrine-project.org/en/latest/reference/events.html#entity-listeners-resolver , все там есть

    Спустя 32 сек.

    Как же я рад, что постепенно пыховцы становятся на светлую сторону силы и начинают использовать Symfony2
  • vasa_c

    Сообщения: 3127 Репутация: N Группа: в ухо

    Spritz 31 января 2014 г. 7:32, спустя 1 час 4 минуты 26 секунд

    @Абырвалг, да ты фанатег!
  • adw0rd

    Сообщения: 22905 Репутация: N Группа: в ухо

    Spritz 2 февраля 2014 г. 14:32, спустя 2 дня 7 часов

    питон же? не все такие крутые

    @artoodetoo, так это только фронтендом надо решать, надо найти "более подходящую" либу на js и прикрепить в шаблон

    adw/0
  • Абырвалг

    Сообщения: 6476 Репутация: N Группа: Джедаи

    Spritz 2 февраля 2014 г. 14:49, спустя 17 минут 17 секунд

    почему фронтендом?

    Спустя 195 сек.

    http://highlightjs.org/ - от Салагаева
    https://github.com/alexgorbatchev/SyntaxHighlighter - тоже русский разработчик
  • artoodetoo

    Сообщения: 5139 Репутация: N Группа: в ухо

    Spritz 2 февраля 2014 г. 18:57, спустя 4 часа 7 минут 18 секунд

    @adw0rd, согласен, лучше JS подходящий подцепить. если дашь доступ, я попробую.



    Спустя 307 сек.

    или как минимум сделаю
    code {white-space: pre;}
    ιιlllιlllι унц-унц
  • adw0rd

    Сообщения: 22905 Репутация: N Группа: в ухо

    Spritz 3 февраля 2014 г. 22:53, спустя 1 день 3 часа 56 минут

    http://highlightjs.org/](http://highlightjs.org/) - от Салагаева

    @Абырвалг, сейчас и так он стоит и включено автоматическое определение языка в коде

    Спустя 77 сек.

    Тест текущей подсветки:

    class Test:
        one = 1
    
        def test(self):
            return self.one
    

    Спустя 298 сек.

    Тест ббкода

    [code]
    class Test:
    one = 1

    def test(self):
        return self.one [/code]
    
    adw/0
  • artoodetoo

    Сообщения: 5139 Репутация: N Группа: в ухо

    Spritz 4 февраля 2014 г. 21:49, спустя 22 часа 56 минут 32 секунды

    Просто цитирую чтобы понять

    Тест текущей подсветки:

    class Test:
        one = 1
    
        def test(self):
            return self.one
    

    Тест ббкода

    [code]
    class Test:
    one = 1

    def test(self):
        return self.one
    

    [/code]

    Что понял:

    Прямо сейчас чтобы вставить код надо использовать markdown и тупо отбить код (без какой-либо доп. разметки).
    см. http://softwaremaniacs.org/playground/showdown-highlight/](http://softwaremaniacs.org/playground/showdown-highlight/ "http://softwaremaniacs.org/playground/showdown-highlight/"">

    ιιlllιlllι унц-унц
  • Sinkler

    Сообщения: 8096 Репутация: N Группа: в ухо

    Spritz 4 февраля 2014 г. 22:28, спустя 38 минут 16 секунд

    в одном сообщении нельзя смешивать ббкод и маркдаун, кстати говоря. и не я это придумал

  • artoodetoo

    Сообщения: 5139 Репутация: N Группа: в ухо

    Spritz 4 февраля 2014 г. 22:43, спустя 15 минут 47 секунд

    это не проблема.
    фигня в том, что markdown временами портит сиволы угловых скобочек, а bbcode создает разметку
    <code>…</code>
    в которой пробелы интерпретируются не как в
    <pre>…</pre>.
    ιιlllιlllι унц-унц
  • Timur

    Сообщения: 1068 Репутация: N Группа: Джедаи

    Spritz 6 февраля 2014 г. 9:44, спустя 1 день 11 часов

    По-умничаю немного. Инжектить контейнер в сервис не тру. Передавай в констурктор @session и @security, другие сервисы всё равно там не используются.

Пожалуйста, авторизуйтесь, чтобы написать комментарий!