Doctrine2: วิธีที่ดีที่สุดในการจัดการแบบตัวต่อตัวกับคอลัมน์พิเศษในตารางอ้างอิง


282

ฉันสงสัยว่าอะไรดีที่สุดสะอาดและเป็นวิธีที่ง่ายที่สุดในการทำงานกับความสัมพันธ์แบบหลายต่อหลายคนใน Doctrine2

สมมติว่าเรามีอัลบั้มอย่างMaster of Puppetsโดย Metallica ที่มีหลายแทร็ก แต่โปรดทราบความจริงที่ว่าหนึ่งแทร็กอาจปรากฏในมากกว่าหนึ่งอัลบั้มเช่นBattery by Metallicaทำ - อัลบั้มที่สามมีเนื้อเรื่องนี้

ดังนั้นสิ่งที่ฉันต้องการคือความสัมพันธ์แบบหลายต่อหลายกลุ่มระหว่างอัลบั้มและแทร็กโดยใช้ตารางที่สามกับคอลัมน์เพิ่มเติมบางอย่าง (เช่นตำแหน่งของแทร็กในอัลบั้มที่ระบุ) ที่จริงฉันต้องใช้ตามเอกสารของ Doctrine แนะนำความสัมพันธ์แบบหนึ่งต่อหลายสองครั้งเพื่อให้บรรลุการทำงานนั้น

/** @Entity() */
class Album {
    /** @Id @Column(type="integer") */
    protected $id;

    /** @Column() */
    protected $title;

    /** @OneToMany(targetEntity="AlbumTrackReference", mappedBy="album") */
    protected $tracklist;

    public function __construct() {
        $this->tracklist = new \Doctrine\Common\Collections\ArrayCollection();
    }

    public function getTitle() {
        return $this->title;
    }

    public function getTracklist() {
        return $this->tracklist->toArray();
    }
}

/** @Entity() */
class Track {
    /** @Id @Column(type="integer") */
    protected $id;

    /** @Column() */
    protected $title;

    /** @Column(type="time") */
    protected $duration;

    /** @OneToMany(targetEntity="AlbumTrackReference", mappedBy="track") */
    protected $albumsFeaturingThisTrack; // btw: any idea how to name this relation? :)

    public function getTitle() {
        return $this->title;
    }

    public function getDuration() {
        return $this->duration;
    }
}

/** @Entity() */
class AlbumTrackReference {
    /** @Id @Column(type="integer") */
    protected $id;

    /** @ManyToOne(targetEntity="Album", inversedBy="tracklist") */
    protected $album;

    /** @ManyToOne(targetEntity="Track", inversedBy="albumsFeaturingThisTrack") */
    protected $track;

    /** @Column(type="integer") */
    protected $position;

    /** @Column(type="boolean") */
    protected $isPromoted;

    public function getPosition() {
        return $this->position;
    }

    public function isPromoted() {
        return $this->isPromoted;
    }

    public function getAlbum() {
        return $this->album;
    }

    public function getTrack() {
        return $this->track;
    }
}

ข้อมูลตัวอย่าง:

             Album
+----+--------------------------+
| id | title                    |
+----+--------------------------+
|  1 | Master of Puppets        |
|  2 | The Metallica Collection |
+----+--------------------------+

               Track
+----+----------------------+----------+
| id | title                | duration |
+----+----------------------+----------+
|  1 | Battery              | 00:05:13 |
|  2 | Nothing Else Matters | 00:06:29 |
|  3 | Damage Inc.          | 00:05:33 |
+----+----------------------+----------+

              AlbumTrackReference
+----+----------+----------+----------+------------+
| id | album_id | track_id | position | isPromoted |
+----+----------+----------+----------+------------+
|  1 |        1 |        2 |        2 |          1 |
|  2 |        1 |        3 |        1 |          0 |
|  3 |        1 |        1 |        3 |          0 |
|  4 |        2 |        2 |        1 |          0 |
+----+----------+----------+----------+------------+

ตอนนี้ฉันสามารถแสดงรายการอัลบั้มและเพลงที่เกี่ยวข้อง:

