2013-08-26 48 views
1

我是Doctrine的新手,很難弄清楚如何在Symfony2中使用Doctrine2編寫查詢,這給我一個用戶對象列表作爲結果。Doctrine2:爲ManytoMany創建查詢而不反面

查詢說明:老師必須把分配給他被分配到作爲教師

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(); 

回答

2

不幸的是,雖然它往往進行在某些情況下,反正工作,這種關係的雙方應始終定義 - 你得到的學說警告如果它看到它們不是,那麼它就是控制檯

的雙向關係的持有端必須通過使用OneToOne, 多對一,或多對多映射聲明的inversedBy屬性的引用其 逆側。 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,這是更多的設計選擇,我會根據你打算與你的實體相關的東西來衡量。

  • 單個表的傳承 - 這增加了一個鑑別列字段到描述特定行表示實體數據庫表,和學說將相應水合正確的對象類型。鑑別器映射的缺點是映射是在父類上定義的,這將涉及在你的父用戶包中描述你的課程oritentated用戶實體 - 我用來自this Gist, which allows you to define these mappings in parameters的代碼解決了這個問題 - 這意味着你的父類只需要將自己聲明爲鑑別符映射,並將其本身作爲可能的映射之一;這意味着它可以擴展或孤立使用。

    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

這些都是相當顯著的架構決策,並坦率地說,需要一些玩弄着得到感受他們是怎麼一回事,如何做到這一點最適合您的特定需求。單表繼承可能是讓你頭腦清醒的主要概念,以及如何在你的應用程序中以邏輯方式實現它。

+0

快速瀏覽之後,如果您擁有不同類型的共享一些基本值的用戶,那麼它看起來像Single table inheritence是一條路。在這種情況下,我的CourseBundle具有課程,作業,作業,課程等實體完全不同。所以我想這不是這種特殊情況的方法。
解決目標實體聽起來很有趣,但我有更多與用戶有關係的實體,這可能是一個問題
給我留下了很好的舊Bundle/Object繼承。將進一步挖掘並稍後再回來。一個深深的鞠躬響應btw – Thyreen

+0

我認爲解決目標實體解決一些問題時,做某些事情與形式或查詢(我真的不記得),我相信如果你設置一個參數,你重寫你的子實體fqcn,並將其用於鑑別器映射和作爲以父類爲關鍵字的解決目標實體,涵蓋所有基礎並確保跨應用程序的一致性。 – Steve