Eloquent Relation

Laravel Eloquent မှာ ပုံမှန် အားဖြင့် အသုံးပြုကြတာကတော့

  1. One To One
  2. One to Many
  3. Many to Many

တို့ပါ။

ဒါပေမယ့် ထပ်ပြီး အသုံးပြုနိုင်သေးတာကတော့

  1. Through
  2. Morph

တို့ပါ။

Through

Through ကို ဘယ်လို နေရာတွေမှာ အသုံးပြုကြလဲ ဆိုတော့

UserGroup မှာ User ရှိတယ်။

User မှာ Post တွေ ရှိတယ်။

Premium User Group က တင်ထားသည့် Post တွေကို လိုချင်တယ် ဆိုပါဆို့။ အဲဒီလိုမျိုး Use Case တွေကို Through ကို အသုံးပြုနိုင်ပါတယ်။

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

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
    ];


    function posts() {
        return $this->hasMany(Post::class);
    }

}
class UserGroup extends Model{
    use HasFactory;

    function users() {
        return $this->hasMany(User::class);
    }

    function posts() {
        return $this->hasManyThrough(Post::class,User::class);
    }
}

ဒီ code မှာ ဆိုရင် UserGroup က posts တွေကို ရဖို့ အတွက် User class ကနေ တဆင့် ခေါ်ထားတယ် ဆိုပြီး တွေ့နိုင်ပါတယ်။

Migration table ကို ကြည့်ရအောင်။

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

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('users');
    }
};
return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('user_groups', function (Blueprint $table) {
            $table->id();
            $table->text("name");
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('user_groups');
    }
};
return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->text("title");
            $table->text("body");
            $table->foreignId("user_id");
            $table->timestamps();
        });
    }

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

ဒီ migration file ကို ကြည့်လိုက်ရင် သဘောပေါက်သွားမှာပါ။

Posts table မှာ user_id ရှိတယ်။ Users table မှာ user_group_id ရှိပါတယ်။ ဒါကြောင့် UserGroups table က posts စာရင်း ကို ခေါ်လို့ရသွားတာပါ။ ဥပမာ SQL အရ ဆိုရင် အောက်ပါ အတိုင်းဖြစ်ပါလိမ့်မယ်။

SELECT * FROM Posts 
INNER JOIN Users ON Users.id = Posts.id
INNER JOIN UserGroups ON Users.user_group_id = UserGroups.id
WHERE UsersGroup.id = 1

MorphOne

Larvel ရဲ့ Eloquent relationship ထဲမှာ Morph က တော်တော်မိုက်တယ်။

Post က Text Post လည်း ဖြစ်နိုင်သလို VideoPost လည်း ဖြစ်နိုင်တယ်။

ပုံမှန် အားဖြင့် Post က text post အတွက် relationship တစ်ခု။ video post အတွက် relationship တစ်ခု ဖန်တီး ရမှာပါ။ Morph နဲ့ ဆိုရင် တော့ postable_type နဲ့ postable_id ပေါ်မှာ မူတည်ပြီး ခွဲလို့ရပါတယ်။

idpostable_typepostable_idnameuser_id
1App/Models/VideoPost1Sample Video1
2App/Models/TextPost1Sample Post3
sample dataset for morph

အခု table မှာဆိုရင် id 1 က VideoPost ဖြစ်ပြီး VideoPost ရဲ့ id 1 ကို ချိတ်ထားပြီး id 2 က TextPost ဆိုရင် TextPost table ရဲ့ 1 နဲ့ ချိတ်ထားပါတယ်။

Model မှာ ဆိုရင်

class Post extends Model
{
    use HasFactory;
    use LikeableRealation;

    function user() {
        return $this->belongsTo(User::class);
    }


    function postable() {
        return $this->morphTo();
    }

    
}

postable မှာ morphTo နဲ့ ချိတ်ထားသည့် အတွက်ကြောင့် postable_type က VideoPost ဆိုရင် VideoPost model ကို ရမှာ ဖြစ်ပြီး TextPost ဆိုရင် TextPost model ကို ရမှာပါ။

class VideoPost extends Model
{
    use HasFactory;
    use PostablePost;

}

class TextPost extends Model
{
    use HasFactory;
    use PostablePost;

}

PostablePost ကတော့ trail နဲ့ ရေးထားပါတယ်။

trait PostablePost {
    function post() {
        return $this->morphOne(Post::class,"postable");
    }
}

VideoPost , TextPost ကနေ post ကို ပြန်ခေါ်မယ် ဆိုရင်တော့ morphOne နဲ့ ပြန်ခေါ်လို့ရပါလိမ့်မယ်။

Migration file ကို ကြည့်ရအောင်။

 Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->text("title");
            $table->morphs("postable"); //auto include _type and _id
            $table->foreignId("user_id");
            $table->timestamps();
})

$table->morphs ဆိုသည့် code က နောက်မှာ _type နဲ့ _id column အလိုလို ထည့်ပေးပါတယ်။ ဒါကြောင့် postable_type , postable_id ဆိုပြီး ကြေငြာနေဖို့ မလိုပါဘူး။

MorphToMany

morphToMany ကတော့ နည်းနည်းရှုပ်သွားပါပြီ။ User က comment ကို like လုပ်မယ်။ Post ကို like လုပ်မယ်။ ပုံမှန်အားဖြင့် Comment နဲ့ Post က likes_type နဲ့ likes_id ပဲ ပါပါမယ်။ အခု user_id ပါ ထပ်ပြီး ပါလာပါပြီ။

ဒါကြောင့် likes table တစ်ခု ထပ်ပြီး create လုပ်သည့် အမှာ user_id နဲ့ ထပ် join ပါမယ်။

Schema::create('likes', function (Blueprint $table) {
            $table->foreignId("user_id");
            $table->morphs("likes");
            $table->timestamps();
});

ဒီ table မှာ id မပါပါဘူး။ နောက်ပြီး သတိထားရမှာက morphtoMany သုံးမယ်ဆိုရင် morphs က table name နဲ့ တူမှ အဆင်ပြေမှာပါ။ $table->morphs(“likes”) က likes_id နဲ့ likes_type ကို အလိုလို create လုပ်သွားမှာပါ။

trait LikeableRealation {
    function like(User $user) {
        $this->likes()->attach($user);
    }

    function likes() {
        return $this->morphToMany(User::class,"likes")->withTimestamps();
    }
}
class Post extends Model
{
    use HasFactory;
    use LikeableRealation;

    function user() {
        return $this->belongsTo(User::class);
    }

    function tags() {
        return $this->belongsToMany(Tag::class);
    }

    function postable() {
        return $this->morphTo();
    }

    
}
class Comment extends Model
{
    use HasFactory;
    use LikeableRealation;
    
    function user() {
        return $this->belongsTo(User::class);
    }
    
    function post() {
        return $this->belongsTo(Post::class);
    }

    
}
class User extends 
{
    //Other code
    public function likedPosts()
    {
        return $this->morphedByMany(Post::class, 'likes');
    }

    public function likedComments()
    {
        return $this->morphedByMany(Comment::class, 'likes');
    }
}

$post->likes ဆိုရင် likes လုပ်ထားသည့် users object တွေ ရမှာပါ။

$user->likedPosts ဆိုရင်တော့ အဲဒီ user liked လုပ်ထားသည့် post တွေကို ရမှာပါ။

Source code ကို https://github.com/saturngod/eloquent_relationship မှာ ရနိုင်ပါတယ်။