Eloquent المتقدم: التعدد الشكلي وخرائط التحويل
Eloquent المتقدم: التعدد الشكلي وخرائط التحويل
العلاقات متعددة الأشكال في Laravel تسمح لنموذج بالانتماء إلى أكثر من نوع واحد من النماذج في ارتباط واحد. يستكشف هذا الدرس الأنماط المتقدمة للتعدد الشكلي وخرائط التحويل وأفضل الممارسات لبناء مخططات قواعد بيانات مرنة.
فهم العلاقات متعددة الأشكال
توفر العلاقات متعددة الأشكال طريقة لإنشاء ارتباط واحد يمكنه الإشارة إلى أنواع نماذج متعددة. هذا مفيد للميزات مثل التعليقات أو الإعجابات أو الصور التي يمكن أن تنتمي إلى كيانات مختلفة.
<?php
// ترحيل قاعدة البيانات للتعليقات متعددة الأشكال
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->text('body');
$table->morphs('commentable'); // ينشئ commentable_id و commentable_type
$table->timestamps();
});
// نموذج التعليق
class Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
}
// نموذج المنشور
class Post extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
// نموذج الفيديو
class Video extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
// الاستخدام
$post = Post::find(1);
$post->comments()->create(['body' => 'منشور رائع!']);
$video = Video::find(1);
$video->comments()->create(['body' => 'فيديو مذهل!']);
// استرجاع العنصر الأب
$comment = Comment::find(1);
$parent = $comment->commentable; // يرجع نسخة من Post أو Video
morphs() تنشئ تلقائياً كلاً من عمود المعرف (commentable_id) وعمود النوع (commentable_type). يمكنك أيضاً استخدام nullableMorphs() للعلاقات الاختيارية.
خرائط التحويل: فصل النماذج عن قاعدة البيانات
بشكل افتراضي، يخزن Laravel اسم الفئة المؤهل بالكامل في عمود النوع (مثل "App\Models\Post"). تسمح خرائط التحويل باستخدام أسماء مستعارة أقصر، مما يجعل قاعدة البيانات أكثر قابلية للصيانة ومرونة أمام تغييرات مساحة الأسماء.
<?php
// app/Providers/AppServiceProvider.php
use Illuminate\Database\Eloquent\Relations\Relation;
public function boot()
{
Relation::enforceMorphMap([
'post' => 'App\Models\Post',
'video' => 'App\Models\Video',
'user' => 'App\Models\User',
]);
}
// ستخزن قاعدة البيانات الآن 'post' بدلاً من 'App\Models\Post'
// هذا يجعل الكود أكثر قابلية للصيانة ومستقلاً عن قاعدة البيانات
// استرجاع خريطة التحويل
$map = Relation::morphMap();
// الحصول على الاسم المستعار للنموذج
$alias = Relation::getMorphAlias(Post::class); // يرجع 'post'
العلاقات متعددة الأشكال من متعدد لمتعدد
تسمح العلاقات متعددة الأشكال من متعدد لمتعدد لنموذج بالانتماء إلى أنواع متعددة من النماذج في علاقة مشتركة. هذا مثالي للميزات مثل الوسوم أو الفئات التي يمكن تطبيقها على أنواع محتوى مختلفة.
<?php
// ترحيل قاعدة البيانات لجدول taggables
Schema::create('tags', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
Schema::create('taggables', function (Blueprint $table) {
$table->foreignId('tag_id')->constrained()->onDelete('cascade');
$table->morphs('taggable');
$table->timestamps();
});
// نموذج الوسم
class Tag extends Model
{
public function posts()
{
return $this->morphedByMany(Post::class, 'taggable');
}
public function videos()
{
return $this->morphedByMany(Video::class, 'taggable');
}
}
// نموذج المنشور
class Post extends Model
{
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
// نموذج الفيديو
class Video extends Model
{
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
}
// الاستخدام
$post = Post::find(1);
$post->tags()->attach([1, 2, 3]);
$tag = Tag::find(1);
$allPosts = $tag->posts; // جميع المنشورات بهذا الوسم
$allVideos = $tag->videos; // جميع الفيديوهات بهذا الوسم
// إزالة الوسوم
$post->tags()->detach([2, 3]);
// مزامنة الوسوم (إرفاق جديد، إزالة المفقود)
$post->tags()->sync([1, 4, 5]);
أنواع متعددة الأشكال مخصصة
يمكنك إنشاء علاقات متعددة الأشكال مخصصة تتجاوز الأنماط القياسية من خلال تعريف دوال مخصصة واستخدام منشئ استعلامات العلاقة.
<?php
// علاقة واحد لواحد متعددة الأشكال
class Image extends Model
{
public function imageable()
{
return $this->morphTo();
}
}
class User extends Model
{
public function image()
{
return $this->morphOne(Image::class, 'imageable');
}
}
class Product extends Model
{
public function images()
{
return $this->morphMany(Image::class, 'imageable');
}
}
// نطاقات استعلام متعددة الأشكال مخصصة
class Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
public function scopeForPosts($query)
{
return $query->where('commentable_type', 'post');
}
public function scopeForVideos($query)
{
return $query->where('commentable_type', 'video');
}
}
// الاستخدام
$postComments = Comment::forPosts()->get();
$videoComments = Comment::forVideos()->get();
// التحميل المسبق للعلاقات متعددة الأشكال
$comments = Comment::with('commentable')->get();
// التحميل المسبق المقيد
$comments = Comment::with([
'commentable' => function ($query) {
$query->where('status', 'published');
}
])->get();
// قيود التحميل المسبق متعدد الأشكال
$posts = Post::with([
'comments' => function ($query) {
$query->where('approved', true);
}
])->get();
أنماط متقدمة متعددة الأشكال
<?php
// علاقات متعددة الأشكال متداخلة
class Activity extends Model
{
public function subject()
{
return $this->morphTo();
}
public function causer()
{
return $this->morphTo();
}
}
// تسجيل النشاط مع علاقات متعددة الأشكال متعددة
Activity::create([
'subject_type' => 'post',
'subject_id' => 1,
'causer_type' => 'user',
'causer_id' => 5,
'description' => 'تم نشر المنشور',
]);
// علاقات متعددة الأشكال مع بيانات محورية
class User extends Model
{
public function favoriteables()
{
return $this->hasMany(Favorite::class);
}
public function favoritePosts()
{
return $this->morphedByMany(Post::class, 'favoriteable')
->withPivot('favorited_at', 'notes')
->withTimestamps();
}
}
// الإرفاق مع بيانات محورية
$user->favoritePosts()->attach($postId, [
'favorited_at' => now(),
'notes' => 'مقالة مفيدة حقاً',
]);
// استرجاع البيانات المحورية
$user->favoritePosts->each(function ($post) {
echo $post->pivot->favorited_at;
echo $post->pivot->notes;
});
تمرين 1: نظام الإعجابات متعدد الأشكال
أنشئ نظام إعجابات متعدد الأشكال يسمح للمستخدمين بالإعجاب بالمنشورات والتعليقات والفيديوهات. نفذ خرائط التحويل وأضف دوال لـ:
- التحقق مما إذا كان المستخدم قد أعجب بعنصر
- الحصول على إجمالي عدد الإعجابات لأي عنصر قابل للإعجاب
- الحصول على جميع العناصر التي أعجب بها المستخدم مجموعة حسب النوع
- إلغاء الإعجاب بعنصر
تمرين 2: سجل النشاط
ابنِ نظام سجل نشاط باستخدام العلاقات متعددة الأشكال يتتبع:
- موضوع النشاط (ما الذي تم تغييره)
- مسبب النشاط (من قام بالتغيير)
- تخزين حالات قبل/بعد في أعمدة JSON
- إنشاء دالة لوحة تحكم لاسترجاع جميع الأنشطة للمستخدم
- تنفيذ التصفية حسب نوع النشاط ونطاق التاريخ
تمرين 3: نظام وسوم متقدم
أنشئ نظام وسوم متعدد الأشكال من متعدد لمتعدد مع الميزات التالية:
- يمكن تطبيق الوسوم على المنشورات والفيديوهات والمنتجات
- تخزين بيانات وصفية للوسم (من وسم، متى، السياق)
- تنفيذ دالة للحصول على الوسوم الأكثر شعبية عبر جميع الأنواع
- إنشاء دالة بحث تجد العناصر حسب الوسم عبر جميع الأنواع
- إضافة فئات للوسوم (مثل "تقني"، "تسويقي"، "تصميم")