Фильтровать подобъект в doctrine - PullRequest
0 голосов
/ 09 марта 2020

У меня есть две сущности, которые имеют отношение один ко многим.

Есть проект:

class Project
{
    // ...

    /**
     * @var \Doctrine\ORM\PersistentCollection|Template[]
     *
     * @ORM\OneToMany(targetEntity="Template", mappedBy="project")
     */
    private $templates;

    // ...
}

И у меня есть шаблоны, которые могут быть ограничены определенными пользователями:

class Template
{
    /**
     * @var Project
     *
     * @ORM\ManyToOne(targetEntity="Project", inversedBy="templates")
     * @ORM\JoinColumn(referencedColumnName="project_id", nullable=false)
     */
    private $projectId;

    /**
     * @var array|null
     *
     * @ORM\Column(type="simple_array", nullable=true)
     */
    private $userIds;

    // ...
}

Теперь я хочу получить все проекты. Но переменная templates каждого проекта должна иметь только те шаблоны, которые имеют либо userIds NULL, либо userId текущего пользователя.

В своем репозитории я уже пробовал следующее:

public function findForUser(int $userId): array
{
    $qb = $this->entityManager->createQueryBuilder();
    $qb->select('p')
       ->from(Project::class, 'p')
       ->leftJoin(Template::class, 't', Join::WITH, 't.project = p.projectId')
       ->where(
           $qb->expr()
              ->isNull('t.userIds')
       )
       ->orWhere('FIND_IN_SET(:userId, t.userIds) > 0');

    $qb->setParameter(':userId', $userId);

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

Но когда я вызываю getTemplates () , у меня все еще есть все шаблоны, даже те, которые пользователь не может видеть.

Я также попытался вместо ->select('p') использовать ->select('p, t') как Я надеялся, что doctrine тогда уже заполнит поле шаблонов выбранными шаблонами, но вместо этого вернет смешанный массив шаблонов и проектов.

1 Ответ

0 голосов
/ 09 марта 2020

Я думаю, что схема вашей сущности не совсем оптимизирована для такого рода операций. Вы не должны сохранять идентификаторы пользователей в простом массиве, но вместо этого используйте отношение ManyToMany:

class Template
{
    // ...

    /**
     * @var User[]|Collection
     *
     * @ORM\ManyToMany(targetEntity="User", inversedBy="templates")
     */
    private $users;
}

Тогда запрос dql должен выглядеть примерно так:

public function findForUser(User $user): array
{
    $qb = $this->entityManager->createQueryBuilder();
    $qb->select('p')
       ->from(Project::class, 'p')
       ->leftJoin(Template::class, 't', Join::WITH, 't.project = p.projectId')
       ->leftJoin(User::class, 'u', Join::WITH, 't.users = u.id')
       ->where('COUNT(t.users) = 0')
       ->orWhere('u = :user');

    $qb->setParameter(':user', $user);

    return $qb->getQuery()
              ->execute();
}
...