Laravel Polymorphic Relationships

Laravel এ Polymorphic Relationship কি?

Laravel Polymorphic Relationship

Polymorphic Relationship হল এমন একটি রিলেশনশিপ যেখানে একটি মডেল একাধিক অন্য মডেলের সাথে সংযুক্ত হতে পারে এবং সংযোগ করা মডেলের টাইপ ডাইনামিকভাবে পরিবর্তন করা যায়। অর্থাৎ, একটি টেবিলে একাধিক মডেলের রেকর্ড সংরক্ষণ করতে পারি এবং যাতে একটি reusable বা পুনরাবৃত্তিমূলক রিলেশনশিপ দ্বারা এই রেকর্ডগুলি সংযুক্ত করা যায়, আর এই কাজটি পলিমরফিক রিলেশনশিপ ব্যবহার করে করা হয়।

Polymorphic Relationship ডাটাবেসের নির্দিষ্ট টেবিলে তিনটি কলাম ব্যবহার করে। প্রথমটি হল “Polymorphic Type” কলাম, যা সংশ্লিষ্ট মডেলের নাম সংরক্ষণ করে। দ্বিতীয়টি হল “Polymorphic Id” কলাম, যা সংশ্লিষ্ট মডেলের আইডি সংরক্ষণ করে। তৃতীয়টি হল “Polymorphic Subject” কলাম, যা Polymorphic Relationship সাথে সংযুক্ত মডেলের নাম সংরক্ষণ করে।

Polymorphic Relationship ব্যবহার করার জন্য, আপনার মডেলগুলির মধ্যে একটা Relationship সেটআপ করতে হবে এবং আপনি কমেন্ট মডেলে Polymorphic Relationship ব্যবহার করতে হবে।

Laravel Framework এ Polymorphic Relationship তিন ধরণের হয়।

  1. One To One (Polymorphic)
  2. One To Many (Polymorphic)
  3. Many To Many (Polymorphic)

Laravel One To One (Polymorphic) Relationship

Laravel Framework এ One-to-One Polymorphic Relationship হল এমন একটি Relationship যেখানে একটি মডেল অন্য একটি মডেলের সাথে একটি মধ্যবর্তী সংখ্যার রেফারেন্স সংরক্ষণ করে। এই রিলেশনশিপে একটি সংখ্যার সমান মান শুধুমাত্র একটি মডেলে এবং একটি মধ্যবর্তী মডেলের সাথে ম্যাচ করে।

উদাহরণস্বরূপ, ধরা যাক আপনার একটি “Image” মডেল, “User” মডেল এবং “Post” মডেল রয়েছে। আপনি এমন একটি একটি রিলেশনশিপ স্থাপন করতে চান যেখানে প্রতিটি পোস্ট এবং প্রতিটি ইউজার কে একটি ইমেজের সাথে সংযুক্ত করা হয়েছে। প্রতিটি পোস্ট শুধুমাত্র একটি ইমেজের সাথে সংযুক্ত হতে পারে এবং একটি ইমেজ শুধুমাত্র একটি পোস্টের সাথে সংযুক্ত হতে পারে। একইভাবে প্রতিটি ইউজার শুধুমাত্র একটি ইমেজের সাথে সংযুক্ত হতে পারে এবং একটি ইমেজ শুধুমাত্র একটি ইউজারের সাথে সংযুক্ত হতে পারে। এটি একটি One-to-One Polymorphic Relationship।

Mastering Laravel with ReactJS Course

Create Migration Files

Laravel Framework এ One To One Polymorphic Relationship বুঝার জন্য আমরা এখন posts, users এবং images নামে এই তিনটি টেবিল তৈরি করব। এর জন্য প্রথমে নিম্নোক্ত artisan command রান করার মাধ্যমে আমরা table তিনটির Migration File তৈরি করব :

php artisan make:migration create_posts_table
php artisan make:migration create_users_table
php artisan make:migration create_images_table

এবার সদ্য তৈরি হওয়া Migration File গুলোকে নিচের মতো করে আপডেট করে নিন :

create_posts_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('content');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

create_users_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
};

