DQL для присоединения к свойству свойства

Я пытаюсь создать запрос DQL, проверяя свойство свойства. Цель состоит в том, чтобы создать запрос DQL, который будет указывать, в какой роли пользователь выполняет задание, не возвращая строк, если у пользователя нет надлежащей роли.

Моими сущностями являются Роль, Работа и Пользователь. У пользователей есть роли, заданиям требуется роль (для их выполнения), а у ролей есть «заместители», которые связаны с другой ролью, которая может заменить эту роль.

Заглушенные версии сущностей выглядят так:

class User {
    //Annotation not needed for question 
    protected $id;

    /**
      * @var SystemBundle\Entity\Role
      *
      * @ORM\ManyToMany(targetEntity="SystemBundle\Entity\Role")
      * @ORM\JoinTable(name="User_User__Role",
      *   joinColumns={
      *     @ORM\JoinColumn(name="User_ID", referencedColumnName="ID")
      *   },
      *   inverseJoinColumns={
      *     @ORM\JoinColumn(name="Role_ID", referencedColumnName="ID")
      *   }
      * )
      */
     protected $roles;
}

class Job {
    //Annotation not needed for question 
    protected $id;

   /**
     * @var SystemBundle\Entity\Role $jobRole
     *
     * @ORM\ManyToOne(targetEntity="Role")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="Role_ID", referencedColumnName="ID")
     * })
     */
    protected $jobRole;
}

class Role {
    //Annotation not needed for question 
    protected $id;

   /**
     * @var SystemBundle\Entity\Role $jobRole
     *
     * @ORM\OneToOne(targetEntity="Role")
     * @ORM\JoinColumn(name="BackupRole_ID", referencedColumnName="ID")
     */
    protected $backUpRole;
}

Концептуальная логика состоит в том, чтобы объединить роли с заданиями, проверяя, является ли текущая роль job.JobRole или job.JobRole.backUpRole. Я пробовал это, и доктрине действительно не нравится job.jobRole.backUpRole, и она выдает эту ошибку во втором периоде:

[Syntax Error] line 0, col 210: Error: Expected =, <, <=, <>, >, >=, !=, got '.' 

Моя попытка DQL выглядит так

            SELECT r
            FROM SystemBundle:Role r
            INNER JOIN ProcessBundle:Job j
            WITH j = :j AND j.jobRole = r OR j.jobRole.backUpRole = r
            LEFT JOIN UserBundle:User u
            WITH r MEMBER OF u.roles
            WHERE u = :emp AND u IS NOT NULL 
            ORDER BY r.id

Я могу выполнить эту задачу с помощью чистого SQL, а также просто используя php для обхода ассоциаций, но я хочу оставаться верным использованию DQL (потому что это меня раздражает, и я хочу знать, можно ли это сделать).

Если это поможет, вот мой чистый SQL:

#get all roles
select r.*
from Sys_Role as r
inner join

#get the role assigned to the job
(select r.*
FROM Sys_Role as r
INNER JOIN Sys_Job as j
ON j.JobRole_ID = r.ID
WHERE j.ID = ?) as jr

#join roles based on the job-role's id or backup id
# this should filter the list of roles down to the role and backup role for the job
on ar.ID = aar.ID OR  ar.ID = aar.BackUpRole_ID

# and filter by the roles the user is assigned
INNER JOIN User_User__Role as uur
ON r.ID = uur.Role_ID 
WHERE uur.User_ID = ?

РЕДАКТИРОВАТЬ: Мои извинения. При редактировании макета вопроса я случайно удалил файл DQL. Я добавил это обратно в вопрос!

ОБНОВЛЕНИЕ: я изучаю возможность сделать $backUpRole двунаправленным самосоединением и атакую ​​с этого направления. Doctrine не любит наивных попыток, поэтому похоже, что если используется этот такт, нужен свежий DQL.

Изменение DQL на WITH j = :j AND j.jobRole = r OR j.jobRole = r.roleIBackUp приводит к новой ошибке: A single-valued association path expression to an inverse side is not supported in DQL queries. Use an explicit join instead.


person Fodagus    schedule 02.07.2013    source источник


Ответы (1)


Я понял. Это ВОЗМОЖНО... вам нужно присоединиться к подобъектам, прежде чем вы сможете их использовать. Мне пришлось изменить порядок соединений, и я не очень доволен тем, что мои фильтры находятся в предложениях WITH вместо WHERE, но с результатами трудно спорить!

            SELECT r
            FROM SystemBundle:Role r
            LEFT JOIN UserBundle:User u
            WITH u = :emp
            INNER JOIN ProcessBundle:Job j
            WITH j = :job
            JOIN j.jobRole jr
            JOIN jr.backUpRole jrbr
            WHERE (jr = ar OR jrbr = ar) AND r MEMBER OF u.role

Последние 2 соединения позволяют нам использовать псевдонимы для объектов, принадлежащих связанным сущностям, чтобы мы могли использовать их для сравнения.

«30 000-футовое представление» о том, как работает этот DQL, таково:

  • Роль Foreach
  • Эта роль принадлежит пользователю?
  • Получите роль для работы, которую мы хотим
  • Псевдоним этой роли
  • Псевдоним, который поддерживает резервные копии
  • Является ли эта роль ролью задания или ролью резервного копирования роли задания?

Немного странно, но довольно просто, как только вы это поймете.

person Fodagus    schedule 02.07.2013