Kaer_Morchen
@Kaer_Morchen
Разрабатываю web-приложения.

Общий рейтинг для разных типов entity в symfony 2?

Здравствуйте.

В проекте используются symfony 2, doctrine.

Есть задача сделать общий рейтинг для различных типов entity через одну общую сущность vote.
Упрощено мы видим ее так:
/**
 * Flavi\AppBundle\Entity\Vote
 *
 * @ORM\Table(name="votes")
 * @ORM\Entity(repositoryClass="Flavi\AppBundle\Entity\VoteRepository")
 * @ORM\HasLifecycleCallbacks()
 * @JMS\ExclusionPolicy("none")
 */
class Vote {
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="integer")
     */
    protected $userId;

    /**
     * @ORM\Column(type="integer")
     */
    protected $entityId;

    /**
     * @ORM\Column(type="string")
     */
    protected $entity;

    /**
     * @ORM\Column(type="integer")
     */
    protected $value;

    /**
     * @ORM\Column(type="datetime")
     */
    protected $createdAt;
}


Пользователь голосует +1 за например за entry с id = 2. В таблице создается Vote c полями: кто голосовал (userId), за какую сущность (entity, entityId), как (value), ну и когда (createdAt).
b92bd1476a65430480cc059620efe54f.png

Так как в проект это серверное API, мы используем сериализацию в json. Не понятно, как настроить связи в doctrine, чтобы рейтинг сущности лежал внутри нее и при сериализации и выглядел бы примерно так:

{entry: {
    id: 2,
    rating: 1,
    ...
}}


РЕШЕНИЕ:

примерно такое

entity_subscriber:
          class: Flavi\AppBundle\EventListener\EntitySubscriber
          arguments:
              - "@doctrine.orm.entity_manager"
          tags:
              - { name: jms_serializer.event_subscriber }


<?php

namespace Flavi\AppBundle\EventListener;

use Doctrine\ORM\EntityManager;
use JMS\Serializer\EventDispatcher\Events;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\ObjectEvent;

class EntitySubscriber implements EventSubscriberInterface
{
    protected $em;

    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    public static function getSubscribedEvents()
    {
        return array(
            array('event' => Events::POST_SERIALIZE, 'method' => 'onPostSerialize'),
        );
    }

    public function onPostSerialize(ObjectEvent $event)
    {
        //Добавляем рейтинг entity из таблицы votes
        $id = $event->getObject()->getId();
        $type = $event->getType()['name'];

        $qb = $this->em->getRepository('FlaviAppBundle:Vote')->createQueryBuilder('v');
        $qb->select('SUM(v.value) as rating');
        $qb->where('v.entity = :entity AND v.entityId = :entity_id');
        $qb->setParameter('entity', $type);
        $qb->setParameter('entity_id', $id);

        $result = $qb->getQuery()->getSingleScalarResult();

        if ($result !== NULL) {
            $event->getVisitor()->addData('rating', (integer) $result);
        }
    }
}
  • Вопрос задан
  • 2407 просмотров
Решения вопроса 1
@myLizzarD
PHP developer
У вас не получится в данном случае сделать это нативно с помощью доктрины. По скольку связи между Vote и другими сущностями вы не создадите!
Я вижу такое решение: по скольку вы используете JMSSerializerBundle для сериализации сущностей, то можете настроить подписчика, который будет на pre_serialize проверять тип сущности, доставать для нее рейтинг и добавлять в результат сериализации.
Подробнее про это мы сможете прочитать тут
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
BoShurik
@BoShurik Куратор тега Symfony
Symfony developer
Я бы сделал VotableInterface с методом setRating и при сохранении Vote, пересчитывал рейтинг для сущности, за которую проголосовали
interface VotableInterface
{
    public function setRating($rating);
}

class Object implements VotableInterface
{
    /**
     * @ORM\Column(type="integer")
     */
    private $rating;

    public  function setRating($rating)
    {
        $this->rating = $rating;
    }

    public function getRating()
    {
        return $this->rating;
    }
}

class VoteListener
{
    public function postPersist(LifecycleEventArgs $event)
    {
        $vote = $event->getEntity();
        if (!$vote instanceof Vote) {
            return;
        }
        /**
         * @var Object $entity
         */
        $entity = $vote->getEntityObject();
        if (!$entity instanceof VotableInterface) {
            // Exception
        }
        $entity->setRating($entity->getRating() + $vote->getValue());

        // Update $entity
    }
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы