Laravel Polymorphic Relationship
Laravel এ Polymorphic Relationships আসলে ৩টি প্রধান
ধরনের
হয়,
এবং
Laravel 9+ থেকে
আরও
কিছু
advanced polymorphic customizations এসেছে, তবে
মূলত
নিচের
৩টি
সবচেয়ে
বেশি
ব্যবহৃত হয়:
✅
১. One to One Polymorphic Relationship
📖 উদাহরণ:
একটি
User অথবা Post
এর
একটি
Image থাকতে পারে।
✅
কাঠামো:
- users টেবিল
- posts টেবিল
- images টেবিল — যেখানে
polymorphic imageable_id ও
imageable_type
থাকবে
📄 Migration:
Schema::create('images',
function (Blueprint $table) {
$table->id();
$table->string('url');
$table->unsignedBigInteger('imageable_id');
$table->string('imageable_type');
$table->timestamps();
});
📄 Model Setup:
Image.php
class
Image extends Model
{
public function imageable()
{
return $this->morphTo();
}
}
User.php
& Post.php
public
function image()
{
return $this->morphOne(Image::class,
'imageable');
}
🔁 ব্যবহার:
$user
= User::find(1);
$user->image()->create(['url'
=> 'user.jpg']);
$post
= Post::find(1);
$post->image()->create(['url'
=> 'post.jpg']);
✅
২. One to Many Polymorphic Relationship
📖 উদাহরণ:
একটি
Post এবং একটি
Video — উভয়েরই অনেকগুলো Comment থাকতে
পারে।
✅
কাঠামো:
- posts টেবিল
- videos টেবিল
- comments
টেবিল — যেখানে commentable_id এবং commentable_type থাকবে
📄 Model Setup:
Comment.php
class
Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
}
Post.php
& Video.php
public
function comments()
{
return $this->morphMany(Comment::class,
'commentable');
}
🔁 ব্যবহার:
$post
= Post::find(1);
$post->comments()->create(['body'
=> 'Nice post!']);
$video
= Video::find(1);
$video->comments()->create(['body'
=> 'Great video!']);
✅
৩. Many to Many Polymorphic Relationship
📖 উদাহরণ:
Post,
Video, এবং অন্যান্য কন্টেন্টে একাধিক
Tag থাকতে পারে
এবং
একটি
Tag অনেকগুলো Post,
Video ইত্যাদিতে থাকতে
পারে।
✅
কাঠামো:
- posts টেবিল
- videos টেবিল
- tags টেবিল
- taggables
টেবিল (pivot table with taggable_id, taggable_type)
📄 Pivot Table Migration:
Schema::create('taggables',
function (Blueprint $table) {
$table->unsignedBigInteger('tag_id');
$table->unsignedBigInteger('taggable_id');
$table->string('taggable_type');
});
📄 Model Setup:
Tag.php
class
Tag extends Model
{
public function posts()
{
return
$this->morphedByMany(Post::class, 'taggable');
}
public function videos()
{
return $this->morphedByMany(Video::class,
'taggable');
}
}
Post.php
& Video.php
public
function tags()
{
return $this->morphToMany(Tag::class,
'taggable');
}
🔁 ব্যবহার:
$tag
= Tag::create(['name' => 'Laravel']);
$post
= Post::find(1);
$post->tags()->attach($tag->id);
$video
= Video::find(1);
$video->tags()->attach($tag->id);
⚡
Bonus: Custom Morph Names (Optional)
আপনি
চাইলে
morphMap() ব্যবহার করে নিজের মতো
করে
নাম
দিতে
পারেন:
php
CopyEdit
//
AppServiceProvider.php এর
boot() মেথডে
Relation::morphMap([
'posts' => \App\Models\Post::class,
'videos' => \App\Models\Video::class,
]);
🧾
সংক্ষেপে তুলনা:
|
Type |
Example |
Method
Used |
|
One to One Polymorphic |
A User has one Image |
morphOne() |
|
One to Many Polymorphic |
A Post has many Comments |
morphMany() |
|
Many to Many Polymorphic |
A Tag belongs to many Posts and Videos |
morphToMany()
+ morphedByMany() |
2.Another Example:-
Laravel Eloquent Polymorphic Relations (পলিমরফিক রিলেশন) হল একটি শক্তিশালী ফিচার যা আপনাকে একটি মডেলকে একাধিক অন্যান্য মডেলের সাথে সম্পর্ক স্থাপন করার সুযোগ দেয়। সহজভাবে বলতে গেলে, এটি আপনাকে একটি নির্দিষ্ট টেবিলের সাথে একাধিক ভিন্ন মডেলকে যুক্ত করতে দেয়।
উদাহরণস্বরূপ, ধরুন আপনার একটি Comment মডেল আছে এবং আপনি এই Comment মডেলটিকে Post মডেল এবং Video মডেল উভয়টির সাথে যুক্ত করতে চান। যদি আপনি সাধারণ ওয়ান-টু-মেনি (One-to-Many) সম্পর্ক ব্যবহার করেন, তাহলে আপনাকে comments টেবিলে post_id এবং video_id উভয় কলামই রাখতে হবে, যেখানে একটি নির্দিষ্ট মন্তব্যের জন্য শুধুমাত্র একটি কলামে ডেটা থাকবে এবং অন্যটি null থাকবে। এটি ডেটাবেস ডিজাইনকে অগোছালো করে তোলে।
পলিমরফিক রিলেশন এই সমস্যাটির সমাধান করে। এটি আপনাকে comments টেবিলে শুধুমাত্র দুটি কলাম রাখতে দেয়: একটি commentable_id (যা পোস্ট বা ভিডিওর আইডি ধারণ করবে) এবং একটি commentable_type (যা পোস্ট বা ভিডিও মডেলের নাম ধারণ করবে)।
পলিমরফিক রিলেশনের প্রকারভেদ:
Laravel এ তিন ধরণের পলিমরফিক রিলেশন রয়েছে:
One-to-One Polymorphic (ওয়ান-টু-ওয়ান পলিমরফিক)
One-to-Many Polymorphic (ওয়ান-টু-মেনি পলিমরফিক)
Many-to-Many Polymorphic (মেনি-টু-মেনি পলিমরফিক)
চলুন, প্রতিটি প্রকারের বিস্তারিত ব্যাখ্যা এবং কোড উদাহরণ দেখা যাক।
1. One-to-One Polymorphic (ওয়ান-টু-ওয়ান পলিমরফিক)
এই রিলেশনশিপে, একটি মডেলের একটি রেকর্ড অন্য একাধিক মডেলের একটি রেকর্ডের সাথে সম্পর্কিত হতে পারে।
উদাহরণ: ধরুন আপনার User, Post এবং Video মডেল আছে, এবং প্রতিটি User, Post বা Video এর জন্য শুধুমাত্র একটি Image থাকতে পারে।
মাইগ্রেশন (Migrations):
প্রথমে images টেবিল তৈরি করুন:
// database/migrations/xxxx_xx_xx_create_images_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateImagesTable extends Migration
{
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->id();
$table->string('url');
$table->morphs('imageable'); // এই লাইনটি গুরুত্বপূর্ণ
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('images');
}
}
এখানে morphs('imageable') শর্টকাটটি imageable_id (unsignedBigInteger) এবং imageable_type (string) দুটি কলাম তৈরি করে।
মডেল (Models):
Image Model:
// app/Models/Image.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Image extends Model
{
use HasFactory;
protected $fillable = ['url', 'imageable_id', 'imageable_type'];
public function imageable()
{
return $this->morphTo(); // এটি পলিমরফিক রিলেশনের বিপরীত দিক
}
}
User Model:
// app/Models/User.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use HasFactory, Notifiable;
// ... অন্যান্য কোড ...
public function image()
{
return $this->morphOne(Image::class, 'imageable'); // One-to-One Polymorphic
}
}
Post Model:
// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $fillable = ['title', 'content'];
public function image()
{
return $this->morphOne(Image::class, 'imageable'); // One-to-One Polymorphic
}
}
Video Model:
// app/Models/Video.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
use HasFactory;
protected $fillable = ['title', 'url'];
public function image()
{
return $this->morphOne(Image::class, 'imageable'); // One-to-One Polymorphic
}
}
ডেটা সেভ (Saving Data):
use App\Models\User;
use App\Models\Post;
use App\Models\Video;
use App\Models\Image;
// একটি User এর জন্য Image সেভ করা
$user = User::find(1);
$user->image()->create(['url' => 'user_profile.jpg']);
// একটি Post এর জন্য Image সেভ করা
$post = Post::find(1);
$post->image()->create(['url' => 'post_thumbnail.jpg']);
// একটি Video এর জন্য Image সেভ করা
$video = Video::find(1);
$video->image()->create(['url' => 'video_cover.jpg']);
ডেটা ফেচ (Fetching Data):
use App\Models\User;
use App\Models\Post;
use App\Models\Video;
use App\Models\Image;
// User এর Image ফেচ করা
$user = User::with('image')->find(1);
echo $user->image->url; // user_profile.jpg
// Post এর Image ফেচ করা
$post = Post::with('image')->find(1);
echo $post->image->url; // post_thumbnail.jpg
// Video এর Image ফেচ করা
$video = Video::with('image')->find(1);
echo $video->image->url; // video_cover.jpg
// Image থেকে সম্পর্কিত মডেল ফেচ করা
$image = Image::find(1); // ধরে নিলাম এটি User এর Image
$imageable = $image->imageable; // এটি একটি User মডেল ইনস্ট্যান্স হবে
echo $imageable->name; // User এর নাম
2. One-to-Many Polymorphic (ওয়ান-টু-মেনি পলিমরফিক)
এই রিলেশনশিপে, একটি মডেলের একটি রেকর্ড অন্য একাধিক মডেলের একাধিক রেকর্ডের সাথে সম্পর্কিত হতে পারে।
উদাহরণ: ধরুন আপনার Post এবং Video মডেল আছে, এবং প্রতিটি Post বা Video এর জন্য একাধিক Comment থাকতে পারে।
মাইগ্রেশন (Migrations):
প্রথমে comments টেবিল তৈরি করুন:
// database/migrations/xxxx_xx_xx_create_comments_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCommentsTable extends Migration
{
public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->text('body');
$table->morphs('commentable'); // এই লাইনটি গুরুত্বপূর্ণ
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('comments');
}
}
এখানে morphs('commentable') শর্টকাটটি commentable_id এবং commentable_type কলাম তৈরি করে।
মডেল (Models):
Comment Model:
// app/Models/Comment.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
use HasFactory;
protected $fillable = ['body', 'commentable_id', 'commentable_type'];
public function commentable()
{
return $this->morphTo(); // এটি পলিমরফিক রিলেশনের বিপরীত দিক
}
}
Post Model:
// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $fillable = ['title', 'content'];
public function comments()
{
return $this->morphMany(Comment::class, 'commentable'); // One-to-Many Polymorphic
}
}
Video Model:
// app/Models/Video.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
use HasFactory;
protected $fillable = ['title', 'url'];
public function comments()
{
return $this->morphMany(Comment::class, 'commentable'); // One-to-Many Polymorphic
}
}
ডেটা সেভ (Saving Data):
use App\Models\Post;
use App\Models\Video;
use App\Models\Comment;
// একটি Post এ Comment সেভ করা
$post = Post::find(1);
$post->comments()->create(['body' => 'This is a comment on the post.']);
$post->comments()->create(['body' => 'Another comment on the post.']);
// একটি Video এ Comment সেভ করা
$video = Video::find(1);
$video->comments()->create(['body' => 'Great video!']);
$video->comments()->create(['body' => 'I loved this video.']);
ডেটা ফেচ (Fetching Data):
use App\Models\Post;
use App\Models\Video;
use App\Models\Comment;
// Post এর Comment ফেচ করা
$post = Post::with('comments')->find(1);
foreach ($post->comments as $comment) {
echo $comment->body . "\n";
}
/* Output:
This is a comment on the post.
Another comment on the post.
*/
// Video এর Comment ফেচ করা
$video = Video::with('comments')->find(1);
foreach ($video->comments as $comment) {
echo $comment->body . "\n";
}
/* Output:
Great video!
I loved this video.
*/
// Comment থেকে সম্পর্কিত মডেল ফেচ করা
$comment = Comment::find(1); // ধরে নিলাম এটি একটি Post এর Comment
$commentable = $comment->commentable; // এটি একটি Post মডেল ইনস্ট্যান্স হবে
echo $commentable->title; // Post এর টাইটেল
3. Many-to-Many Polymorphic (মেনি-টু-মেনি পলিমরফিক)
এই রিলেশনশিপে, একটি মডেলের একাধিক রেকর্ড অন্য একাধিক মডেলের একাধিক রেকর্ডের সাথে সম্পর্কিত হতে পারে। এর জন্য একটি ইন্টারমিডিয়েট টেবিলের প্রয়োজন হয়।
উদাহরণ: ধরুন আপনার Post এবং Video মডেল আছে, এবং আপনি Tag মডেল ব্যবহার করে Post এবং Video উভয়কেই ট্যাগ করতে চান। একটি ট্যাগ একাধিক পোস্ট বা ভিডিওতে থাকতে পারে, এবং একটি পোস্ট বা ভিডিওতে একাধিক ট্যাগ থাকতে পারে।
মাইগ্রেশন (Migrations):
প্রথমে tags টেবিল তৈরি করুন:
// database/migrations/xxxx_xx_xx_create_tags_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTagsTable extends Migration
{
public function up()
{
Schema::create('tags', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('tags');
}
}
এরপর taggables পিভট টেবিল তৈরি করুন:
// database/migrations/xxxx_xx_xx_create_taggables_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTaggablesTable extends Migration
{
public function up()
{
Schema::create('taggables', function (Blueprint $table) {
$table->foreignId('tag_id')->constrained()->onDelete('cascade');
$table->morphs('taggable'); // এই লাইনটি গুরুত্বপূর্ণ
$table->timestamps();
$table->unique(['tag_id', 'taggable_id', 'taggable_type']); // ডুপ্লিকেট এড়াতে
});
}
public function down()
{
Schema::dropIfExists('taggables');
}
}
এখানে morphs('taggable') শর্টকাটটি taggable_id এবং taggable_type কলাম তৈরি করে।
মডেল (Models):
Tag Model:
// app/Models/Tag.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
use HasFactory;
protected $fillable = ['name'];
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable');
}
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable');
}
}
Post Model:
// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $fillable = ['title', 'content'];
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable'); // Many-to-Many Polymorphic
}
}
Video Model:
// app/Models/Video.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
use HasFactory;
protected $fillable = ['title', 'url'];
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable'); // Many-to-Many Polymorphic
}
}
ডেটা সেভ (Saving Data):
use App\Models\Post;
use App\Models\Video;
use App\Models\Tag;
// ট্যাগ তৈরি করা
$tag1 = Tag::create(['name' => 'Laravel']);
$tag2 = Tag::create(['name' => 'PHP']);
$tag3 = Tag::create(['name' => 'Database']);
$tag4 = Tag::create(['name' => 'Vue.js']);
// Post এ ট্যাগ যুক্ত করা
$post = Post::find(1);
$post->tags()->attach([$tag1->id, $tag2->id]); // attach() ব্যবহার করা হয় Many-to-Many এর জন্য
// Video এ ট্যাগ যুক্ত করা
$video = Video::find(1);
$video->tags()->attach([$tag1->id, $tag3->id, $tag4->id]);
ডেটা ফেচ (Fetching Data):
use App\Models\Post;
use App\Models\Video;
use App\Models\Tag;
// Post এর ট্যাগ ফেচ করা
$post = Post::with('tags')->find(1);
foreach ($post->tags as $tag) {
echo $tag->name . "\n";
}
/* Output:
Laravel
PHP
*/
// Video এর ট্যাগ ফেচ করা
$video = Video::with('tags')->find(1);
foreach ($video->tags as $tag) {
echo $tag->name . "\n";
}
/* Output:
Laravel
Database
Vue.js
*/
// একটি ট্যাগ থেকে সম্পর্কিত পোস্ট ফেচ করা
$tag = Tag::with('posts')->find(1); // Laravel ট্যাগ
foreach ($tag->posts as $post) {
echo $post->title . "\n";
}
// output: Post 1 Title (যদি পোস্টের টাইটেল "Post 1 Title" হয়)
// একটি ট্যাগ থেকে সম্পর্কিত ভিডিও ফেচ করা
$tag = Tag::with('videos')->find(1); // Laravel ট্যাগ
foreach ($tag->videos as $video) {
echo $video->title . "\n";
}
// output: Video 1 Title (যদি ভিডিওর টাইটেল "Video 1 Title" হয়)
পলিমরফিক ম্যাপ (Polymorphic Map):
অনেক সময় commentable_type বা imageable_type কলামে মডেলের পুরো নাম (যেমন App\Models\Post) সেভ হয়। এটি দীর্ঘ এবং কিছু ক্ষেত্রে সমস্যা তৈরি করতে পারে। Laravel আপনাকে এই মডেলের নামের পরিবর্তে কাস্টম স্ট্রিং ব্যবহার করার অনুমতি দেয়। এটি boot মেথডে Relation::morphMap() ব্যবহার করে করা হয়।
App\Providers\AppServiceProvider.php ফাইলে:
// app/Providers/AppServiceProvider.php
use Illuminate\Database\Eloquent\Relations\Relation;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Relation::morphMap([
'posts' => 'App\Models\Post',
'videos' => 'App\Models\Video',
'users' => 'App\Models\User',
]);
}
// ...
}
এখন যখন আপনি ডেটা সেভ করবেন, তখন commentable_type বা imageable_type কলামে App\Models\Post এর পরিবর্তে posts সেভ হবে। ডেটা ফেচ করার সময়ও Laravel স্বয়ংক্রিয়ভাবে posts স্ট্রিংটিকে App\Models\Post মডেলের সাথে ম্যাপ করে নেবে।
কখন পলিমরফিক রিলেশন ব্যবহার করবেন?
যখন আপনার একটি মডেল একাধিক ভিন্ন মডেলের সাথে একই ধরণের সম্পর্ক স্থাপন করবে।
যখন আপনি আপনার ডেটাবেস ডিজাইনকে পরিষ্কার এবং সুসংগঠিত রাখতে চান, অপ্রয়োজনীয় কলাম এড়িয়ে।
যখন আপনার অ্যাপ্লিকেশন স্কেল করার প্রয়োজন হয় এবং ভবিষ্যতে নতুন মডেল যুক্ত হওয়ার সম্ভাবনা থাকে যা একই সম্পর্ক ব্যবহার করবে।
No comments