$dql = '
    SELECT   a, tl, t
    FROM     Entity\Album a
    JOIN     a.tracklist tl
    JOIN     tl.track t
    ORDER BY tl.position ASC
';

$albums = $em->createQuery($dql)->getResult();

foreach ($albums as $album) {
    echo $album->getTitle() . PHP_EOL;

    foreach ($album->getTracklist() as $track) {
        echo sprintf("\t#%d - %-20s (%s) %s\n", 
            $track->getPosition(),
            $track->getTrack()->getTitle(),
            $track->getTrack()->getDuration()->format('H:i:s'),
            $track->isPromoted() ? ' - PROMOTED!' : ''
        );
    }   
}

ผลลัพธ์คือสิ่งที่ฉันคาดหวังเช่น: รายการอัลบั้มที่มีแทร็กของพวกเขาตามลำดับที่เหมาะสม

The Metallica Collection
    #1 - Nothing Else Matters (00:06:29) 
Master of Puppets
    #1 - Damage Inc.          (00:05:33) 
    #2 - Nothing Else Matters (00:06:29)  - PROMOTED!
    #3 - Battery              (00:05:13) 

แล้วมีอะไรผิดปกติ?

รหัสนี้แสดงให้เห็นถึงสิ่งที่ผิด:

foreach ($album->getTracklist() as $track) {
    echo $track->getTrack()->getTitle();
}

Album::getTracklist()ส่งคืนอาร์เรย์ของAlbumTrackReferenceวัตถุแทนTrackวัตถุ ฉันไม่สามารถสร้างเมธอดพร็อกซีทำให้เกิดอะไรขึ้นถ้าทั้งคู่AlbumและTrackจะมีgetTitle()เมธอดหรือไม่ ฉันสามารถทำการประมวลผลเพิ่มเติมภายในAlbum::getTracklist()วิธีการได้ แต่วิธีที่ง่ายที่สุดในการทำเช่นนั้นคืออะไร ฉันถูกบังคับอย่าเขียนอะไรอย่างนั้นหรือ

public function getTracklist() {
    $tracklist = array();

    foreach ($this->tracklist as $key => $trackReference) {
        $tracklist[$key] = $trackReference->getTrack();

        $tracklist[$key]->setPosition($trackReference->getPosition());
        $tracklist[$key]->setPromoted($trackReference->isPromoted());
    }

    return $tracklist;
}

// And some extra getters/setters in Track class

แก้ไข

@beberlei แนะนำให้ใช้วิธีพร็อกซี:

class AlbumTrackReference {
    public function getTitle() {
        return $this->getTrack()->getTitle()
    }
}

นั่นเป็นความคิดที่ดี แต่ฉันใช้ "การอ้างอิงวัตถุ" จากทั้งสองด้าน: $album->getTracklist()[12]->getTitle()และ$track->getAlbums()[1]->getTitle()ดังนั้นgetTitle()วิธีการควรกลับข้อมูลที่แตกต่างกันตามบริบทของการภาวนา

ฉันจะต้องทำสิ่งที่ชอบ:

 getTracklist() {
     foreach ($this->tracklist as $trackRef) { $trackRef->setContext($this); }
 }

 // ....

 getAlbums() {
     foreach ($this->tracklist as $trackRef) { $trackRef->setContext($this); }
 }

 // ...

 AlbumTrackRef::getTitle() {
      return $this->{$this->context}->getTitle();
 }

และนั่นไม่ใช่วิธีที่สะอาดมาก


2
คุณจัดการกับ AlbumTrackReference อย่างไร ตัวอย่างเช่น $ album-> addTrack () หรือ $ album-> removeTrack ()?
Daniel

ฉันไม่เข้าใจคุณแสดงความคิดเห็นเกี่ยวกับบริบท ในตัวเลือกของฉันข้อมูลไม่ได้ขึ้นอยู่กับบริบท About $album->getTracklist()[12]คือ AlbumTrackRefวัตถุดังนั้น$album->getTracklist()[12]->getTitle()จะส่งคืนชื่อของแทร็กเสมอ (หากคุณใช้วิธีพร็อกซี) ในขณะที่$track->getAlbums()[1]เป็นAlbumวัตถุดังนั้น$track->getAlbums()[1]->getTitle()จะกลับชื่อของอัลบั้มเสมอ
Vinícius Fagundes

