۲۰ نکته و ترفند درباره Eloquent در لاراول

ترجمه و تالیف : فاطمه شیرزادفر
تاریخ انتشار : 30 مرداد 99
خواندن در 3 دقیقه
دسته بندی ها : لاراول

Eloquent ORM به نظر می‌رسد یک مکانیزم ساده است، اما اگر به درون آن دقت کنیم، بسیاری از کارکردهای نیمه‌پنهان و روش‌های کمتر شناخته شده برای دستیابی بیشتر به آن وجود دارد. در این مقاله از راکت چند ترفند را به شما نشان خواهم داد.

۱. افزایش و کاهش

به جای این کار:

$article = Article::find($article_id);
$article->read_count++;
$article->save();

می‌توانید این کار را انجام دهید:

$article = Article::find($article_id);
$article->increment('read_count');

همچنین این‌ها نیز کار خواهند کرد:

Article::find($article_id)->increment('read_count');
Article::find($article_id)->increment('read_count', 10); // +10
Product::find($produce_id)->decrement('stock'); // -1

۲. متدهای X یا Y

Eloquent فانکشن‌های زیادی دارد که دو متد را ترکیب می‌کند، مثل “ لطفاً X را انجام دهید، در غیر این صورت Y را انجام دهید“.

مثال ۱ -findOrFail():

به جای انجام این کار:

$user = User::find($id);
if (!$user) { abort (404); }

این کار را انجام دهید:

$user = User::findOrFail($id);

مثال ۲ -firstOrCreate():

به جای انجام این کار :

$user = User::where('email', $email)->first();
if (!$user) {
  User::create([
    'email' => $email
  ]);
}

فقط این کار را انجام دهید:

$user = User::firstOrCreate(['email' => $email]);


۳. متد Model boot()

 یک مکان جادویی به نام boot() در یک Eloquent model وجود دارد که می‌توانید رفتار پیش‌فرض را نادیده بگیرید:

class User extends Model
{
    public static function boot()
    {
        parent::boot();
        static::updating(function($model)
        {
            // do some logging
            // override some property like $model->something = transform($something);
        });
    }
}

احتمالاً یکی از محبوب‌ترین نمونه‌ها تعیین مقدار فیلد در زمان ساخت model object است. بیایید بگوییم که شما در آن لحظه می‌خواهید فیلدUUID را تولید کنید.

public static function boot()
{
  parent::boot();
  self::creating(function ($model) {
    $model->uuid = (string)Uuid::generate();
  });
}

۴. رابطه با شرایط و نظم

این یک روش معمول برای تعریف رابطه است:

public function users() {
    return $this->hasMany('App\User');    
}

اما آیا می‌دانید که در این نقطه ما می‌توانید اضافه یا بر اساس چیزی مرتبش کنیم؟

به عنوان مثال اگر رابطه خاصی برای بعضی از انواع کاربران می‌خواهید، همچنین با ایمیل مرتب می‌شوند، می‌توانید این کار را انجام دهید:

public function approvedUsers() {
    return $this->hasMany('App\User')->where('approved', 1)->orderBy('email');
}

۵. ویژگی‌های مدل: timestamp ها ،append ها و …

چند پارامتر از یک Eloquent model ،در قالب ویژگی‌های آن کلاس وجود دارد. مشهورترین آن‌ها احتمالاً این موارد هستند:

class User extends Model {
    protected $table = 'users';
    protected $fillable = ['email', 'password']; // which fields can be filled with User::create()
    protected $dates = ['created_at', 'deleted_at']; // which fields will be Carbon-ized
    protected $appends = ['field1', 'field2']; // additional values returned in JSON
}

اما صبر کنید، چیزهای بیشتری هم وجود دارد:

protected $primaryKey = 'uuid'; // it doesn't have to be "id"
public $incrementing = false; // and it doesn't even have to be auto-incrementing!
protected $perPage = 25; // Yes, you can override pagination count PER MODEL (default 15)
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at'; // Yes, even those names can be overridden
public $timestamps = false; // or even not used at all

و حتی موارد جالب دیگری نیز وجود دارد که من جالب‌ترین آن‌ها را ذکر کردم، برای اطلاعات بیشتر لطفاً کد abstract Model class پیش‌فرض را چک کنید و تمام صفات مورد استفاده را بررسی کنید.

۶. پیدا کردن چندین ورودی

همه متد find() را می‌شناسند، درست است؟

$user = User::find(1);

من بسیار تعجب کردم که چطور تعداد کمی از این موضوع آگاه هستند که می‌توان چندین آیدی را به عنوان یک آرایه بپذیرند:

$users = User::find([1,2,3]);

۷. WhereX

یک روش زیبا برای چرخاندن این وجود دارد:

$users = User::where('approved', 1)->get();

به این:

$users = User::whereApproved(1)->get();

