لارافيل, Model / 2024-03-04

ما هي Query Scopes في Laravel Eloquent

ما هي Query Scopes في Laravel Eloquent

2024-03-04 وقت القراءه : 3 دقائق

في هذا المقال سأحاول توضيح فكرة query scopes، حيث يوجد منه نوعان globally, locally. والفائدة من إستخدام scope أنه يمكننا من تجميع  جمل الـ query المتكرره في الـ controller، بمعنى أخر عدم تكرار الكود

Local Scope

لفرض لدينا المثال التالي

public function index(){
     $articles = Article::where('active', 1)->get();
 }
 public function search(Request $request){
     $articles = Article::where('active', 1)
         ->where('title', $request->title)
         ->get();
 }

 

كما نلاحظ بالكود أعلاه فإننا قمنا بتكرار where(‘active’,1) في الدالتين، إلا إنه في eloquent يمكن تجنب ذلك كي يصبح الكود أكثر إحترافية، ولمنع التكرار يجب الذهاب على article model ونعرف دالة تقوم فقط بإرجاع شرطwhere(‘active’,1) 

//in article Model
public function scopeActive($query){
     return $query->where('active', 1);
 }

 كما نلاحظ أن تعريف scope يتم من خلال كتابة كلمة scope ثم أي إسم لكن الحرف الأول capital.

ولإستخدام هذه الدالة في  eloquent بداخل الـ controller

public function index(){
     $articles = Article::active()->get();
 }
 public function search(Request $request){
     $articles = Article::active()
         ->where('titel', $request->title)
         ->get();
 }

 نستخدم scope من خلال كتابة إسم scope الذي قمنا بتعريفه في الـ model دون كتابة كلمة scope


مثال أخر

في هذا المثال فإننا نريد إرجاع جميع المقالات التي تم كتابتها خلال 30 يوم

//in ArticleModel
public function scopeNewest($query){
     return $query->where('created_at', '>', now()->subDays(30))
 }


//Controller
public function index(){
     $articles = Article::active()->newest()->get();
 }
 public function search(Request $request){
     $articles = Article::active()->newest()
         ->where('titel', $request->title)
         ->get();
 }

لكن السؤال ، ماذا لو أردنا داخل الدالة index إرجاع المقالات التي كتبت خلال 30 يوم، أما في البحث نريد إرجاع المقالات التي كتبت خلال 10 أيام، كيف سنتعامل مع هذه الحالة بإستخدام scope واحد

لحل هذه المشكله يمكن إستخدام parameter في scope

public function scopeNewest($query, $days){
     return $query->where('created_at', '>', now()->subDays($days));
 }

 

وفي eloquent يمكن تمرير أي رقم (يوم) نريده

public function index(){
     $articles = Article::active()->newest(30)->get();
 }
 public function search(Request $request){
     $articles = Article::active()->newest(10)
         ->where('titel', $request->title)
         ->get();
 }

 



Global Scope

كما رأينا في الأمثلة أعلاه، فإننا قمنا بتعريف scope بداخل الـ model وتم إستدعاءه في الـ controller ، لكن يوجد طريقة أخرى للتعامل مع scope من خلال تعريف global Scope بداخل model

يتم تعريف global scope بداخل model من خلال booted function وبداخلها addGGlobal Scope.

يوجد طريقتين لكتابة Global Scope

الطريقة الأولى : بداخل الموديل مباشرة

//ArticleModel
use Illuminate\Database\Eloquent\Builder;
 protected static function booted(){
     static::addGlobalScope('active', function (Builder $builder) {
         $builder->where('active', 1);
     });
 }

و في الـ controller بداخل function يتم كتابه الـ function مباشرة دون إستدعاء الـ scope

//Controller
public function index(){
     $articles = Article::get();
     return view('/article', compact('articles'));
 }
 public function search(Request $request){
     $articles = Article::where('titel', $request->title)->get();
     return view('/article', compact('articles'));
 }


الطريقة الثانية : إنشاء كلاس منفصل

كما يوجد طريقة أخرى لكتابة Global Scope من خلال إنشاء Global Scope Class أي كلاس منفصل ، هنا سأقوم بإنشاء Global Scope Class بإسم ActiveSope

php artisan make:scope ActiveScope 

بعد تنفيذ الأمر، سيتم إنشاء كلاس جديد بإسم ActiveScope بداخل مجلد Scopes بداخل المجلد Models 

<?php
namespace App\Models\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class ArticleScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        $builder->where('active',1);
    }
}

وفي الـ Article Model بداخل الدالة booted يمكن إستدعاء هذا الكلاس

class Article extends Model
{
    use HasFactory;
    public static function booted()
    {
        static::addGlobalScope(new ActiveScope());
    }
}


لكن ماذا لو قمنا بإنشاء Global Scope لكن لا أريد أن يتم تطبيقها على دالة معينة.

هنا يمكن لنا إستخدام الدالة withoutGlobalScopes

public function index()
{
    $articles=Article::withoutGlobalScopes()->get();
    return $articles;
}

التعليقات
Mogahid gaffar
منذ سنتين

شرح جميل للغايه ! مشكور

زائر
منذ سنتين

يعطيك العافية جدا جميل العمل و الجميل أنه يتم التركيز على أمور متقدمة وغير مفهومة في اللارافيل . بتمنى تستمر في هذه المقالات المفيدة كل الدعم و التشجيع.

moemen gaballa
منذ سنتين

شرح جميل ممتاز جدا جزاك الله خيرا

زائر
منذ سنة

الف مليون شكر لحضرتك

الوريفي
منذ شهر

يعطيكم العافيه

إضافة تعليق
Loading...