ความคิดก็คือการใช้ในAlbumTrackReferenceสองวิธีพร็อกซี่และgetTrackTitle() getAlbumTitle
Vinícius Fagundes

คำตอบ:


158

ฉันได้เปิดคำถามที่คล้ายกันในรายชื่อผู้รับจดหมายของผู้ใช้ Doctrine และได้รับคำตอบที่ง่ายมาก

พิจารณาความสัมพันธ์แบบหลายต่อหลายครั้งเป็นเอนทิตี้ของตัวเองแล้วคุณจะรู้ว่าคุณมี 3 วัตถุเชื่อมโยงระหว่างพวกเขากับความสัมพันธ์แบบหนึ่งต่อหลายและแบบหนึ่งต่อหนึ่ง

http://groups.google.com/group/doctrine-user/browse_thread/thread/d1d87c96052e76f7/436b896e83c10868#436b896e83c10868

เมื่อความสัมพันธ์มีข้อมูลก็ไม่มีความสัมพันธ์อีกต่อไป!


ไม่มีใครรู้ว่าฉันจะได้รับเครื่องมือบรรทัดคำสั่ง doctrine เพื่อสร้างเอนทิตีใหม่นี้เป็นไฟล์สกีมา yml ได้อย่างไร คำสั่งนี้: app/console doctrine:mapping:import AppBundle ymlยังคงสร้างความสัมพันธ์หลายอย่างหลายอย่างสำหรับสองตารางดั้งเดิมและเพียงละเว้นตารางที่สามแทนการทำให้เป็นนิติบุคคล:/
Stphane

foreach ($album->getTracklist() as $track) { echo $track->getTrack()->getTitle(); }@Crozin ให้บริการแตกต่างกันconsider the relationship as an entityอย่างไร ฉันคิดว่าสิ่งที่เขาต้องการถามคือทำอย่างไรจึงจะข้ามเอนทิตี้สัมพันธ์และดึงชื่อเพลงโดยใช้foreach ($album->getTracklist() as $track) { echo $track->getTitle(); }
แพนด้า

6
"เมื่อความสัมพันธ์มีข้อมูลก็ไม่มีความสัมพันธ์อีกต่อไป" นี่เป็นการตรัสรู้อย่างแท้จริง ฉันไม่สามารถคิดถึงความสัมพันธ์จากมุมมองเอนทิตี!
หัวหอม

จะเกิดอะไรขึ้นถ้าความสัมพันธ์นั้นถูกสร้างขึ้นและถูกใช้เป็นหลาย ๆ อย่าง เราตระหนักว่าเราจำเป็นต้องมีเขตข้อมูลพิเศษในหลาย ๆ สาขาของเราดังนั้นเราจึงสร้างเอนทิตีที่แตกต่างกัน ปัญหาคือด้วยข้อมูลที่มีอยู่และตารางที่มีอยู่ซึ่งมีชื่อเหมือนกันดูเหมือนจะไม่ต้องการเป็นเพื่อน มีใครเคยลองใช้มาก่อนหรือไม่
ปกครองแบบเผด็จการ

สำหรับผู้ที่สงสัยว่า: การสร้างเอนทิตีที่มีร่วมกันหลายต่อหลายคน (ตามที่มีอยู่แล้ว) เป็นตารางทำงานอย่างไรก็ตามเอนทิตีที่ถือหลายต่อหลายคนจะต้องปรับให้เข้ากับกิจการใหม่แทน ยังอินเตอร์เฟสกับภายนอก (getters / setters สำหรับหลายต่อหลายคน) มักจะต้องมีการปรับเปลี่ยน
จากุมิ

17

จาก $ album-> getTrackList () คุณจะได้รับเอนทิตี "AlbumTrackReference" กลับมาดังนั้นการเพิ่มเมธอดจาก Track และ proxy

class AlbumTrackReference
{
    public function getTitle()
    {
        return $this->getTrack()->getTitle();
    }