بله می‌توانید نام هر فیلد را تغییر دهید و آن را به عنوان suffix (پسوند) به where اضافه کنید و این به صورت جادویی کار خواهد کرد.

همچنین برخی از متدهای از پیش تعریف شده در Eloquent ، مربوط به تاریخ و زمان وجود دارد:

User::whereDate('created_at', date('Y-m-d'));
User::whereDay('created_at', date('d'));
User::whereMonth('created_at', date('m'));
User::whereYear('created_at', date('Y'));

۸. مرتب‌کردن بر اساس رابطه

یک ترفند کمی پیچیده‌تر. اگر  مباحث تالار گفتگو دارید اما بخواهید آن‌ها را با آخرین پست مرتب کنید چه؟ نیاز بسیار رایج در انجمن‌ها با آخرین موضوعات به روز شده در بالا، درست است؟

ابتدا یک رابطه جداگانه برای آخرین پست آن موضوع تعریف کنید:

public function latestPost()
{
    return $this->hasOne(\App\Post::class)->latest();
}

و سپس، در controller مان، می‌توانیم این جادو را انجام دهیم:

$users = Topic::with('latestPost')->get()->sortByDesc('latestPost.created_at');

۹. Eloquent::when()، دیگر if-else بس است

بسیاری از ما کوئری‌های شرطی را با if-else می‌نویسیم، چیزی شبیه به این:

if (request('filter_by') == 'likes') {
    $query->where('likes', '>', request('likes_amount', 0));
}
if (request('filter_by') == 'date') {
    $query->orderBy('created_at', request('ordering_rule', 'desc'));
}

اما روش بهتری وجود دارد- استفاده از when() :

$query = Author::query();
$query->when(request('filter_by') == 'likes', function ($q) {
    return $q->where('likes', '>', request('likes_amount', 0));
});
$query->when(request('filter_by') == 'date', function ($q) {
    return $q->orderBy('created_at', request('ordering_rule', 'desc'));
});

ممکن است کوتاه‌تر یا ظریف‌تر به نظر نرسد، اما قدرتمندترین راه pass کردن پارامترهاست:

$query = User::query();
$query->when(request('role', false), function ($q, $role) { 
    return $q->where('role_id', $role);
});
$authors = $query->get();

۱۰. BelongsTo پیش‌فرض مدل‌ها

بیایید بگوییم که شما پستی دارید که متعلق به نویسنده است و سپس کد Blade آن :

{{ $post->author->name }}

اما اگر نویسنده حذف شود، یا به دلایلی تنظیم نشده باشد چه خواهد شد؟ شما خطایی دریافت خواهید کرد، چیزی مثل “property of non-object”.

البته، شما می‌توانید از این جلوگیری کنید، مثل این:

{{ $post->author->name ?? '' }}

اما شما می‌توانید آن را در سطح Eloquent relationship انجام دهید:

public function author()
{
    return $this->belongsTo('App\Author')->withDefault();
}

در این مثال، در صورتی که نویسنده‌ای به پست اختصاص نداشته باشد، ارتباط author() یک App\Author model خالی برمی‌گرداند.

به‌علاوه، می‌توانیم مقدار property پیش‌فرض را به آن مدل پیش‌فرض اختصاص دهیم.

public function author()
{
    return $this->belongsTo('App\Author')->withDefault([
        'name' => 'Guest Author'
    ]);
}

۱۱. مرتب سازی به وسیله Mutator

تصور کنید چنین چیزی دارید:

function getFullNameAttribute()
{
  return $this->attributes['first_name'] . ' ' . $this->attributes['last_name'];
}

اکنون می‌خواهید با full_name آن را مرتب کنید؟ این کار نخواهد کرد:

$clients = Client::orderBy('full_name')->get(); // doesn't work

راه حل بسیار ساده است. ما باید نتایج را بعد از اینکه دریافت کردیم مرتب کنیم:

$clients = Client::get()->sortBy('full_name'); // works!

توجه کنید که نام فانکشن متفاوت است؛ order By نشده، sortBy شده است.

۱۲. مرتب سازی پیش‌‌فرض در global scope

اگر بخواهید User::all() همیشه با فیلد name مرتب باشد چه؟ می‌توانید یک global scope اختصاص دهید. بیایید به متد boot() که قبلاً اشاره کردیم، برگردیم.

protected static function boot()
{
    parent::boot();

    // Order by name ASC
    static::addGlobalScope('order', function (Builder $builder) {
        $builder->orderBy('name', 'asc');
    });
}

اطلاعات بیشتر درمورد Query Scope ها را اینجا بخوانید.

۱۳. متد Raw query (کوئری خام)

بعضی اوقات ما نیاز به اضافه کردن نمایش داده‌های خام به Eloquent statement های خود داریم. خوشبختانه، فانکشن‌هایی برای آن وجود دارد.