create_images_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('images', function (Blueprint $table) {
            $table->id();
            $table->string('url');
            $table->unsignedBigInteger('imageable_id');
            $table->string('imageable_type');          
            $table->index(['imageable_id', 'imageable_type']);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('images');
    }
};

এবার উক্ত মাইগ্রেশন ফাইল গুলো ব্যবহার করে টেবিল তৈরির জন্য নিম্নোক্ত artisan command গুলো রান করুন। আর অবশ্যই নিম্নোক্ত কমান্ডে ব্যবহৃত ফাইল নামের পরিবর্তে আপনার সদ্য তৈরি হওয়া মাইগ্রেশন ফাইলের নাম ব্যবহার করুন :

php artisan migrate --path=/database/migrations/your_posts_table_migration_file_name.php
php artisan migrate --path=/database/migrations/your_users_table_migration_file_name.php
php artisan migrate --path=/database/migrations/your_images_table_migration_file_name.php

এবার সদ্য তৈরি হওয়া টেবিল তিনটির জন্য নিম্নোক্ত artisan command এর মাধ্যমে তিনটি Model তৈরি করব।

php artisan make:model Post
php artisan make:model User
php artisan make:model Image

table এবং Model গুলো তৈরির কাজ শেষ , এবার table গুলোতে কিছু স্যাম্পল ডেটা তৈরির জন্য আমরা তিনটি seed file তৈরি করব। seed file তিনটি তৈরির জন্য নিম্নোক্ত কমান্ড ব্যবহার করুন :

php artisan make:seed PostsTableSeeder
php artisan make:seed UsersTableSeeder
php artisan make:seed ImagesTableSeeder

এবার সদ্য তৈরি হওয়া Seed File গুলোকে নিচের মতো করে আপডেট করে নিন :

PostsTableSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Post;

class PostsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
          // Create some example posts
          Post::create([
            'title' => 'First Post',
            'content' => 'This is the content of the first post.',
        ]);

        Post::create([
            'title' => 'Second Post',
            'content' => 'This is the content of the second post.',
        ]);

        Post::create([
            'title' => 'Third Post',
            'content' => 'This is the content of the Third post.',
        ]);

        // You can create more posts as needed
    }
}

UsersTableSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use App\Models\User;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
         // Create an example user
         User::create([
            'name' => 'John Doe',
            'email' => 'johndoe@example.com',
            'email_verified_at' => now(),
            'password' => Hash::make('password'),
            'remember_token' => str::random(10),
        ]);

        User::create([
            'name' => 'Jane Doe',
            'email' => 'janedoe@example.com',
            'email_verified_at' => now(),
            'password' => Hash::make('password'),
            'remember_token' => str::random(10),
        ]);

        User::create([
            'name' => 'Wiliam Petrik',
            'email' => 'wilium@example.com',
            'email_verified_at' => now(),
            'password' => Hash::make('password'),
            'remember_token' => str::random(10),
        ]);

        // You can create more users as needed
    }
}

ImagesTableSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Image;
use App\Models\Post;
use App\Models\User;

class ImagesTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
           // Create example images for posts
           Image::create([
            'url' => 'https://example.com/image1.jpg',
            'imageable_id' => Post::first()->id,
            'imageable_type' => Post::class,
        ]);

        Image::create([
            'url' => 'https://example.com/image2.jpg',
            'imageable_id' => Post::find(2)->id,
            'imageable_type' => Post::class,
        ]);

        // Create example images for users
        Image::create([
            'url' => 'https://example.com/image3.jpg',
            'imageable_id' => User::first()->id,
            'imageable_type' => User::class,
        ]);

        // You can create more images as needed for different models
    }
}

এবার নিম্নোক্ত artisan command ব্যবহার করে উপরোক্ত seed file গুলো রান করুন :

php artisan db:seed --class=UsersTableSeeder
php artisan db:seed --class=PostsTableSeeder
php artisan db:seed --class=ImagesTableSeeder

Mastering Laravel with ReactJS Course

এতক্ষনে আমরা table গুলোর structure তৈরির জন্য Migration File গুলো এবং উক্ত টেবিল গুলোর জন্য dummy data এর জন্য seeding এর কাজ গুলো করেছি। এখন One To One Polymorphic Relationship এর জন্য আপনার User, Post, এবং Image Model কে নিচের মতো করে আপডেট করে নিন :