    public function getDuration()
    {
        return $this->getTrack()->getDuration();
    }
}

วิธีนี้ทำให้การวนซ้ำของคุณง่ายขึ้นมากเช่นเดียวกับรหัสอื่น ๆ ทั้งหมดที่เกี่ยวข้องกับการวนลูปแทร็กของอัลบั้มเนื่องจากวิธีการทั้งหมดเป็นเพียงการพร็อกซีภายใน AlbumTrakcReference:

foreach ($album->getTracklist() as $track) {
    echo sprintf("\t#%d - %-20s (%s) %s\n", 
        $track->getPosition(),
        $track->getTitle(),
        $track->getDuration()->format('H:i:s'),
        $track->isPromoted() ? ' - PROMOTED!' : ''
    );
}

Btw คุณควรเปลี่ยนชื่อ AlbumTrackReference (เช่น "AlbumTrack") เห็นได้ชัดว่ามันไม่ได้เป็นเพียงการอ้างอิง แต่มีตรรกะเพิ่มเติม เนื่องจากอาจมีแทร็กที่ไม่ได้เชื่อมต่อกับอัลบั้ม แต่มีให้ผ่านทาง promo-cd หรือบางสิ่งบางอย่างที่ช่วยให้แยกได้ชัดเจนยิ่งขึ้น


1
วิธีการมอบฉันทะไม่สามารถแก้ปัญหาได้ 100% (ตรวจสอบการแก้ไขของฉัน) Btw You should rename the AlbumT(...)- ข้อดี
Crozin

3
ทำไมคุณไม่มีสองวิธี getAlbumTitle () และ getTrackTitle () บนวัตถุ AlbumTrackReference พร็อกซีทั้งสองไปยัง subobjects ที่เกี่ยวข้อง
beberlei

เป้าหมายคือAPI วัตถุที่เป็นธรรมชาติที่สุด $album->getTracklist()[1]->getTrackTitle()เป็นสิ่งที่ดี / $album->getTracklist()[1]->getTrack()->getTitle()ไม่ดีเท่า อย่างไรก็ตามดูเหมือนว่าฉันจะต้องมีคลาสที่แตกต่างกันสองคลาส: อันหนึ่งสำหรับอัลบั้ม -> เพลงอ้างอิงและอีกเพลงหนึ่งสำหรับเพลง -> อัลบั้มอ้างอิง - และมันยากเกินไปที่จะนำไปใช้ ดังนั้นอาจจะว่าทางออกที่ดีที่สุดเพื่อให้ห่างไกล ...
Crozin

13

ไม่มีอะไรเต้นเป็นตัวอย่างที่ดี

สำหรับผู้ที่กำลังมองหาตัวอย่างรหัสสะอาดของความสัมพันธ์แบบหนึ่งต่อหลาย / ต่อกลุ่มระหว่าง 3 คลาสที่เข้าร่วมเพื่อจัดเก็บแอตทริบิวต์เพิ่มเติมในความสัมพันธ์ลองดูที่เว็บไซต์นี้:

ตัวอย่างที่ดีของความสัมพันธ์แบบหนึ่งต่อหลาย / หลายกลุ่มระหว่าง 3 คลาสที่เข้าร่วม

คิดถึงกุญแจหลักของคุณ

ลองคิดถึงคีย์หลักของคุณด้วย คุณมักจะสามารถใช้คีย์ผสมเพื่อความสัมพันธ์แบบนี้ หลักคำสอนสนับสนุนสิ่งนี้โดยกำเนิด คุณสามารถทำให้เอนทิตีที่อ้างอิงของคุณเป็นรหัส ตรวจสอบเอกสารประกอบของคีย์ผสมที่นี่


10

ฉันคิดว่าฉันจะไปกับคำแนะนำของ @ beberlei เกี่ยวกับการใช้วิธีพร็อกซี สิ่งที่คุณสามารถทำได้เพื่อทำให้กระบวนการนี้ง่ายขึ้นคือการกำหนดอินเตอร์เฟสสองอินเตอร์เฟส:

interface AlbumInterface {
    public function getAlbumTitle();
    public function getTracklist();
}

