मैं सोच रहा हूं कि Doctrine2 में कई-से-कई संबंधों के साथ काम करने का सबसे अच्छा, सबसे साफ और सबसे सरल तरीका क्या है।
मान लेते हैं कि हमें मेटालिका द्वारा मास्टर ऑफ़ पपेट्स जैसा एक एल्बम मिला है जिसमें कई ट्रैक हैं। लेकिन कृपया इस तथ्य पर ध्यान दें कि एक ट्रैक अधिक दिखाई दे सकता है जिसमें एक एल्बम, जैसे बैटरी बाय मेटालिका करता है - तीन एल्बम इस ट्रैक की विशेषता हैं।
इसलिए मुझे कुछ अतिरिक्त स्तंभों (जैसे निर्दिष्ट स्थान में ट्रैक की स्थिति) के साथ तीसरी तालिका का उपयोग करके एल्बम और पटरियों के बीच कई-से-कई संबंध चाहिए। वास्तव में मुझे उपयोग करना है, जैसा कि डॉक्ट्रिन के दस्तावेज से पता चलता है, उस कार्यक्षमता को प्राप्त करने के लिए एक डबल-टू-कई संबंध।
/** @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
।