User.php Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\Relations\MorphOne;

class User extends Authenticatable
{
    use HasFactory;
 /**
     * Get the user's image.
     */
    public function image(): MorphOne
    {
        return $this->morphOne(Image::class, 'imageable');
    }
  
}

Post.php Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;

class Post extends Model
{
    use HasFactory;

    /**
     * Get the post's image.
     */
    public function image(): MorphOne
    {
        return $this->morphOne(Image::class, 'imageable');
    }

}

Image.php Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class Image extends Model
{
    use HasFactory;

    /**
     * Get the parent imageable model (user or post).
     */
    public function imageable(): MorphTo
    {
        return $this->morphTo();
    }
}

মোটামুটি One To One Polymorphic Relationship এর জন্য যে ধরণের function যুক্ত করার দরকার ছিল , তার সবই আমরা যুক্ত করেছি। এখন আপনার web.php ফাইলে নিম্নোক্ত route দিয়ে One To One Polymorphic Relationship এর উদাহরণ গুলো দেখে নিন :

use App\Models\Post;
use App\Models\Image;
Route::get('/morph-one',function(){
    $post = Post::find(1);
    //dd($post->image);

    $image = Image::find(1);
    $imageable = $image->imageable;
    //dd($imageable);
});

Laravel One To Many (Polymorphic) Relationship

Laravel Framework এ One-to-Many Polymorphic Relationship হল এমন একটি Relationship যেখানে একটি মডেল অন্য অনেকগুলো মডেলের সাথে একটি মধ্যবর্তী সংখ্যার রেফারেন্স সংরক্ষণ করে। এই রিলেশনশিপে একটি সংখ্যার সমান মান অনেকগুলো মডেলের সাথে এবং অনেকগুলো মধ্যবর্তী মডেলের সাথে ম্যাচ করে।

Laravel One To Many Polymorphic Relationship ব্যবহার করার একটি সহজ উদাহরণ হল, ধরুন আপনার একটি Comments Table রয়েছে, কিন্তু এই Comments Table এর কমেন্টস গুলো হতে পারে কোনো একটি posts table এর বা একটি videos table সাথে সম্পর্কৃত। আর তাই প্রতিটি কমেন্ট কোন টেবিল এর সাথে সম্পর্কৃত তা সহজে বুঝার জন্য প্রতিটি comment রেকর্ডে তার সাথে রিলেটেড বা সংযুক্ত মডেল এবং সংযুক্ত মডেলের আইডি সংরক্ষণ করা দরকার। এটি দ্বারা সিস্টেমটি বোঝানো হয় যে এই কমেন্টগুলি কোন মডেল এর সাথে Related বা সংযুক্ত আছে।

Create Migration Files

Laravel Framework এ Laravel One To Many Polymorphic Relationship বুঝার জন্য আমরা এখন posts, videos এবং comments নামে এই তিনটি টেবিল তৈরি করব। এর জন্য প্রথমে নিম্নোক্ত artisan command রান করার মাধ্যমে আমরা table তিনটির Migration File তৈরি করব :

php artisan make:migration create_posts_table
php artisan make:migration create_videos_table
php artisan make:migration create_comments_table

এবার সদ্য তৈরি হওয়া Migration File গুলোকে নিচের মতো করে আপডেট করে নিন :

create_posts_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('content');
            $table->unsignedBigInteger('commentable_id');
            $table->string('commentable_type');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

create_videos_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('videos', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('url');
            $table->unsignedBigInteger('commentable_id');
            $table->string('commentable_type');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('videos');
    }
};

create_comments_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->text('body');
            $table->unsignedBigInteger('commentable_id');
            $table->string('commentable_type');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('comments');
    }
};

এবার উক্ত মাইগ্রেশন ফাইল গুলো ব্যবহার করে টেবিল তৈরির জন্য নিম্নোক্ত artisan command গুলো রান করুন। আর অবশ্যই নিম্নোক্ত কমান্ডে ব্যবহৃত ফাইল নামের পরিবর্তে আপনার সদ্য তৈরি হওয়া মাইগ্রেশন ফাইলের নাম ব্যবহার করুন :