interface TrackInterface {
    public function getTrackTitle();
    public function getTrackDuration();
}

จากนั้นทั้งของคุณAlbumและของคุณTrackสามารถใช้งานได้ในขณะที่AlbumTrackReferenceยังสามารถใช้งานได้ทั้งสองอย่างดังต่อไปนี้:

class Album implements AlbumInterface {
    // implementation
}

class Track implements TrackInterface {
    // implementation
}

/** @Entity whatever */
class AlbumTrackReference implements AlbumInterface, TrackInterface
{
    public function getTrackTitle()
    {
        return $this->track->getTrackTitle();
    }

    public function getTrackDuration()
    {
        return $this->track->getTrackDuration();
    }

    public function getAlbumTitle()
    {
        return $this->album->getAlbumTitle();
    }

    public function getTrackList()
    {
        return $this->album->getTrackList();
    }
}

ด้วยวิธีนี้โดยการลบตรรกะของคุณที่อ้างอิงโดยตรงTrackหรือหรือAlbumและแทนที่มันเพื่อที่จะใช้TrackInterfaceหรือAlbumInterfaceคุณจะได้ใช้AlbumTrackReferenceในกรณีที่เป็นไปได้ใด ๆ สิ่งที่คุณจะต้องมีคือแยกความแตกต่างระหว่างวิธีการเชื่อมต่อเล็กน้อย

สิ่งนี้จะไม่แยกความแตกต่าง DQL หรือตรรกะ Repository แต่บริการของคุณจะไม่สนใจความจริงที่ว่าคุณกำลังผ่านAlbumหรือAlbumTrackReferenceหรือTrackหรือAlbumTrackReferenceเพราะคุณซ่อนทุกอย่างไว้ด้านหลังอินเตอร์เฟส :)

หวังว่านี่จะช่วยได้!


7

ก่อนอื่นฉันเห็นด้วยกับคำแนะนำของเขาเป็นส่วนใหญ่ อย่างไรก็ตามคุณอาจออกแบบตัวเองให้เป็นกับดัก โดเมนของคุณกำลังพิจารณาว่าชื่อเป็นคีย์ธรรมชาติสำหรับแทร็กซึ่งน่าจะเป็นกรณีที่ 99% ของสถานการณ์ที่คุณเจอ แต่สิ่งที่ถ้าแบตเตอรี่ในต้นแบบของหุ่นเป็นรุ่นที่แตกต่างกัน (ความยาวแตกต่างกันสดอะคูสติกเรียบเรียงมาสเตอร์ ฯลฯ ) กว่ารุ่นบนเมทัลลิคอลเลกชัน

ขึ้นอยู่กับวิธีที่คุณต้องการจัดการ (หรือเพิกเฉย) ในกรณีนี้คุณสามารถไปตามเส้นทางที่แนะนำของ beberlei หรือไปกับตรรกะเพิ่มเติมที่เสนอใน Album :: getTracklist () โดยส่วนตัวแล้วฉันคิดว่าเหตุผลเพิ่มเติมนั้นมีเหตุผลที่จะทำให้ API ของคุณสะอาด แต่ทั้งคู่ก็มีข้อดี

หากคุณต้องการรองรับกรณีการใช้งานของฉันคุณอาจมีแทร็กที่มี OneToMany อ้างอิงด้วยตนเองไปยังแทร็กอื่น ๆ อาจเป็น $ SimilarTracks ในกรณีนี้จะมีสองหน่วยงานสำหรับการติดตามแบตเตอรี่หนึ่งสำหรับเมทัลลิคอลเลกชันและหนึ่งสำหรับปริญญาโทของหุ่น จากนั้นแต่ละแทร็คเอนทิตีที่คล้ายกันจะมีการอ้างอิงซึ่งกันและกัน นอกจากนี้ยังเป็นการกำจัดคลาส AlbumTrackReference ปัจจุบันและกำจัด "ปัญหา" ปัจจุบันของคุณ ฉันยอมรับว่ามันเป็นเพียงการย้ายความซับซ้อนไปยังจุดที่แตกต่างกัน แต่มันสามารถจัดการ usecase ที่ไม่สามารถทำได้ก่อนหน้านี้


