Doctrine2: referans tablosundaki ekstra sütunlar ile çok-çok işlemek için en iyi yolu

0 Cevap php

Ben, en iyi ne temiz ve Doctrine2 içinde çok-çok ilişkileri ile çalışmak için en basit yolu merak ediyorum.

Üç albüm bu parça yer vardır - en biz yapar Metallica with several tracks. But please note the fact that one track might appears in more that one album, like Battery Metallica Master of Puppets gibi bir albüm var olduğunu varsayalım.

Yani ne gerek (belirtilen albümündeki parçanın konumu gibi) bazı ek sütunlar ile üçüncü bir tablo kullanarak, albümler ve parçalar arasında çok-çok ilişkisi olduğunu. Doktrini belgelerine anlaşılacağı gibi, aslında ben, kullanmak için bu işlevselliği elde etmek için bir çift tek-çok ilişkisi var.

/** @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;
    }
}

Örnek veri:

             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 |
+----+----------+----------+----------+------------+

Şimdi ben onlara eşlik albüm ve parça listesini görüntüleyebilirsiniz:

$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!' : ''
        );
    }   
}

Sonuçlar, yani, bekliyorum ne: Bunların uygun sırayla parça ve terfi olarak işaretlenmiş terfi olanlarla albümlerin bir listesi.

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) 

So what's wrong?

Bu kod neyin yanlış olduğunu gösterir:

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

Album::getTracklist() AlbumTrackReference yerine Track nesnelerin nesneleri dizisi döndürür. Ben neden vekil yöntemleri oluşturmak değil hem ne varsa, Album ve Track getTitle() yöntemi olurdu? I Album::getTracklist() yöntemi içinde bazı ekstra işlem yapabilirdi ama bunu yapmak için en basit yolu nedir? Ben böyle bir şey yazmak do zorla muyum?

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

EDIT

@ Beberlei vekil yöntemleri kullanmak için önerilen:

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

Bu iyi bir fikir olurdu ama ben her iki taraftan bu "referans nesnesi" istimal: $album->getTracklist()[12]->getTitle() ve $track->getAlbums()[1]->getTitle(), yani getTitle() dayalı yöntemi farklı veri dönmesi gerekir çağırma bağlamında.

Ben böyle bir şey yapmak zorunda kalacaktı:

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

Ve bu çok temiz bir yol değil.

0 Cevap