php artisan migrate --path=/database/migrations/your_posts_table_migration_file_name.php
php artisan migrate --path=/database/migrations/your_videos_table_migration_file_name.php
php artisan migrate --path=/database/migrations/your_comments_table_migration_file_name.php

এবার সদ্য তৈরি হওয়া টেবিল তিনটির জন্য নিম্নোক্ত artisan command এর মাধ্যমে তিনটি Model তৈরি করব।

php artisan make:model Post
php artisan make:model Video
php artisan make:model Comment

table এবং Model গুলো তৈরির কাজ শেষ , এবার table গুলোতে কিছু স্যাম্পল ডেটা তৈরির জন্য আমরা তিনটি seed file তৈরি করব। seed file তিনটি তৈরির জন্য নিম্নোক্ত কমান্ড ব্যবহার করুন :

php artisan make:seed PostsTableSeeder
php artisan make:seed VideosTableSeeder
php artisan make:seed CommentsTableSeeder

এবার সদ্য তৈরি হওয়া Seed File গুলোকে নিচের মতো করে আপডেট করে নিন :

PostsTableSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Post;

class PostsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Post::create([
            'title' => 'First Post',
            'content' => 'This is the content of the first post.',
            'commentable_id' => 1,
            'commentable_type' => 'App\Models\Post',
        ]);

        Post::create([
            'title' => 'Second Post',
            'content' => 'This is the content of the Second post.',
            'commentable_id' => 2,
            'commentable_type' => 'App\Models\Post',
        ]);
    }
}

VideosTableSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Video;

class VideosTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Video::create([
            'title' => 'First Video',
            'url' => 'https://example.com/video1.mp4',
            'commentable_id' => 1,
            'commentable_type' => 'App\Models\Video',
        ]);
        Video::create([
            'title' => 'Second Video',
            'url' => 'https://example.com/video1.mp4',
            'commentable_id' => 2,
            'commentable_type' => 'App\Models\Video',
        ]);
    }
}

CommentsTableSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Comment;

class CommentsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Comment::create([
            'body' => 'Great post!',
            'commentable_id' => 1,
            'commentable_type' => 'App\Models\Post',
        ]);

        Comment::create([
            'body' => 'Nice video!',
            'commentable_id' => 1,
            'commentable_type' => 'App\Models\Video',
        ]);
    }
}

এবার নিম্নোক্ত artisan command ব্যবহার করে উপরোক্ত seed file গুলো রান করুন :

php artisan db:seed --class=PostsTableSeeder
php artisan db:seed --class=VideosTableSeeder
php artisan db:seed --class=CommentsTableSeeder

এতক্ষনে আমরা table গুলোর structure তৈরির জন্য Migration File গুলো এবং উক্ত টেবিল গুলোর জন্য dummy data এর জন্য seeding এর কাজ গুলো করেছি। এখন One To Many Polymorphic Relationship এর জন্য আপনার Post,Videos এবং Comment Model কে নিচের মতো করে আপডেট করে নিন :

Post.php Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\Relations\MorphMany;

class Post extends Model
{
    use HasFactory;

    public function image():MorphOne
    {
        return $this->morphOne(Image::class,'imageable');
    }

      /**
     * Get all of the post's comments.
     */
    public function comments(): MorphMany
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

Video.php Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;

class Video extends Model
{
    use HasFactory;

    /**
     * Get all of the video's comments.
     */
    public function comments(): MorphMany
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}

Comment.php Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;

class Comment extends Model
{
    use HasFactory;