6

คุณถามหา "วิธีที่ดีที่สุด" แต่ไม่มีวิธีที่ดีที่สุด มีหลายวิธีและคุณค้นพบแล้วบางส่วน วิธีที่คุณต้องการจัดการและ / หรือห่อหุ้มการจัดการการเชื่อมโยงเมื่อใช้คลาสการเชื่อมโยงนั้นขึ้นอยู่กับคุณและโดเมนที่เป็นรูปธรรมของคุณไม่มีใครสามารถแสดงให้คุณเห็น "วิธีที่ดีที่สุด" ฉันกลัว

นอกจากนั้นคำถามสามารถทำให้ง่ายขึ้นมากโดยการลบหลักคำสอนและฐานข้อมูลเชิงสัมพันธ์ออกจากสมการ สาระสำคัญของคำถามของคุณทำให้เกิดคำถามเกี่ยวกับวิธีจัดการกับคลาสความสัมพันธ์ใน OOP แบบธรรมดา


6

ฉันได้รับจากความขัดแย้งกับตารางการเข้าร่วมที่กำหนดไว้ในคลาสการเชื่อมโยง (ที่มีฟิลด์เพิ่มเติมที่กำหนดเอง) และการเพิ่มตารางการเข้าร่วมที่กำหนดไว้ในคำอธิบายประกอบแบบหลายต่อหลายคน

คำจำกัดความการแมปในสองเอนทิตีที่มีความสัมพันธ์แบบหลายต่อหลายโดยตรงปรากฏว่ามีผลในการสร้างตารางการเข้าร่วมโดยอัตโนมัติโดยใช้คำอธิบายประกอบ 'joinTable' อย่างไรก็ตามตารางการเข้าร่วมได้ถูกกำหนดไว้แล้วโดยการเพิ่มความคิดเห็นในคลาสเอนทิตีพื้นฐานและฉันต้องการให้ใช้นิยามฟิลด์ของคลาสเอนทิตีของความสัมพันธ์นี้เพื่อขยายตารางการเข้าร่วมด้วยฟิลด์ที่กำหนดเองเพิ่มเติม

คำอธิบายและวิธีแก้ไขคือสิ่งที่ระบุโดย FMaz008 ด้านบน ในสถานการณ์ของฉันต้องขอบคุณโพสต์นี้ในฟอรัม ' คำถามคำอธิบายประกอบหลักคำสอน ' โพสต์นี้ดึงความสนใจไปที่เอกสารหลักคำสอนเกี่ยวกับความสัมพันธ์ ManyToMany ทิศทางเดียว ดูหมายเหตุเกี่ยวกับวิธีการใช้ 'การเชื่อมโยงคลาสเอนทิตี้ของ' ดังนั้นแทนที่การแม็พหมายเหตุประกอบแบบหลายต่อหลายโดยตรงระหว่างคลาสเอนทิตีหลักสองคลาสด้วยคำอธิบายประกอบแบบหนึ่งต่อกลุ่มในคลาสเอนทิตีหลักและสอง -one 'หมายเหตุประกอบในคลาสเอนทิตีที่เชื่อมโยง มีตัวอย่างที่ให้ไว้ในฟอรั่มโพสต์นี้เป็นรุ่นที่สมาคมกับเขตพิเศษ :

public class Person {

  /** @OneToMany(targetEntity="AssignedItems", mappedBy="person") */
  private $assignedItems;

}

public class Items {

    /** @OneToMany(targetEntity="AssignedItems", mappedBy="item") */
    private $assignedPeople;
}

public class AssignedItems {

    /** @ManyToOne(targetEntity="Person")
    * @JoinColumn(name="person_id", referencedColumnName="id")
    */
private $person;

    /** @ManyToOne(targetEntity="Item")
    * @JoinColumn(name="item_id", referencedColumnName="id")
    */
private $item;

}

3

ตัวอย่างที่มีประโยชน์จริงๆ มันขาดในเอกสารหลักคำสอน 2

ขอบคุณมาก.

สำหรับฟังก์ชั่นพร็อกซี่สามารถทำได้:

class AlbumTrack extends AlbumTrackAbstract {
   ... proxy method.
   function getTitle() {} 
}

class TrackAlbum extends AlbumTrackAbstract {
   ... proxy method.
   function getTitle() {}
}

class AlbumTrackAbstract {
   private $id;
   ....
}

และ

/** @OneToMany(targetEntity="TrackAlbum", mappedBy="album") */
protected $tracklist;

/** @OneToMany(targetEntity="AlbumTrack", mappedBy="track") */
protected $albumsFeaturingThisTrack;

3

สิ่งที่คุณอ้างถึงคือข้อมูลเมตาข้อมูลเกี่ยวกับข้อมูล ฉันมีปัญหาเดียวกันนี้สำหรับโครงการที่ฉันกำลังทำงานอยู่และต้องใช้เวลาพยายามคิดออก มันมีข้อมูลมากเกินไปที่จะโพสต์ที่นี่ แต่ด้านล่างนี้เป็นลิงก์สองลิงก์ที่คุณอาจพบว่ามีประโยชน์ พวกเขาอ้างอิงกรอบงาน Symfony แต่ขึ้นอยู่กับหลักคำสอน ORM

http://melikedev.com/2010/04/06/symfony-saving-metadata-during-form-save-sort-ids/

http://melikedev.com/2009/12/09/symfony-w-doctrine-saving-many-to-many-mm-relationships/

ขอให้โชคดีและการอ้างอิง Metallica ที่ดี!


3

การแก้ปัญหาอยู่ในเอกสารของหลักคำสอน ในคำถามที่พบบ่อยคุณสามารถดูสิ่งนี้:

http://docs.doctrine-project.org/en/2.1/reference/faq.html#how-can-i-add-columns-to-a-many-to-many-table

และการสอนอยู่ที่นี่:

http://docs.doctrine-project.org/en/2.1/tutorials/composite-primary-keys.html

ดังนั้นคุณจะไม่ทำอีกต่อไปmanyToManyแต่คุณต้องสร้างเอนทิตีพิเศษและใส่เอนทิตีmanyToOneทั้งสองของคุณ

เพิ่มสำหรับความคิดเห็น @ f00bar:

มันง่ายคุณต้องทำสิ่งนี้:

Article  1--N  ArticleTag  N--1  Tag

ดังนั้นคุณสร้างนิติบุคคลแท็ก

ArticleTag:
  type: entity
  id:
    id:
      type: integer
      generator:
        strategy: AUTO
  manyToOne:
    article:
      targetEntity: Article
      inversedBy: articleTags
  fields: 
    # your extra fields here
  manyToOne:
    tag:
      targetEntity: Tag
      inversedBy: articleTags

ฉันหวังว่ามันจะช่วย



นั่นคือสิ่งที่ฉันกำลังมองหา exacly ขอบคุณ! น่าเสียดายที่ไม่มีตัวอย่าง yml สำหรับกรณีใช้งานครั้งที่สาม! :(ทุกคนสามารถแบ่งปันกรณีการใช้ที่สามโดยใช้รูปแบบ yml ได้หรือไม่ ฉันอยากจะชื่นชมจริงๆ:#
Stphane

ฉันได้เพิ่มไว้ในคำตอบกรณีของคุณ;)
Mirza Selimovic

มันไม่ถูกต้อง เอนทิตีไม่จำเป็นต้องอยู่กับ id (id) AUTO นั่นเป็นสิ่งที่ผิดฉันกำลังพยายามสร้างตัวอย่างที่ถูกต้อง
Gatunox

ฉันจะโพสต์คำตอบใหม่ที่จะได้รับถ้ารูปแบบถูกต้อง
Gatunox

3

ทิศทางเดียว เพียงเพิ่ม inversedBy: (ชื่อคอลัมน์ต่างประเทศ) เพื่อทำให้เป็นแบบสองทิศทาง

# config/yaml/ProductStore.dcm.yml
ProductStore:
  type: entity
  id:
    product:
      associationKey: true
    store:
      associationKey: true
  fields:
    status:
      type: integer(1)
    createdAt:
      type: datetime
    updatedAt:
      type: datetime
  manyToOne:
    product:
      targetEntity: Product
      joinColumn:
        name: product_id
        referencedColumnName: id
    store:
      targetEntity: Store
      joinColumn:
        name: store_id
        referencedColumnName: id

