Doctrine2에서 다 대다 관계로 작업하는 가장 좋고, 깨끗하고, 가장 간단한 방법이 무엇인지 궁금합니다.
Metallica의 Master of Puppets 와 같은 앨범 에 여러 트랙 이 있다고 가정 해 봅시다 . 그러나 Battery by Metallica 처럼 하나의 트랙이 하나 이상의 앨범에 나타날 수 있다는 사실에 유의하십시오 .이 트랙에는 3 개의 앨범이 있습니다.
그래서 필요한 것은 앨범과 트랙 사이의 다 대다 관계이며, 지정된 열의 트랙 위치와 같은 몇 가지 추가 열이있는 세 번째 테이블을 사용합니다. 실제로 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();
}
그리고 그것은 매우 깨끗한 방법이 아닙니다.
$album->getTracklist()[12]
는 AlbumTrackRef
객체이므로 $album->getTracklist()[12]->getTitle()
항상 트랙의 제목을 반환합니다 (프록시 방법을 사용하는 경우). 동안 $track->getAlbums()[1]
인 Album
목적은, 그래서 $track->getAlbums()[1]->getTitle()
항상 앨범의 제목을 반환합니다.
AlbumTrackReference
두 개의 프록시 방법, getTrackTitle()
및 getAlbumTitle
.