     /**
     * Get the parent commentable model (post or video).
     */
    public function commentable(): MorphTo
    {
        return $this->morphTo();
    }
}

Mastering Laravel with ReactJS Course

Retrieving The Relationship

এতক্ষন আমরা আমাদের ডাটাবেস টেবিল, টেবিল এর জন্য স্যাম্পল ডেটা এবং মডেলগুলি তৈরি করেছি, এখন আপনি আপনার dynamic relationship properties গুলির মাধ্যমে relationships গুলি অ্যাক্সেস করতে পারেন। উদাহরণস্বরূপ, একটি পোস্টের জন্য সমস্ত comments এ অ্যাক্সেস করতে, আমরা comments dynamic property ব্যবহার করতে পারি:

use App\Models\Post;
Route::get('/morph-many',function(){
     $post = Post::find(1);
     foreach ($post->comments as $comment) {
        echo $comment->body,"<br>";
    }
});

এছাড়াও আপনি morphTo-তে কল সম্পাদনকারী method name অ্যাক্সেস করে একটি পলিমরফিক চাইল্ড মডেলের parent ও retrieve করতে পারেন। এই ক্ষেত্রে, এটি Comment মডেলের commentable method। সুতরাং, comment এর parent model এ অ্যাক্সেস করার জন্য আমরা একটি dynamic relationship property হিসাবে সেই method টি অ্যাক্সেস করব:

use App\Models\Comment;
Route::get('/morph-many',function(){
    $comment = Comment::find(1);
     
    echo $comment->commentable;
});

Comment মডেলের commentable relation টি একটি Post বা Video instance instance প্রদান করবে, আর এটা নির্ভর করে কোন ধরনের মডেল comment’s parent তার উপর ।

One Of Many (Polymorphic)

কখনও কখনও একটি মডেলের অনেক related models থাকতে পারে, তবুও আপনি related মডেল গুলোর “latest” বা “oldest” মডেলটি সহজেই retrieve করতে পারেন। উদাহরণস্বরূপ, একটি User model এর অনেকগুলি Image models এর সাথে related হতে পারে, তবে আপনি ইউজারের আপলোড করা সাম্প্রতিক (Latest) image গুলির সাথে ইন্টারঅ্যাক্ট করার একটি সুবিধাজনক উপায় নির্ধারণ করতে চান৷ তাহলে আপনি User Model এ ofMany methods দিয়ে morphOne relationship type ব্যবহার করে এই কাজটি করতে পারেন:

 /**
     * Get the user's most recent image.
     */
    public function latestImage(): MorphOne
    {
        return $this->morphOne(Image::class, 'imageable')->latestOfMany();
    }

এখন আপনি আপনার route এ web.php ফাইলে নিম্নোক্ত কোডের মাধ্যমে আপনার লেটেস্ট ছবি গুলো সহজেই পেতে পারেন :

use App\Models\User;
Route::get('/latest-image',function(){
    $user = User::find(1); // Retrieve the user instance

    $latestImage = $user->latestImage; // Access the latestImage relationship

    if ($latestImage) {
        // Access the image attributes
        echo $latestImage->url,"<br>";
        echo $latestImage->imageable_id,"<br>";
        echo $latestImage->imageable_type,"<br>";
        
        // You can perform further operations with the image data
        // ...
    } else {
        // No image found for the user
    }
});

একইভাবে, আপনি একটি relationship এর “oldest”, বা first, related model retrieve করার একটি method সংজ্ঞায়িত করতে পারেন:

    /**
 * Get the user's oldest image.
    */
    public function oldestImage(): MorphOne
    {
        return $this->morphOne(Image::class, 'imageable')->oldestOfMany();
    }

এখন আপনি আপনার route এ web.php ফাইলে নিম্নোক্ত কোডের মাধ্যমে আপনার Oldest Image গুলো সহজেই পেতে পারেন :

use App\Models\User;
Route::get('/oldest-image',function(){
    $user = User::find(1); // Retrieve the user instance

    $oldestImage = $user->oldestImage; // Access the latestImage relationship

    if ($oldestImage) {
        // Access the image attributes
        echo $oldestImage->url,"<br>";
        echo $oldestImage->imageable_id,"<br>";
        echo $oldestImage->imageable_type,"<br>";
        
        // You can perform further operations with the image data
        // ...
    } else {
        // No image found for the user
    }
});

ডিফল্টরূপে, latestOfMany এবং oldestOfMany method গুলি মডেলের primary key-এর উপর ভিত্তি করে latest বা oldest সম্পর্কিত মডেল retrieve করবে, যা অবশ্যই sortable হতে হবে। যাইহোক, কখনও কখনও আপনি একটি ভিন্ন sorting criteria ব্যবহার করে একটি বড় relationship থেকে একটি single model retrieve করতে চাইতে পারেন।