ฉันหวังว่ามันจะช่วย แล้วพบกันใหม่.


2

คุณอาจจะสามารถบรรลุสิ่งที่คุณต้องการด้วยการสืบทอดคลาสของตารางที่คุณเปลี่ยน AlbumTrackReference เป็น AlbumTrack:

class AlbumTrack extends Track { /* ... */ }

และgetTrackList()จะมีAlbumTrackวัตถุที่คุณสามารถใช้งานได้ตามที่คุณต้องการ:

foreach($album->getTrackList() as $albumTrack)
{
    echo sprintf("\t#%d - %-20s (%s) %s\n", 
        $albumTrack->getPosition(),
        $albumTrack->getTitle(),
        $albumTrack->getDuration()->format('H:i:s'),
        $albumTrack->isPromoted() ? ' - PROMOTED!' : ''
    );
}

คุณจะต้องตรวจสอบสิ่งนี้อย่างถี่ถ้วนเพื่อให้แน่ใจว่าคุณจะไม่ได้รับประสิทธิภาพที่ดี

การตั้งค่าปัจจุบันของคุณนั้นเรียบง่ายมีประสิทธิภาพและเข้าใจง่ายแม้ว่าความหมายบางอย่างอาจไม่เหมาะกับคุณ


0

ในขณะที่ได้รับแบบฟอร์มแทร็กอัลบั้มทั้งหมดภายในชั้นอัลบั้มคุณจะต้องสร้างอีกหนึ่งข้อความค้นหาสำหรับอีกหนึ่งระเบียน นั่นเป็นเพราะวิธีการมอบฉันทะ มีอีกตัวอย่างของรหัสของฉัน (ดูโพสต์ล่าสุดในหัวข้อ): http://groups.google.com/group/doctrine-user/browse_thread/thread/d1d87c96052e76f7/436b896e83c10868#436b896e83c10868

มีวิธีอื่นในการแก้ไขไหม? การเข้าร่วมเป็นทางออกที่ดีกว่าไม่ใช่หรือ


1
ในขณะที่สิ่งนี้อาจตอบคำถามในทางทฤษฎีมันก็ควรที่จะรวมส่วนสำคัญของคำตอบที่นี่และให้ลิงค์สำหรับการอ้างอิง
Spontifixus

0

นี่คือวิธีแก้ปัญหาตามที่อธิบายไว้ในเอกสารประกอบ Doctrine2

<?php
use Doctrine\Common\Collections\ArrayCollection;

/** @Entity */
class Order
{
    /** @Id @Column(type="integer") @GeneratedValue */
    private $id;

    /** @ManyToOne(targetEntity="Customer") */
    private $customer;
    /** @OneToMany(targetEntity="OrderItem", mappedBy="order") */
    private $items;

    /** @Column(type="boolean") */
    private $payed = false;
    /** @Column(type="boolean") */
    private $shipped = false;
    /** @Column(type="datetime") */
    private $created;

    public function __construct(Customer $customer)
    {
        $this->customer = $customer;
        $this->items = new ArrayCollection();
        $this->created = new \DateTime("now");
    }
}

/** @Entity */
class Product
{
    /** @Id @Column(type="integer") @GeneratedValue */
    private $id;

    /** @Column(type="string") */
    private $name;

    /** @Column(type="decimal") */
    private $currentPrice;

    public function getCurrentPrice()
    {
        return $this->currentPrice;
    }
}

/** @Entity */
class OrderItem
{
    /** @Id @ManyToOne(targetEntity="Order") */
    private $order;

    /** @Id @ManyToOne(targetEntity="Product") */
    private $product;

    /** @Column(type="integer") */
    private $amount = 1;

    /** @Column(type="decimal") */
    private $offeredPrice;

    public function __construct(Order $order, Product $product, $amount = 1)
    {
        $this->order = $order;
        $this->product = $product;
        $this->offeredPrice = $product->getCurrentPrice();
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.