আমি ভাবছি যে ডক্ট্রাইন 2-তে বহু-থেকে-বহু সম্পর্কের সাথে কাজ করার সবচেয়ে ভাল, সবচেয়ে পরিষ্কার এবং সবচেয়ে সহজ উপায়।
আসুন ধরে নেওয়া যাক যে আমরা বেশ কয়েকটি ট্র্যাক সহ মেটালিকার বাইরের মাস্টার অফ পুতুলের মতো একটি অ্যালবাম পেয়েছি । তবে দয়া করে এই বিষয়টি লক্ষ্য করুন যে ব্যাটারি বাই মেটালিকার মতো আরও একটি অ্যালবামে একটি ট্র্যাক উপস্থিত হতে পারে - তিনটি অ্যালবাম এই ট্র্যাকটির বৈশিষ্ট্যযুক্ত।
সুতরাং আমার যা দরকার তা হল অতিরিক্ত অতিরিক্ত কলাম (নির্দিষ্ট অ্যালবামে ট্র্যাকের অবস্থানের মতো) সহ তৃতীয় টেবিল ব্যবহার করে অ্যালবাম এবং ট্র্যাকগুলির মধ্যে বহু থেকে বহু সম্পর্ক। প্রকৃতপক্ষে আমাকে ব্যবহার করতে হবে, যেমন ডক্ট্রিনের ডকুমেন্টেশনগুলি পরামর্শ দেয়, সেই কার্যকারিতা অর্জনের জন্য দ্বিগুণ একের সাথে অনেকের সম্পর্ক।
/** @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।