    /**
     * Get the user's most popular image.
    */
    public function bestImage(): MorphOne
    {
        return $this->morphOne(Image::class, 'imageable')->ofMany('likes', 'max');
    }

এখন আপনি আপনার route এ web.php ফাইলে নিম্নোক্ত কোডের মাধ্যমে আপনার Most Liked Image গুলো সহজেই পেতে পারেন :

use App\Models\User;
Route::get('/best-image',function(){
    $user = User::find(1); // Retrieve the user instance

    $bestImage = $user->bestImage; // Access the latestImage relationship

    if ($bestImage) {
        // Access the image attributes
        echo $bestImage->url,"<br>";
        echo $bestImage->imageable_id,"<br>";
        echo $bestImage->imageable_type,"<br>";
        
        // You can perform further operations with the image data
        // ...
    } else {
        // No image found for the user
    }
});

এতক্ষনে আমরা যতগুলো উদাহরণ দেখলাম। আমাদের User মডেলে সবগুলো হবে নিম্নরুপঃ

<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;





class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    
    /**
     * Get the latest post associated with the user.
     */
    public function latestPost():HasOne
    {
        return $this->hasOne(Post::class)->latestOfMany();
    }

    /**
    * Get the oldest post associated with the user.
    */
    public function oldestPost():HasOne
    {
        return $this->hasOne(Post::class)->oldestOfMany();
    }

    /**
     * Get the user's popular post.
     */
    public function popularPost():HasOne
    {
        return $this->hasOne(Post::class)->ofMany('views', 'max');
    }

    public function latestImage():MorphOne
    {
        return $this->MorphOne(Image::class,'imageable')->latestOfMany();
    }
  

}

Many To Many (Polymorphic)

Many-to-many polymorphic relations “morph one” এবং “morph many” relationships চেয়ে কিছুটা জটিল। উদাহরণস্বরূপ, একটি Post model এবং Video model একটি Tag model সাথে একটি polymorphic relation ভাগ করতে পারে। এই পরিস্থিতিতে many-to-many polymorphic relation ব্যবহার করা আপনার অ্যাপ্লিকেশনটিকে posts বা videos গুলির সাথে যুক্ত হতে পারে এমন unique tags একটি একক টেবিলের অনুমতি দেবে৷ প্রথমে, আসুন এই relationship তৈরি করার জন্য প্রয়োজনীয় table structure দেখা যাক:

posts
    id - integer
    name - string
 
videos
    id - integer
    name - string
 
tags
    id - integer
    name - string
 
taggables
    tag_id - integer
    taggable_id - integer
    taggable_type - string

Mastering Laravel with ReactJS Course

Create Migration Files

Laravel Framework এ Laravel Many To Many Polymorphic Relationship বুঝার জন্য আমাদের posts, videos, tags এবং taggables নামে এই চারটি টেবিল তৈরি করতে হবে। তাই আমরা এখন নিম্নোক্ত artisan command রান করার মাধ্যমে এই চারটি table যথাক্রমে posts,videos,tags এবং taggables table এর জন্য Migration File তৈরি করব :

php artisan make:migration create_posts_table
php artisan make:migration create_videos_table
php artisan make:migration create_tags_table
php artisan make:migration create_taggables _table

এবার সদ্য তৈরি হওয়া Migration File গুলোকে নিচের মতো করে আপডেট করে নিন :

create_posts_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

create_videos_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('videos', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('videos');
    }
};

create_tags_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('tags', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('tags');
    }
};

create_taggables_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('taggables', function (Blueprint $table) {
            $table->id();
            $table->foreignId('tag_id')->constrained('tags');
            $table->unsignedBigInteger('taggable_id');
            $table->string('taggable_type');
            $table->timestamps();

            $table->index(['taggable_id', 'taggable_type']);

            // Optional: If you want to cascade delete the taggables when a tag is deleted
            // $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('taggables');
    }
};

