اذهب إلى المحتوى

السؤال

Recommended Posts

  • 1
نشر

إن العلاقات متعددة الأشكال هي أن يكون لديك نماذج يمكن أن تنتمي إلى أكثر من كيان واحد. يحتفظ هذا النوع من النماذج بالبنية نفسها التي لا تتغير بغض النظر عن النموذج الآخر المرتبط به.

من الأمثلة الشائعة على هذا النوع من السيناريوهات هي (التعليقات). في مدونة على سبيل المثال ، يمكن أن توضع التعليقات على منشور محدد أو على صفحة مستقلة مع الحفاظ على الهيكل نفسه بغض النظر عما إذا كان منشور أو صفحة. أي بمعنى آخر ستبقى جميع الخصائص للتعليق ثابتة مثل (الاسم، تاريخ التعليق، محتوى التعليق،...).

مع أخذ المثال المذكور أعلاه في الاعتبار ، لدينا كيانان: Post و Page. للحصول على تعليقات على كل منها ، يمكننا أن نقرر إنشاء قاعدة بياناتنا على النحو التالي:

posts:
  id الرقم المعرف للمقال
  title عنوان المقال
  content محتوى المقال
  
posts_comments:
  id رقم معرف فريد
  post_id الرقم المعرف للمقال
  comment التعليق
  date تاريخ التعليق
  
pages:
  id الرقم المعرف للصفحة
  body محتوى الصفحة
  
pages_comments:
  id رقم معرف فريد
  page_id الرقم المعرف للصفحة
  comment التعليق
  date تاريخ التعليق

ولكن نلاحظ أنه باعتماد النهج السابق، وجب علينا إنشاء جدولين للتعليقات، جدول للتعليقات الخاص بالصفحة، وجدول آخر للتعليقات الخاصة بالمنشور أو المقال. وكليهما يقومان بنفس الدور تماماً ويحتويان نفص الخصائص، إنما الاختلاف الوحيد هو بالمكان الذي يتواجدان فيه (مرة بالمنشور ومرة أخرى بالصفحة).
مع استخدام العلاقات المتعددة، يمكننا تبسيط البينة السابقة ومنع التكرار الذي حصل، فتصبح لدينا بنية الجداول كالتالي:

posts:
  id الرقم المعرف للمنشور
  title عنوان المنشور
  content محتوى المنشور
  
pages:
  id الرقم المعرف للصفحة
  body محتوى الصفحة
  
comments:
  id الرقم المعرف للتعليق
  commentable_id الرقم المعرف للمكان الذي يوجد فيه التعليق (أي رقم معرف للصفحة أو رقم معرف للمنشور)
  commentable_type نوع المكان الهدف (صفحة أو منشور)
  date تاريخ التعليق
  body محتوى التعليق

وبذلك نلاحظ لدينا عمودين جديدين مهمين يجب الانتباه إليهما: commentable_id و commentable_type. وبدورهما قاما باختصار تكرار جدول التعليق مرتين وتكرار البيانات. وهذا ببساطة هو مفهوم العلاقات المتعددة الأشكال.

الآن لنأخذ المثال السابق ونقوم بتضمينه ضمن لارافيل:

عند إنشاء الجداول للتهجير نقوم بالتالي:

Schema::create('posts', function (Blueprint $table) {
    $table->increments('id');
    $table->string('title');
    $table->text('content');
});

Schema::create('pages', function (Blueprint $table) {
    $table->increments('id');
    $table->text('body');
});

Schema::create('comments', function (Blueprint $table) {
    $table->increments('id');
    $table->morphs(‘comment’); //->  ستنشئ تلقائيًا عمودين باستخدام النص الذي تم تمريره إليه
    $table->text('body');
    $table->date('date');
});

بوضع morphs سيقوم تلقائياً بإنشاء العمودين commentable_id و commentable_type.
ثم عند إنشاء جدول التعليقات يمكننا تعريف دالة تساعدنا بالحصول على أي مودل آخر يملك تعليق (أي يمكننا مباشرة الحصول على تعليقات المنشور و تعليقات الصفحة) بدالة واحدة بدلاً من اللجوء لآليات أخرى أكثر تعقيداً، كالتالي:


<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
 
	//باستخدام هذا التابع نستطيع الآن أن نحصل على أي مودل يملك تعليق أي مودل الصفحة ومودل المنشور

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

وعند إنشاء الجدول الهدف (الكيان الذي سيحوي التعليقات) على سبيل المثال جدول المنشور:

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    //هذه الدالة ستقوم باستعادة جميع التعليقات الموجودة على منشور محدد
    public function comments()
    {
        return $this->morphMany('App\Comment', 'commentable');
    }
}

نلاحظ قمنا باستخدام morphMany للدلالة بأن المنشور قد يحوي عدة تعليقات، وبذلك نستطيع الحصول على كافة التعليقات الموجودة على هذا المنشور. وكذلك الأمر طبعاً بالنسبة (للصفحة) نكرر الخطوة السابقة morphMany.

يمكنك أيضاً الاطلاع على باقي أشكال العلاقات وأنواع العلاقات متعددة الأشكال الأخرى من التوثيق الرسمي في لارافيل.

 

  • 0
نشر

تُعتبر العلاقات المُتعددة الأشكال من المواضيع المُتقدمة في الإطار، و العديد من المطورين يجهلون إستخدامها، بسبب أنها معقدة قليلاً، يُمكنك عدم إستخدامها لكن توجد بعض الحالات إذا ما استخدمت فيها هذا النوع من العلاقات ستُسهل عليك الكثير من الأمور، 

