في كل مشروع فإننا نتعامل مع العلاقات بين الجداول (hasMany, belongsTo, hasOne.....)، في هذه المقالة سنتحدث عن العلاقات بشكل معمق.
لفرض أن لدي الجداول التالية
public function up() { Schema::create('countries', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); }
public function up() { Schema::create('cities', function (Blueprint $table) { $table->id(); $table->string('name'); $table->foreignId('country_id')->constrained(); $table->timestamps(); }); }
public function up() { Schema::create('doctors', function (Blueprint $table) { $table->id(); $table->string('name'); $table->foreignId('city_id')->constrained(); $table->timestamps(); }); }
كما نلاحظك أن لدي جدول countries وهو يحتوي على مجموعة من المدن وكل مدينة يتواجد بها مجموعة من الأطباء.
لنفرض أننا نريد أن يتم إظهار الدولة مع عدد الأطباء المتواجدين بها؟
هنا في Country Model نحتاج لتحديد العلاقة بين جدولي countries و doctors
class Country extends Model { use HasFactory; public function doctors(){ return $this->hasManyThrough(Doctor::class, City::class); } }
كما نلاحظ أن نوع العلاقة هو hasManyThrough اي أن الدولة تحتوي على العديد من الأطباء ويتم الربط بين الجدولين من خلال City Model.
لكتابة جملة الإستعلام في الـ CountryController
class DoctorController extends Controller { public function index(){ $countries = Country::with('doctors')->get(); return view('country',compact('countries')); } }
وفي ملف blade للعرض
@foreach($countries as $country) <tr> <td>{{ $country->name }}</td> <td>{{ $country->doctors->count() }}</td> </tr> @endforeach
لكن نلاحظ في جملة الإستعلام أننا قمنا بتحميل جميع الـ Doctor Model ونحن نحتاج فقط إلى العدد، لذلك يمكن تعديل جملة الإستعلام وإستخدام الدالة
class DoctorController extends Controller { public function index(){ $countries = Country::withCount('doctors')->get(); return view('country',compact('countries')); } }
وفي ملف blade يمكن الوصول للعدد من خلال doctors_count كـ property
@foreach($countries as $country) <tr> <td>{{ $country->name }}</td> <td>{{ $country->doctors_count }}</td> </tr> @endforeach
لو ألقينا نظرة على laravel debug bar
نرى أنه لا مشكله حيث تم تنفيذ جملة إستعلام واحدة وبوقت قليل جداً.
ماذا إذا طلب منا أن يتم عرض قائمة الأطباء مع الدول التي يعملون بها؟
بداية نحتاج لتحديد العلاقة بين Doctor Model و City Model
class Doctor extends Model { use HasFactory; public function city(){ return $this->belongsTo(City::class); } }
ومن ثم تحديد العلاقة بين City Model و Country Model
class City extends Model { use HasFactory; public function country(){ return $this->belongsTo(Country::class); } }
ومن ثم كتابة جملة الإستعلام
public function index(){ $doctors=Doctor::with('city.country')->get(); return view('country',compact('doctors')); }
هنا يجب الإنتباه إنه تم إستخدام eager loading
وفي ملف blade للعرض
@foreach($doctors as $doctor) <tr> <td>{{ $doctor->name }}</td> <td>{{ $doctor->city->country->name }}</td> </tr> @endforeach
بالتالي سوف نحصل على هذه النتيجة
لو ألقينا نظرة على laravel debug bar
نرى إنه تم تنفيذ ٣ جمل إستعلام، وهذا جيد، لكن نحن لا نريد المدن ونرى إنه تم جلب City Model ونحن لسنا بحاجة إليه، ولتجنب ذلك، يمكن إستخدام حزمة
staudenmeir/belongs-to-through composer require staudenmeir/belongs-to-through:"^2.5"
كما نلاحظ من إسم الإضافة أنها توفر لنا علاقة جديده وهي belongs-to-through فهي لا توفرها لارافيل بالشكل الإفتراضي بل توفر hasMany-Through
بعد تثبيت الإضافة نحتاج لإستخدام trait التالي في Doctor Model
use \Znck\Eloquent\Traits\BelongsToThrough;
class Doctor extends Model { use HasFactory; use \Znck\Eloquent\Traits\BelongsToThrough; public function city(){ return $this->belongsTo(City::class); } public function country(){ return $this->belongsToThrough(Country::class, City::class); } }
بعد إضافة trait نستطيع إضافة علاقة belongsToThrough في Doctor Model وذلك حتى يتم الربط بين جدولي Doctor و Country من خلال City::class.
الأن في eloquent نستطيع إستخدام country دون إستخدام city
public function index(){ $doctors=Doctor::with('country')->get(); return view('country',compact('doctors')); }
وفي ملف blade
@foreach($doctors as $doctor) <tr> <td>{{ $doctor->name }}</td> <td>{{ $doctor->country->name }}</td> </tr> @endforeach
لو ألقينا نظره على laravel debug bar
نرى أنه تم تنفيذ جملتين إستعلام، وكذلك لم يتم تحميل City Model
ماذا لو أن كل طبيب يتبع له مجموعة من المرضى، ونريد أن يتم عرض كل دولة مع المرضى الموجودين بها؟
بداية نحتاج لإنشاء جدول Patients
public function up() { Schema::create('patients', function (Blueprint $table) { $table->id(); $table->string('name'); $table->foreignId('doctor_id')->constrained(); $table->timestamps(); }); }
كما نلاحظ أنه تم إضافة حقل doctor_id وهو foreign key.
ولأداء أفضل فإنه يفضل إستخدام إضافة
staudenmeir/eloquent-has-many-deep composer require staudenmeir/eloquent-has-many-deep:"^1.7"
حيث يمكن لنا إستخدام علاقة جديدة وهي hasManyDeep
بعد تثبين الحزمة في Country Model يمكن لنا إستخدام العلاقة مع إستخدام الـ trait
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
class Country extends Model { use HasFactory; use \Staudenmeir\EloquentHasManyDeep\HasRelationships; public function patients() { return $this->hasManyDeep(Patient::class, [City::class, Doctor::class]); } }
كما نلاحظ إنه تم إستخدام مصفوفة من الـ model، ويمكننا إضافة المزيد إن إحتجنا لذلك، وكذلك تم إستخدام العلاقة hasManyDeep.
في eloquent
class DoctorController extends Controller { public function index(){ $countries = Country::withCount('patients')->get(); return view('country',compact('countries')); } }
وفي blade
@foreach($countries as $country) <tr> <td>{{ $country->name }}</td> <td>{{ $country->patients_count }}</td> </tr> @endforeach
وبالنظر إلى debug bar
وبذلك نكون قد جلبنا الدولة مع عدد المرضى التي بداخلها.
عندي المنشور له عدة tags تابعة لقسم كيف احصل عل المنشور ات من الاقسام
very good
زائر
كيف انشاء العلاقة اذاكانت من hasMany الى belongsToMany ...