Doctrine2:为ManytoMany创建没有反向的查询

时间:2013-08-26 12:04:27

标签: symfony doctrine-orm

我是Doctrine的新手,并且很难在Symfony2中找到如何使用Doctrine2编写下面的查询,这样就可以得到一个User对象列表。

查询说明:教师必须获取分配给他被分配为教师的课程的用户列表

Select * from fos_user_user as user
    LEFT JOIN course_assigned_students as cas ON cas.student_id = user.id
    WHERE cas.course_id IN 
        (SELECT cat.course_id from course_assigned_teachers where teachers_id = 1) 
    GROUP BY user.id

或类似的

Select * from fos_user_user as user
        LEFT JOIN course_assigned_students as cas ON cas.student_id = user.id
        LEFT JOIN course_assigned_teachers as cat ON cat.course_id = cas.course_id
    WHERE cat.teachers_id = 1
    GROUP BY user.id

表:

fos_user_user: id
course_assigned_students: student_id, course_id
course_assigned_teachers: teachers_id, course_id
course: id

课程实体

/**
 * @var User $teachers
 *
 * @ORM\ManyToMany(targetEntity="Application\Sonata\UserBundle\Entity\User")
 * @ORM\JoinTable(name="course_assigned_teachers",
 *      joinColumns={@ORM\JoinColumn(name="course_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="teachers_id", referencedColumnName="id")}
 * )
 */

protected $teachers;

/**
 * @var User $students
 *
 * @ORM\ManyToMany(targetEntity="Application\Sonata\UserBundle\Entity\User")
 * @ORM\JoinTable(name="course_assigned_students",
 *      joinColumns={@ORM\JoinColumn(name="course_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="student_id", referencedColumnName="id")}
 * )
 */
protected $students;

我的问题是我不希望我的用户实体引用课程,因为并非每个应用程序都会使用CourseBundle。

我是否必须创建代表连接表的CourseAssignedStudents和CourseAssignedTeachers实体?

所以我可以这样做:

$users = $this->getDoctrine()->getEntityManager()
        ->createQuery('SELECT user FROM ApplicationSonataUserBundle:User user 
                    LEFT JOIN CourseBundle:CourseAssignedStudents cas WITH cas.student_id = user.id
                    WHERE cas.course_id IN (SELECT cat.course_id FROM CourseBundle:CourseAssignedTeachers cat where cat.teachers_id = :uid) 
                    GROUP BY user.id')
         ->setParameter('uid', $this->getUser()->getId())
         ->execute();

1 个答案:

答案 0 :(得分:2)

不幸的是,尽管在某些情况下它确实倾向于继续工作,但是应该始终定义关系的两个方面 - 如果它发现它们不是,那么你会在Doctrine控制台中收到警告。

  

双向关系的拥有方必须参考它   通过使用OneToOne的inversedBy属性反面,   ManyToOne,或ManyToMany映射声明。 inversedBy属性   指定实体中与其相反的字段   关系。

http://docs.doctrine-project.org/en/2.0.x/reference/association-mapping.html#owning-side-and-inverse-side

不过我会说你提出的问题是一个更大的问题,但有一些解决方案:

  

我的问题是我不希望我的用户实体有引用   当然,因为并非每个应用程序都会使用   CourseBundle。

是的,这正是您的通用用户实体根本不应该与课程相关的原因。如果你想要真正可重复使用的软件包(你应该),你需要采取不同的策略,允许你抽象这样的关系。

假设您有一个UserBundle,其中包含一个User实体,这应该只与您打算在所有应用程序中出现的实体直接相关,可能没有很多,所以你可以期待它作为用户,名称,用户名,密码,电子邮件等的轻量级描述。

对于您希望添加其他关系的特定应用程序,您可以使用以下一个或多个概念来实现此目的,虽然在某些方面有点麻烦,但应该在结构上改进您的各种代码库,以及让您实现最终目标。

  • 捆绑/对象继承 - 您需要扩展核心实体类,以便添加其他列和关系注释,属性和方法,可以在同名的项目命名空间中执行此操作,例如ProjectName/UserBundle扩展CompanyName/UserBundle或者更合适ProjectName/AppBundleProjectName/SchoolBundle,这更像是我根据您打算将您的实体与您的实体联系起来的设计选择。

  • 单表继承 - 这会在数据库表中添加一个鉴别器列字段,用于描述特定行所代表的实体,并且Doctrine将相应地保存正确的对象类型。鉴别器映射的缺点是映射是在父类上定义的,这将涉及在父用户包中描述您的课程oritentated 用户实体 - 我解决了这个问题,代码来自{{3 - 意味着你的父类只需要将自己声明为映射的鉴别器,并将其自身作为可能的映射之一;这意味着它可以被扩展或单独使用。

    this Gist, which allows you to define these mappings in parameters

  • 解析目标实体 - 我对此的经验较少,但它可能会满足您的需求,我认为在某些情况下甚至可能需要使用鉴别器映射。简而言之,它涉及使用接口类(或我认为的父类)为您的模型定义关系,Doctrine将这些接口解析为适当的实体类。

    http://docs.doctrine-project.org/en/2.0.x/reference/inheritance-mapping.html#single-table-inheritance

这些都是非常重要的架构决策,说实话,需要一些玩法来了解他们的所有内容,以及如何最好地满足您的特定需求。单表继承可能是让您了解的主要概念,以及如何以合理的方式跨应用程序中的bundle进行。