لنفترض أنك تعمل على مشروع، و هذا المشروع به مودل إسمه User و مودل ثاني إسمه Post و مودل ثالث إسمه Photo حيث أن لكل User صورة و لكل مقال صورة، و يمكن أيضاً إضافة نماذج أخرى لها علاقة مع النموذج Photo. قد يكون Product او أي كان. لنتوقف على User و Post بالإضافة إلى Photo فقط.

في هذه الحالة كيف ستعمل؟ هل ستقوم بإنشاء نموذج PostPhoto لصور المقالات و جدول خاص به و نموذج ثاني UserPhoto لصور المستخدمين و جدول خاص به أيضاً، و إذا كان هناك نماذج أخرى تتعلق بالصور تُنشئ لها جداول أخرى في قاعدة البيانات أيضاً،

ألا ترى أن هذه الطريقة ليست عملية! إذاً ما الحل؟ هل ستستخدم جدول واحد للصور photos و تضع فيه حقلين post_id و user_id و تجعلهما nullable ثم إن كانت الصورة تتعلق ب Post تضع مُعرف المقال في الحقل post_id و تترك الآخر null أما إن كانت الصورة تتعلق ب User تعكس الأمر. ماذا إن كان نموذج آخر يتعلق بالصور و ليكن Product هل ستضيف حقل آخر product_id لجدول الصور!! ألا تُلاحظ أن هذه الطريقة ليست عملية لأننا نضيف كل مرة حقل جديد، و كل مازادت الحقول التي ليس لها داع، تنفيذ الإستعلامات سيكون بطيء،

و بالتالي فإن الأفضل هو إستخدام جدول واحد و حقلين فقط بغض النظر عن الحقول الإضافية للنموذج فنحن نتحدث عن حقول العلاقات. و هذين الحقلين هما:

  • imageable_id سيتم فيه تخزين مُعرف النموذج سواء كان User او Post او اي كان
  • imageable_type و فيه سيتم تخزين نوع النموذج مثلا Post او User على شكل سلسلة نصية

و بهذه الطريقة تمكنا من إستخدام جدول واحد و تخلصنا من الحقول الإضافية، أيضاً إذا أردنا مستقبلاً إضافة نموذج ثالث له علاقة مع نموذج الصور لسنا بحاجة للتعديل على هيكلية جدول الصور.

ملفات التهجير ستكون بهذا الشكل:

جدول photos:

<?php

class CreatePhotosTable extends Migration
{

  public function up()
  {
    Schema::create('photos', function (Blueprint $table) {
      $table->id();
      $table->unsignedInteger('imageable_id');
      $table->string('imageable_type');
      // other fields
      $table->timestamps();
    });
  }

  // ...

}

العلاقات:

النموذج Photo:

<?php

class Photo extends Model
{

  // ...

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

}

النموذج User:

<?php

class User extends Model
{

  // ...

  public function photo()
  {
    return $this->morphOne(Photo::class, 'imageable');
  }
}

النموذج Post:

<?php

class Post extends Model
{

  // ...

  public function photo()
  {
    return $this->morphOne(Photo::class, 'imageable');
  }
}

هذه العلاقة تُسمى ( one to one polymorphic) أي واحد لواحد مُتعدد الأشكال يعني لكل مُستخدم صورة و لكل مقال صورة و لكل منتج صورة.

ماذا لو كان لدينا الحالة التالية لكل مستخدم عدة صور و لكل مقال عدة صور هذه العلاقة تُسمى One To Many (Polymorphic) هي تُشبه العلاقة الأولى فعند تطبيقها نحتاج فقط لتعديل إسم العلاقة من photo إلى photos حتى تُصبح مُعبرة و نُغير التابع morphOne إلى morphMany بهذا الشكل في كل من النموذج User و النموذج Post:

<?php

public function photos()
{
  return $this->morphMany(Photo::class, 'imageable');
}

ماذا لو أردنا أن تكون صورة ما مُتعلقة بكل من النموذج User و النموذج Post، يعني صورة تابعة لمجموعة من المستخدمين و تابعة في نفس الوقت لمجموعة من المقالات، هذه العلاقة تُسمى Many To Many (Polymorphic) في هذه الحالة نحتاج إلى جدول آخر يُسمى Pivot Table يحتوي على التالي:

imageables
    photo_id - integer
    imageable_id - integer
    imageable_type - string

بحيث photo_id يُشير إلى معرف الصورة في جدول الصور الذي يكون بالشكل التالي:

 

photos
    id - integer
    -- other fields

حيث أن العلاقات ستكون بالشكل التالي:

النموذج Post و النموذج User:

<?php


public function photos()
{
  return $this->morphToMany(Photo::class, 'imageable');
}

و في النموذج Photo بهذا الشكل:

<?php

public function posts()
{
  return $this->morphedByMany(Post::class, 'imageable');
}

public function users()
{
  return $this->morphedByMany(User::class, 'imageable');
}

افضل شيء للتعلم هو التطبيق يُمكنك تخيل سيناريو في ذهنك و تطبيقه. أيضا بإمكانك الإطلاع على توثيق موسوعة حسوب: العلاقات مُتعددة الأشكال

انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

  • إعلانات

  • تابعنا على



×
×
  • أضف...