1

我是 Doctrine 的新手,并且很难弄清楚如何在 Symfony2 中使用 Doctrine2 编写下面的查询,这给了我一个用户对象列表作为结果。

查询描述:教师必须获得分配给他作为教师所分配的课程的用户列表

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;

我的问题是我不希望我的用户实体引用 Course,因为不是每个应用程序都会使用 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();
4

1 回答 1

2

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

双向关系的拥有方必须通过使用 OneToOne、ManyToOne 或 ManyToMany 映射声明的 inversedBy 属性来引用其反向方。inversedBy 属性指定实体中作为关系反面的字段。

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

但是,我想说的是,无论如何您提出的问题是一个更大的问题,但是有一些解决方案:

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

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

假设您有一个 UserBundle,其中包含一个 User 实体,这应该只与您打算在所有应用程序中出现的实体直接相关,这些实体可能并不多,因此您可以期望它是一个用户、姓名、用户名、密码、电子邮件等的轻量级描述。

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

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

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

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

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

    http://symfony.com/doc/current/cookbook/doctrine/resolve_target_entity.html

这些都是非常重要的架构决策,老实说,需要进行一些尝试才能了解它们的全部内容以及如何根据您的特定需求最好地做到这一点。单表继承可能是让您了解的主要概念,以及如何在应用程序中的捆绑包之间以合乎逻辑的方式进行。

于 2013-08-26T14:07:08.400 回答