// whereRaw
$orders = DB::table('orders')
    ->whereRaw('price > IF(state = "TX", ?, 100)', [200])
    ->get();

// havingRaw
Product::groupBy('category_id')->havingRaw('COUNT(*) > 1')->get();

// orderByRaw
User::where('created_at', '>', '2016-01-01')
  ->orderByRaw('(updated_at - created_at) desc')
  ->get();

۱۴. کپی: یک کپی از یک سطر درست کنید

این یکی کوتاه است. بدون توضیحات عمیق، بهترین روش برای تهیه یک کپی از ورودی پایگاه داده:

$task = Tasks::find(1);
$newTask = $task->replicate();
$newTask->save();

۱۵. متد Chunk() برای جدول‌های بزرگ

دقیقاً مربوط به Eloquent نیست، بیشتر درباره collection است، اما هنوز هم قدرتمند است. برای پردازش‌های بزرگ‌تر، می‌توانید آن‌ها را به قطعه‌های دیگر chunk یا(تکه) کنید.

به جای انجام این کار:

$users = User::all();
foreach ($users as $user) {
    // ...

می‌توانید این کار را انجام دهید:

User::chunk(100, function ($users) {
    foreach ($users as $user) {
        // ...
    }
});

۱۶. هنگام ساخت یک مدل جدید چیزهای اضافی ایجاد کنید

همه ما این دستور artisan را می‌شناسیم:

php artisan make:model Company

اما آیا می‌دانید سه flag مفید برای تولید فایل‌های مرتبط با مدل وجود دارد؟

php artisan make:model Company -mcr
  • -m یک فایل migration ایجاد می‌کند.
  • -c یک controller ایجاد می‌کند.
  • -r نشان می‌دهد که controller باید resourceful باشد.

۱۷. صرف نظر از updated_at هنگام ذخیره کردن

آیا می‌دانید که متد →save() می‌تواند پارامترها را بپذیرد؟ در نتیجه، می‌توانیم بگوییم که updated_at پیش‌‌فرض فانکشنال را برای پر کردن با timestamp فعلی نادیده بگیرند. این را ببینید:

$product = Product::find($id);
$product->updated_at = '2019-01-01 10:00:00';
$product->save(['timestamps' => false]);

در اینجا ما updated_at پیش‌فرض را با نسخه از پیش تعریف شدهoverride می‌کنیم.

۱۸. نتیجه‌ی update() چیست؟

آیا تاکنون فکر کرده‌اید که این کد در‌واقع چه چیزی را باز می‌گرداند؟

$result = $products->whereNull('category_id')->update(['category_id' => 2]);

منظور من این است که بروز‌رسانی در پایگاه‌داده اجرا می‌شود، اما $result آن شامل چه چیزی است؟

پاسخ روی سطرها اثر می‌گذارد. بنابراین اگر لازم دارید چک کنید چند سطر تحت ‌تاثیر قرار گرفته است، دیگر نیاز به فراخوانی چیز دیگری ندارید- متد update() این شماره را برای شما برمی‌گرداند.

۱۹. براکت‌ها را به یک Eloquent query تبدیل کنید

اگر and-or را به صورت ترکیبی داشته باشید، مثل این:

... WHERE (gender = 'Male' and age >= 18) or (gender = 'Female' and age >= 65)

چگونه آن را به Eloquent ترجمه کنیم؟ این روش اشتباه است:

$q->where('gender', 'Male');
$q->orWhere('age', '>=', 18);
$q->where('gender', 'Female');
$q->orWhere('age', '>=', 65);

این order نادرست است. راه درست‌ کمی پیچیده‌تر است، با استفاده از closure function ها به عنوان ساب‌کوئری‌ها:

$q->where(function ($query) {
    $query->where('gender', 'Male')
        ->where('age', '>=', 18);
})->orWhere(function($query) {
    $query->where('gender', 'Female')
        ->where('age', '>=', 65); 
})

۲۰. orWhere با چند پارامتر

در آخر، می‌توانید یک آرایه از پارمترها را به orWhere() منتقل(pass) کنید.

روش معمول:

$q->where('a', 1);
$q->orWhere('b', 2);
$q->orWhere('c', 3);

می‌توانید چیز شبیه به این را انجام دهید:

$q->where('a', 1);
$q->orWhere(['b' => 2, 'c' => 3]);

و در آخر امیدوارم از این مقاله لذت برده باشید و برای شما مفید بوده باشد.

منبع

گردآوری و تالیف فاطمه شیرزادفر
آفلاین
user-avatar

تجربه کلمه‌ای هست که همه برای توصیف اشتباهاتشون ازش استفاده میکنن، و من همیشه دنبال اشتباهات جدیدم! برنامه‌نویس هستم و لینوکس‌ دوست

دیدگاه‌ها و پرسش‌ها

برای ارسال نظر لازم است ابتدا وارد سایت شوید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید