我是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();
答案 0 :(得分:2)
不幸的是,尽管在某些情况下它确实倾向于继续工作,但是应该始终定义关系的两个方面 - 如果它发现它们不是,那么你会在Doctrine控制台中收到警告。
双向关系的拥有方必须参考它 通过使用OneToOne的inversedBy属性反面, ManyToOne,或ManyToMany映射声明。 inversedBy属性 指定实体中与其相反的字段 关系。
不过我会说你提出的问题是一个更大的问题,但有一些解决方案:
我的问题是我不希望我的用户实体有引用 当然,因为并非每个应用程序都会使用 CourseBundle。
是的,这正是您的通用用户实体根本不应该与课程相关的原因。如果你想要真正可重复使用的软件包(你应该),你需要采取不同的策略,允许你抽象这样的关系。
假设您有一个UserBundle,其中包含一个User实体,这应该只与您打算在所有应用程序中出现的实体直接相关,可能没有很多,所以你可以期待它作为用户,名称,用户名,密码,电子邮件等的轻量级描述。
对于您希望添加其他关系的特定应用程序,您可以使用以下一个或多个概念来实现此目的,虽然在某些方面有点麻烦,但应该在结构上改进您的各种代码库,以及让您实现最终目标。
捆绑/对象继承 - 您需要扩展核心实体类,以便添加其他列和关系注释,属性和方法,可以在同名的项目命名空间中执行此操作,例如ProjectName/UserBundle
扩展CompanyName/UserBundle
或者更合适ProjectName/AppBundle
或ProjectName/SchoolBundle
,这更像是我根据您打算将您的实体与您的实体联系起来的设计选择。
单表继承 - 这会在数据库表中添加一个鉴别器列字段,用于描述特定行所代表的实体,并且Doctrine将相应地保存正确的对象类型。鉴别器映射的缺点是映射是在父类上定义的,这将涉及在父用户包中描述您的课程oritentated 用户实体 - 我解决了这个问题,代码来自{{3 - 意味着你的父类只需要将自己声明为映射的鉴别器,并将其自身作为可能的映射之一;这意味着它可以被扩展或单独使用。
this Gist, which allows you to define these mappings in parameters
解析目标实体 - 我对此的经验较少,但它可能会满足您的需求,我认为在某些情况下甚至可能需要使用鉴别器映射。简而言之,它涉及使用接口类(或我认为的父类)为您的模型定义关系,Doctrine将这些接口解析为适当的实体类。
这些都是非常重要的架构决策,说实话,需要一些玩法来了解他们的所有内容,以及如何最好地满足您的特定需求。单表继承可能是让您了解的主要概念,以及如何以合理的方式跨应用程序中的bundle进行。