Запрос Symfony Doctrine, основанный на взаимосвязанных объектах «многие ко многим» - PullRequest
0 голосов
/ 16 марта 2019

Я строю простую систему обмена сообщениями. Пользователи связаны с MessageThreads в отношении ManyToMany. MessageThreads может иметь много пользователей (например, групповые чаты или каналы) или только двух пользователей (например, личные сообщения).

class User extends BaseUser
{
   /**
     * HAS MANY: MessageThreads
     * @ORM\ManyToMany(targetEntity="MessageThread", inversedBy="users", fetch="EXTRA_LAZY", cascade={"persist"})
     * @ORM\JoinTable(
     *  name="user_message_thread",
     *  joinColumns={
     *      @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     *  },
     *  inverseJoinColumns={
     *      @ORM\JoinColumn(name="mthread_id", referencedColumnName="id")
     *  }
     * )
     */
    private $messageThreads;
...
}

class MessageThread
{
    /**
     * HAS MANY: Users
     * @ORM\ManyToMany(targetEntity="User", mappedBy="messageThreads", cascade={"persist"})
     */
    private $users;
...
}

Теперь, учитывая, что у пользователя есть способ открыть приватный чат с определенным другим пользователем, я хочу иметь возможность определить, существует ли между ними MessageThread (и только для них), или я должен создать новый

public function newPrivAction(Request $request, string $slug)
{
...
    // try locating a thread with ONLY current and $friend user
    if($thread = $em->getRepository('AppBundle:MessageThread')->findOnePrivFor($this->getUser(), $friend)) {

        // ** dbg
        print "Old priv found #" . $thread->getId();
        die();
    }

    // or create a brand new thread
    else {

        $thread = new MessageThread();

        $thread
            ->addUser($this->getUser())
            ->addUser($friend)
        ;

        $em->persist($thread);
        $em->flush();

        // ** dbg
        print "New priv created #" . $thread->getId();
        die();
    }
...
}

Моя проблема в том, что я не могу понять, как структурировать метод репозитория findOnePrivFor(...). В идеале мне нужен метод, который просто находит поток с ТОЛЬКО ЭТИМИ пользователями, но после нескольких часов поисков в Google, я отказался от этого. Мой второй подход (или, скорее, обходной путь) заключался в том, чтобы найти пользователей, которые принадлежат к данному потоку, и ограничить количество пользователей до 2. Этот подход довольно близко подошел ко мне - кроме подсчета, которая, похоже, не работает.

class MessageThreadRepository extends \Doctrine\ORM\EntityRepository
{
    public function findOnePrivFor(User $user1, User $user2)
    {
        $qb = $this->createQueryBuilder('mt');
        $qb
            ->where(':user1 MEMBER OF mt.users')
            ->andWhere(':user2 MEMBER OF mt.users')
            //->andWhere('count(mt.users) = 2')                                 // ** fails
            //->andWhere($qb->expr()->eq($qb->expr()->count('mt.users'), 2))    // ** fails
            ->setMaxResults(1)

            ->setParameters([

                'user1' => $user1,
                'user2' => $user2
            ])
        ;

        return $qb->getQuery()->getOneOrNullResult();
    }
}

Любая помощь будет высоко ценится.

1 Ответ

0 голосов
/ 16 марта 2019

Добавьте условие, имеющееся к вашему построителю запросов:

$qb->join('mt.users', 'u')
   ->having('count(u.id) = 2'))
...