এবার উক্ত মাইগ্রেশন ফাইল গুলো ব্যবহার করে টেবিল তৈরির জন্য নিম্নোক্ত artisan command গুলো রান করুন। আর অবশ্যই নিম্নোক্ত কমান্ডে ব্যবহৃত ফাইল নামের পরিবর্তে আপনার সদ্য তৈরি হওয়া মাইগ্রেশন ফাইলের নাম ব্যবহার করুন :

php artisan migrate --path=/database/migrations/your_posts_table_migration_file_name.php
php artisan migrate --path=/database/migrations/your_videos_table_migration_file_name.php
php artisan migrate --path=/database/migrations/your_tags_table_migration_file_name.php
php artisan migrate --path=/database/migrations/your_taggables_table_migration_file_name.php

table এবং Model গুলো তৈরির কাজ শেষ , এবার table গুলোতে কিছু স্যাম্পল ডেটা তৈরির জন্য আমরা তিনটি seed file তৈরি করব। seed file তিনটি তৈরির জন্য নিম্নোক্ত কমান্ড ব্যবহার করুন :

php artisan make:seed PostsTableSeeder
php artisan make:seed VideosTableSeeder
php artisan make:seed TagsTableSeeder
php artisan make:seed TaggablesTableSeeder

এবার সদ্য তৈরি হওয়া Seed File গুলোকে নিচের মতো করে আপডেট করে নিন :

PostsTableSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;


class PostsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $posts = [
            ['id' => 1, 'name' => 'Post 1'],
            ['id' => 2, 'name' => 'Post 2'],
            ['id' => 3, 'name' => 'Post 3'],
        ];

        DB::table('posts')->insert($posts);
    }
}

VideosTableSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class VideosTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $videos = [
            ['id' => 1, 'name' => 'Video 1'],
            ['id' => 2, 'name' => 'Video 2'],
            ['id' => 3, 'name' => 'Video 3'],
        ];

        DB::table('videos')->insert($videos);

    }
}

TagsTableSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class TagsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $tags = [
            ['id' => 1, 'name' => 'Tag 1'],
            ['id' => 2, 'name' => 'Tag 2'],
            ['id' => 3, 'name' => 'Tag 3'],
        ];

        DB::table('tags')->insert($tags);
    }
}

TaggablesTableSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class TaggablesTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $taggables = [
            ['tag_id' => 1, 'taggable_id' => 1, 'taggable_type' => 'App\\Models\\Post'],
            ['tag_id' => 2, 'taggable_id' => 1, 'taggable_type' => 'App\\Models\\Post'],
            ['tag_id' => 2, 'taggable_id' => 2, 'taggable_type' => 'App\\Models\\Video'],
            ['tag_id' => 3, 'taggable_id' => 2, 'taggable_type' => 'App\\Models\\Video'],
            ['tag_id' => 1, 'taggable_id' => 3, 'taggable_type' => 'App\\Models\\Post'],
        ];

        DB::table('taggables')->insert($taggables);

    }
}

এবার নিম্নোক্ত artisan command ব্যবহার করে উপরোক্ত seed file গুলো রান করুন :

php artisan db:seed --class=PostsTableSeeder
php artisan db:seed --class=VideosTableSeeder
php artisan db:seed --class=TagsTableSeeder
php artisan db:seed --class=TaggablesTableSeeder

Mastering Laravel with ReactJS Course

Model Structure

এবার সদ্য তৈরি হওয়া টেবিল তিনটির জন্য নিম্নোক্ত artisan command এর মাধ্যমে তিনটি Model তৈরি করব।

php artisan make:model Post
php artisan make:model Video
php artisan make:model Tag
php artisan make:model Taggable

অবশেষে, আমরা মডেলগুলিতে relationships গুলি ডিফাইন করতে রেডি। Post এবং Video মডেল উভয়েই একটি tags method থাকবে যা base Eloquent model class দ্বারা প্রদত্ত morphToMany method কে কল করবে।

morphToMany method টি related মডেলের নামের পাশাপাশি “relationship name” গ্রহণ করে। আমাদের মধ্যবর্তী টেবিলের নাম এবং এতে থাকা কীগুলির উপর ভিত্তি করে আমরা relationship টিকে “taggable” হিসাবে উল্লেখ করব:

Post Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;



class Post extends Model
{
    use HasFactory;

    /**
     * Get all of the tags for the post.
     */ 
    public function tags():MorphToMany
    {
        return $this->morphToMany(Tag::class, 'taggable');
    }  
}

Video Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;

class Video extends Model
{
    use HasFactory;
     /**
     * Get all of the tags for the post.
     */
    public function tags(): MorphToMany
    {
        return $this->morphToMany(Tag::class, 'taggable');
    }
}

Defining The Inverse Of The Relationship

এর পরে, Tag মডেলে, আপনার প্রতিটি সম্ভাব্য প্যারেন্ট মডেলের জন্য একটি method নির্ধারণ করা উচিত। সুতরাং, এই উদাহরণে, আমরা একটি posts method এবং একটি videos method ডিফাইন করব। এই উভয় methods এ morphedByMany method এর মাধ্যমে ফলাফল রিটার্ন করবে।

morphedByMany method টি related মডেলের নামের পাশাপাশি “relationship name” গ্রহণ করে। আমাদের মধ্যবর্তী টেবিলের নাম এবং এতে থাকা কীগুলির উপর ভিত্তি করে আমরা relationship টিকে “taggable” হিসাবে উল্লেখ করব:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;

class Tag extends Model
{
    use HasFactory;

    /**
     * Get all of the posts that are assigned this tag.
     */
    public function posts(): MorphToMany
    {
        return $this->morphedByMany(Post::class, 'taggable');
    }
 
    /**
     * Get all of the videos that are assigned this tag.
     */
    public function videos(): MorphToMany
    {
        return $this->morphedByMany(Video::class, 'taggable');
    }
}

Retrieving The Relationship

একবার আপনার ডাটাবেস টেবিল এবং মডেলগুলি ডিফাইন হয়ে গেলে, আপনি আপনার মডেলগুলির মাধ্যমে relationships গুলি অ্যাক্সেস করতে পারেন। উদাহরণস্বরূপ, একটি পোস্টের জন্য সমস্ত ট্যাগ অ্যাক্সেস করতে, আপনি ট্যাগ dynamic relationship property ব্যবহার করতে পারেন:

use App\Models\Post;
use App\Models\Tag;
Route::get('/post-tags',function(){
    $post = Post::find(1);     
    $tag = Tag::find(1);
    $tag2 = Tag::find(2);
    //dd($post->tags);
    //dd($tag->posts);
     dd($tag->videos);
});

আমি মাসুদ আলম, বাংলাদেশের ৩৬ তম Zend Certified Engineer । ২০০৯ সালে কম্পিউটার সাইন্স থেকে বেচেলর ডিগ্রী অর্জন করি। দীর্ঘ ১৫ বছর আমি Winux Soft, SSL Wireless, IBCS-PRIMAX, Max Group, Canadian International Development Agency (CIDA), Care Bangladesh, World Vision, Hellen Keller, Amarbebsha Ltd সহ বিভিন্ন দেশি বিদেশী কোম্পানিতে ডেটা সাইন্স, মেশিন লার্নিং, বিগ ডেটা, ওয়েব ডেভেলপমেন্ট এবং সফটওয়্যার ডেভেলপমেন্ট এর উপর বিভিন্ন লিডিং পজিশন এ চাকরি এবং প্রজেক্ট লিড করি। এছাড়াও বাংলাদেশের ১৮৫ জন জেন্ড সার্টিফাইড ইঞ্জিনিয়ার এর মধ্যে ১২০ এরও অধিক ছাত্র আমার হাতে জেন্ড সার্টিফাইড ইঞ্জিনিয়ার হয়েছেন। বর্তমানে w3programmers ট্রেনিং ইনস্টিটিউট এ PHP এর উপর Professional এবং Advance Zend Certified PHP -8.2 Engineering, Laravel Mastering Course with ReactJS, Python Beginning To Advance with Blockchain, Machine Learning and Data Science, Professional WordPress Plugin Development Beginning to Advance কোর্স করাই। আর অবসর সময়ে w3programmers.com এ ওয়েব টেকনোলজি নিয়ে লেখালেখি করি।

Leave a Reply