تخفیف ویژه

آموزش laravel eloquent با مثال

دسته بندی: لاراول
زمان مطالعه: 32 دقیقه
۱۰ تیر ۱۳۹۹

همیشه برای برنامه نویسان ایجاد ارتباط بین یک برنامه و Database آن کمی دردسر ساز بوده است. فریم ورک لاراول نسبت به سایر رقبای خود، همان‌طور که در مقاله "فریم ورک لاراول چیست" گفتیم، یک ویژگی منحصر به فرد یعنی Eloquent ORM دارد. با بهره‌گیری از قابلیت laravel eloquent ، ایجاد ارتباط بین هر برنامه‌ای با Database مربوط به آن، تنها چند ثانیه زمان می‌برد.

eloquent چیست

فهرست محتوای این مقاله

نحوه کارکرد laravel eloquent

هر کدام از جدول‌های Database با Model هایی نظیر آن در برنامه ارتباط برقرار می‌کند. به‌طور مثال اگر ما در Database یک جدول به‌نام Flights بسازیم، برای ایجاد ارتباط بین برنامه‌‌‌ی خود و این Database، کافی است یک Model به ‌نام Flight بسازیم. در این‌ صورت به ‌طور خودکار برنامه بین جدول Flights از Database و Model نظیر آن، یعنی Flight یک ارتباط ایجاد می‌کند. با استفاده از این Model‌ها می‌توانیم تمام Query هایی که می‌خواهیم را بر روی جدول‌هایمان اعمال کنیم.

ساخت و سفارشی سازی Model

همان‌طور که گفتیم برای ایجاد ارتباط با جدول‌های Database لازم است برای هر‌کدام از از جدول‌های موجود در Database یک Model بسازیم.

ساخت Model

Model‌ها به‌صورت پیش‌فرض در پوشه app قرار دارند و اگر Model جدیدی توسط دستور artisan ایجاد کنیم، به‌طور خودکار در این پوشه قرار می‌گیرد. البته شما می‌توانید که این Model‌ها را در پوشه‌ دیگری نیز قرار دهید.

ساده‌ترین راه ایجاد یک Model، استفاده از دستور make:model در Artisan می‌باشد.

php artisan make:model Flight

دستور بالا یک Model به‌نام Flight در همان پوشه‌ی پیش‌فرض، یعنی app ایجاد می‌کند.

اگر می‌خواهید هم‌زمان با ساخت Model، یک Migration به Database آن ایجاد کنید، به‌ جای دستور بالا از یکی از دستورات زیر استفاده کنید.

php artisan make:model Flight --migration

php artisan make:model Flight -m

سفارشی سازی Model

پس از ساخت Model، امکان دارد که نتوانید به‌صورت صحیح با Database ارتباط برقرار کنید و به یک سری از خطاها برخورد کنید. در لاراول و Model‌های آن به ‌طور مثال id ،Primary Key در نظر گرفته می‌شود، حال اگر به‌جای id در Flight Number ،Database داشته باشیم، لاراول به‌صورت خودکار نمی‌تواند آن را شناسایی کند. بنابراین لازم است پس از ساخت Model برخی از تغییرات را در آن اعمال کنیم.

به‌ طور مثال در این‌ جا Flight Model را مشاهده می‌کنید.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    //
}

نام جدول

همان طور که مشاهده می‌کنید، ما به Model نگفته‌ایم که به کدام جدول از Database دسترسی داشته باشد یا با داده‌های آن ارتباط برقرار کند. در این‌ جا نیز مانند همیشه با یکی از مزایای بزرگ لاراول روبرو می‌شویم. در لاراول تمام Model‌ها از یک قرارداد خاصی پیروی می‌کنند که اگر این قرارداد را به صورت کاملا ساده بخواهیم بررسی کنیم، به این شکل است که تمام Model‌ها به صورت پیش‌فرض یک ارتباط با جدولی از Database ایجاد می‌کنند، که نام آن جدول مانند نام Class در Model ما یا به زبان ساده‌تر نام Model ما باشد. به‌طور مثال در دستور بالا نام Class ما در Flight ,Model است، پس به‌صورت پیش‌فرض Model ما با جدول Flights در Database یک ارتباط برقرار می‌کند.

حال اگر به جای جدول Flights یک جدول به‌نام My_Flights داشته باشیم، دیگر این ارتباط به‌صورت خودکار برقرار نمی‌شود و لازم است تا Model را سفارشی‌سازی کنیم. برای‌ این‌ کار می‌توانیم مانند دستور زیر عمل کنیم.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = 'My_Fflights';
}

Primary Keys

همان‌طور که گفتیم، laravel eloquent به صورت پیش‌فرص Primary Key را id در نظر می‌گیرد. اگر بخواهیم Primary Key را عوض کنیم، از دستور زیر استفاده می‌کنیم.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The primary key associated with the table.
     *
     * @var string
     */
    protected $primaryKey = 'flight_id';
}

هم‌چنین به‌صورت پیش فرض نوع Primary Key یک Integer با روند صعودی در نظر گرفته می‌شود. اگر بخواهید که نوع آن Integer نباشد، می‌توانید از دستور زیر استفاده کنید و به ‌طور مثال نوع آن را به String تغییر دهید.

<?php

class Flight extends Model
{
    /**
     * The "type" of the auto-incrementing ID.
     *
     * @var string
     */
    protected $keyType = 'string';
}

اگر بخواهید که Primary Key روند صعودی نداشته باشد، کافی است incrementing$ را به صورت زیر false کنید.

<?php

class Flight extends Model
{
    /**
     * Indicates if the IDs are auto-incrementing.
     *
     * @var bool
     */
    public $incrementing = false;
}

Timestamps

به‌صورت پیش‌فرض laravel eloquent انتظار دارد که دو ستون با نام‌های created_at و updated_at درون جدول Database وجود داشته باشد که به صورت خودکار هنگام ساخت یک داده، ستون created_at و هنگام بروزرسانی و تغییر آن، ستون updated_at مقدار زمانی را می‌گیرد و ذخیره می‌کند.

اگر بخواهیم این ستون‌ها در جدول ما وجود نداشته باشد، باید timestamps$ را به‌صورت زیر false کنیم.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * Indicates if the model should be timestamped.
     *
     * @var bool
     */
    public $timestamps = false;
}

اگر می‌خواهید که این ستون‌ها، مقدار زمانی را با Format دلخواه شما ذخیره کنند، می‌توانید از دستور زیر استفاده کنید.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The storage format of the model's date columns.
     *
     * @var string
     */
    protected $dateFormat = 'U';
}

هم‌چنین اگر بخواهید نام این ستون‌ها را تغییر دهید، باید از دستور زیر استفاده کنید.

<?php

class Flight extends Model
{
    const CREATED_AT = 'creation_date';
    const UPDATED_AT = 'last_update';
}

ارتباط با Database

به‌طور پیش‌فرض تمام Model‌ها به Database یا پایگاه‌ داده‌ای که در تنظیمات برنامه خود وارد کرده‌ایم متصل می‌شود. حال اگر بخواهیم یکی از این Model‌ها به یک Database دیگر متصل شود، از دستور زیر استفاده می‌کنیم.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The connection name for the model.
     *
     * @var string
     */
    protected $connection = 'connection-name';
}

تعیین مقدار پیش‌فرض Attribute ها

اگر بخواهید برای Attribute ها، یک مقدار پیش‌فرض در نظر بگیریم، از دستور زیر استفاده می‌کنیم.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The model's default values for attributes.
     *
     * @var array
     */
    protected $attributes = [
        'delayed' => false,
    ];
}

در دستور بالا می‌توانیم مقدار پیش‌فرض هر تعداد دیگر Attribute را در آرایه attributes$ وارد کنیم.

دریافت داده‌های Database توسط Model

زمانی که یک Model را ساختیم و متناظر با Database آن تغییرات مورد نیازی که در بالا گفتیم را بر روی‌ آن اعمال کردیم. می‌توانیم داده‌های دل‌خواهمان را از آن دریافت کنیم. به‌طور مثال به دستور زیر توجه کنید:

<?php

$flights = App\Flight::all();

foreach ($flights as $flight) {
    echo $flight->name;
}

در این دستور ابتدا از مدل Fight تمام داده‌ها را گرفتیم. سپس توسط حلقه foreach، اسم تمام آن‌ها را echo کردیم.

همان‌طور که در بالا دیدید، اگر از ()all استفاده کنیم، تمام داده‌ها در اختیار ما قرار می‌گیرند. اما اگر تمام آن‌ها را نخواهیم و تنها به تعدادی از آن‌ها با یک شرط خاص نیاز داشته باشیم، می‌توانیم از where استفاده کنیم.

$flights = App\Flight::where('active', 1)
      ->orderBy('name', 'desc')
      ->take(10)
      ->get();

Refresh کردن Model‌

با استفاده از دستورات fresh یا refresh می‌توانیم Model‌ها را دوباره مرتب کنیم و داده‌های Database مربوط به آن را دوباره دریافت کنیم. به‌طور دقیق‌تر با استفاده از ()fresh، داده‌ها از Model پس از یک refresh سریع، دریافت می‌شود.

$flight = App\Flight::where('number', 'FR 900')->first();
$freshFlight = $flight->fresh();

هم‌چنین همان‌طور که در دستور زیر آمده است، پس از تغییر یک داده Database می‌توانیم مانند همان دستور بالا، از دستور ()refresh نیز استفاده کنیم.

$flight = App\Flight::where('number', 'FR 900')->first();

$flight->number = 'FR 456';

$flight->refresh();

$flight->number; // "FR 900"

Collection ها

هنگام دریافت داده‌ها می‌توانیم از Collection‌های مختلف موجود در لاراول نیز استفاده کنیم. به‌طور مثال فرض کنید می‌خواهید تمام داده در یک آرایه دریافت شوند، که این آرایه دارای چند Index و هر کدام از این Index ها، دارای ۲۰۰ مقدار یا Value باشد، یا به عبارتی دیگر، به‌طور مثال اگر ۱۱۵۰ داده بخواهد دریافت شود و بخواهیم آن‌ها را در بخش‌های ۲۰۰ تایی قرار دهیم، کافی است از Chunk Collection استفاده کنیم.

Flight::chunk(200, function ($flights) {
    foreach ($flights as $flight) {
        //
    }
});

اگر داده‌های ما ۱۰۰۰ تا باشد، آن‌ داده‌ها به‌صورت دقیق در ۵ دسته یا Index قرار می‌گیرند. حال اگر مانند مثال بالا ۱۱۵۰ داده داشته باشیم، ۵ Index که هر کدام ۲۰۰ مقدار یا Value دارند و ۱ Index که ۱۵۰ مقدار دارد، دریافت می‌شود.

در دستور بالا ما داده‌ها را در بخش‌های ۲۰۰‌ تایی دریافت می‌کنیم و بر روی هر کدام از آن‌ها توسط حلقه‌ foreach می‌توانیم یک عمل انجام دهیم.

Query‌های پیشرفته

هنگام استفاده از laravel eloquent می‌توانیم Query‌های پیشرفته‌ای به سمت Database ارسال کنیم. به‌طور مثال فرض کنید که ما یک جدول برای مقصد پروازهایمان به‌نام destinations و یک جدول برای خود پروازهایمان به‌نام flights داریم. هم‌چنین فرض کنید در جدول flights یک ستون به‌نام arrived_at داریم که زمانی را نشان می‌دهد که پرواز به مقصد رسید. حال می‌توانیم تمام دستوراتی که در ادامه به آن‌ها می‌پردازیم را بر روی این داده‌ها اعمال کنیم.

انتخاب داده‌ ها

فرض کنید می‌خواهید تمام مقصد‌ها را از جدول destinations دریافت کنید و آن پرواز‌هایی که به تازگی به مقصد رسیده‌اند را نشان دهید. برای این‌کار می‌توانیم از ()select و ()addselect استفاده کنیم.

use App\Destination;
use App\Flight;

return Destination::addSelect(['last_flight' => Flight::select('name')
    ->whereColumn('destination_id', 'destinations.id')
    ->orderBy('arrived_at', 'desc')
])->get();

مرتب ‌سازی داده‌ها

فرض کنید می‌خواهید تمام مقصد‌ها را به‌ ترتیب تاریخ رسیدنشان، به‌صورت صعودی مرتب کنیم. برای این کار باید از جدول flight که پرواز‌ها‌ را نشان می‌دهد، تمام پرواز‌ها را به‌ ترتیب زمان رسیدنشان دریافت کنیم و آن‌ها را به جدول destination ارسال کنیم، سپس تمام داده‌هایی که در جدول destination قرار می‌گیرد را به‌صورت صعودی، توسط orderByDesc می‌توانیم نشان دهیم.

return Destination::orderByDesc(
    Flight::select('arrived_at')
        ->whereColumn('destination_id', 'destinations.id')
        ->orderBy('arrived_at', 'desc')
        ->limit(1)
)->get();

دریافت یک داده از Database

گاهی اوقات به‌جای دریافت همه یا چند داده، تنها یکی از آن‌ها را می‌خواهیم دریافت کنیم. برای دریافت یک داده می‌توانیم از first ،firstWhere و find استفاده کنیم. به مثال‌های زیر توجه کنید:

// Retrieve a model by its primary key...
$flight = App\Flight::find(1);

دستور بالا داده‌ای را برمی‌گرداند که id یا Primary Key آن برابر با 1 است.

$flights = App\Flight::find([1, 2, 3]);

این دستور همانند دستور اول است با این تفاوت که علاوه بر دریافت داده‌ای که id یا Primary Key آن برابر با 1 است، داده‌هایی که Primary Key آن‌ها برابر با 2 و 3 هست را نیز بر‌می‌گرداند.

// Retrieve the first model matching the query constraints...
$flight = App\Flight::where('active', 1)->first();

دستور بالا تمام داده‌هایی که ستون active آن برابر با 1 باشد را دریافت می‌کند و توسط ()first اولین داده را نشان می‌دهد.

// Shorthand for retrieving the first model matching the query constraints...
$flight = App\Flight::firstWhere('active', 1);

این دستور، کوتاه شده‌‌ی دستور سوم یعنی استفاده از ()first است.

حال امکان دارد داده‌ای با مشخصاتی که وارد کردیم وجود نداشته باشد. در این صورت اگر بخواهیم هنگامی که این اتفاق افتاد، یک داده‌ی‌ دیگر، یک متن یا هر چیز دیگری نمایش داده شود، می‌توانیم از firstOr استفاده ‌کنیم.

$model = App\Flight::where('legs', '>', 100)->firstOr(function () {
        // ...
});

درون تابعی که در firstOr قرار دارد، می‌توانیم هر Query قرار دهیم تا در صورتی که در جدول Flights هیچ داده‌ای با legs بزرگتر از ۱۰۰ وجود نداشت، آن Query اجرا شود.

هم‌چنین firstOr می‌تواند یک آرایه دریافت کند تا بر روی تمام Index‌های آن آرایه، یک Query اجرا کند.

$model = App\Flight::where('legs', '>', 100)
            ->firstOr(['id', 'legs'], function () {
                // ...
            });

نمایش Exception

اگر بخواهیم در صورت پیدا نشدن Model، یک exception یا یک Error به کاربر نشان دهیم، می‌توانیم از findOrFail و firstOrFail استفاده کنیم.

$model = App\Flight::findOrFail(1);

$model = App\Flight::where('legs', '>', 100)->firstOrFail();

اگر در دستورات بالا Model پیدا نشود، برای کاربر یک Error 404 نمایش داده می‌شود.

Collection‌های عددی

هنگامی که داده‌ها را از Database، توسط Model دریافت می‌کنیم می‌توانیم به‌ جای دریافت به‌ طور مثال نام داده‌ها، تعداد آن ها، بزرگترین داده یا جمع داده‌ها را دریافت کنیم. برای همین می‌تواینم از count ,sum و max استفاده کنیم.

$count = App\Flight::where('active', 1)->count();

$max = App\Flight::where('active', 1)->max('price');

از count برای دریافت تعداد داده‌ها، max برای دریافت بزرگترین داده و sum برای دریافت جمع داده‌ها استفاده می‌کنیم.

اضافه کردن داده‌ها به Database توسط Model

برای ایجاد یک داده جدید در Database، می‌توانیم آن داده را در Model ایجاد کنیم و سپس توسط ()save آن را در Database ذخیره نماییم.

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Flight;
use Illuminate\Http\Request;

class FlightController extends Controller
{
    /**
     * Create a new flight instance.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // Validate the request...

        $flight = new Flight;

        $flight->name = $request->name;

        $flight->save();
    }
}

در دستور بالا ابتدا یک متغیر به نام flight ایجاد می‌کنیم. سپس name را از طریق Http Request کاربر دریافت می‌کنیم و آن را برابر با نام متغیر flight قرار می‌دهیم. حال توسط ()save این درخواست به Database ارسال شده و در ستون name پایگاه داده (Database) ذخیره می‌شود.

در صورتی که timestamps$ را false نکرده باشیم، پس از ()save یک داده در Database، به‌طور خودکار ستون‌های created_at و updated_at آن، مقداری برابر با زمان Server می‌گیرد.

هم‌چنین برای ایجاد یک داده می‌توانیم از create که همانند save عمل می‌کند نیز استفاده کنیم.

$flight = App\Flight::create(['name' => 'Flight 10']);

بروزرسانی داده‌ها در Database توسط Model

از ()save علاوه‌بر ایجاد و ذخیره کردن یک داده، می‌توانیم برای بروزرسانی داده‌هایی که از قبل در Database وجود داشتند نیز استفاده کنیم. برای بروزرسانی یک داده باید ابتدا آن را دریافت کنیم، سپس تغییرات دلخواه را بر روی آن داده اعمال کنیم و پس از آن توسط ()save آن داده را در Database ذخیره کنیم.

$flight = App\Flight::find(1);

$flight->name = 'New Flight Name';

$flight->save();

در صورتی که timestamps$ را false نکرده باشیم، پس از ()save یک داده‌ای که از قبل در Database وجود داشته است، به‌طور خودکار ستون‌ updated_at آن، مقداری برابر با زمان Server می‌گیرد.

هم‌چنین برای بروزرسانی چند داده با مشخصاتی خاص، می‌توانیم آن‌ها را با یک Query که تنها داده‌هایی که آن مشخصات خاص را دارند برمی‌گرداند، دریافت کنیم و پس از آن از update استفاده کنیم.

App\Flight::where('active', 1)
          ->where('destination', 'San Diego')
          ->update(['delayed' => 1]);

در دستور بالا، تمام پرواز‌هایی که مقدار active آن برابر با 1 و مقدار destination یا مقصد آن برابر با San Diego است شناسایی شده و توسط update، مقدار delayed آن‌ها برابر با 1 می‌شود.

بررسی Attribute ها

برای بررسی Attribute‌ها می‌توانیم از isDirty ,isClean و wasChanged استفاده کنیم.

  • isDirty : از این method زمانی استفاده می‌کنیم که بخواهیم بدانیم آیا هیچ کدام از Attribute‌های یک Model از زمان ایجاد آن یا بروزرسانی کامل آن یعنی save کردن، تغییری داشته است یا خیر.
  • isClean : دقیقا برعکس isDirty عمل می‌کند.
$user = User::create([
    'first_name' => 'Taylor',
    'last_name' => 'Otwell',
    'title' => 'Developer',
]);

$user->title = 'Painter';

$user->isDirty(); // true
$user->isDirty('title'); // true
$user->isDirty('first_name'); // false

$user->isClean(); // false
$user->isClean('title'); // false
$user->isClean('first_name'); // true

$user->save();

$user->isDirty(); // false
$user->isClean(); // true
  • wasChanged : معمولا از این method زمانی استفاده می‌کنیم که می‌خواهیم بدانیم آیا یک Attribute، پس از ارسال از طریق HTTP Request توسط کاربر، تغییر داده شده است یا خیر.
$user = User::create([
    'first_name' => 'Taylor',
    'last_name' => 'Otwell',
    'title' => 'Developer',
]);

$user->title = 'Painter';
$user->save();

$user->wasChanged(); // true
$user->wasChanged('title'); // true
$user->wasChanged('first_name'); // false
  • getOriginal : این method تمام اطلاعات و Attribute‌های اولیه یک model را، حتی اگر مقدار آن‌ها را هزاران بار عوض کرده باشیم، بر‌می‌گرداند.
$user = User::find(1);

$user->name; // John
$user->email; // john@example.com

$user->name = "Jack";
$user->name; // Jack

$user->getOriginal('name'); // John
$user->getOriginal(); // Array of original attributes...

Mass Assignment

Mass Assignment یکی از مهم‌ترین بخش‌های این آموزش است که رعایت نکردن آن باعث به خطر افتادن جدی امنیت سایت می‌شود.

فرض کنید یک form با بخش‌های نام و نام خانوادگی، شماره همراه، آدرس و رمز عبور داریم که کاربر باید آن را تکمیل کنید. حال برای همین form در Database علاوه ‌بر بخش‌های بالا چند بخش دیگر مانند نقش کاربری که شامل Admin یا User، دسترسی‌ها و بخش‌های امنیتی مهم دیگری می‌شود داریم. در این صورت هر کاربری که دانش برنامه‌نویسی داشته باشد، می‌تواند هنگام تکمیل form با اعمال چند تغییر کوچک نقش کاربری خود را Admin وارد کند و به تمام اطلاعات سایت دسترسی پیدا کند.

برای جلوگیری از این Bug‌های امنیتی، باید در همان ابتدا تعیین کنیم که چه field هایی می‌تواند مقدار بگیرد. بدین منظور می‌توانیم از guarded یا fillable استفاده کنیم.

  • fillable : در آرایه fillable تمام Attribute هایی که می‌خواهیم بتواند از طرف کاربر مقدار بگیرد را وارد می‌کنیم.
  • guarded : در آرایه guarded تمام Attribute هایی که نمی‌خواهیم از طرف کاربر مقدار بگیرد را وارد می‌کنیم.

هنگامی که یک Attribute را در fillable وارد کنیم، به‌طور خودکار تمام Attribute‌های دیگر guarded می‌شوند. هم‌چنین اگر یک Attribute را در guarded وارد کنیم، به‌طور خودکار تمام Attribute‌های دیگر fillable می‌شوند.

در مثال بالا که تنها می‌خواهیم ۲ Attribute غیر‌قابل پر کردن باشند، بهتر است به‌جای وارد کردن Attribute هایی که قابل پر کردن می‌باشند در fillable، آن Attribute هایی که غیر‌قابل پر کردن هستند را در guarded وارد کنیم.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name'];
}

در دستور بالا Attribute که می‌تواند مقدار بگیرد را در fillable وارد کردیم، پس تمام Attributes‌ها به ‌جز name که fillable است، guarded می‌باشند.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The attributes that aren't mass assignable.
     *
     * @var array
     */
    protected $guarded = ['price'];
}

در دستور بالا Attribute که نمی‌تواند مقدار بگیرد را در guarded وارد کردیم، پس تمام Attributes‌ها به‌ جز price که guarded است، fillable می‌باشند.

اضافه کردن داده‌ها به Database همراه با Mass Assignment

برای ایجاد مستقیم یک داده در Database همراه با قابلیت Mass Assignment، می‌توانیم از firstOrCreate و firstOrNew استفاده کنیم.

  • firstOrCreate: این method ابتدا تلاش می‌کند تا با توجه به مقداری که به آن دادیم یک record از Database و از طریق Model پیدا کند. در صورتی که این record در Database پیدا نشود، به‌ صورت خودکار یک داده با همان اطلاعاتی که وارد کردیم، در Database ایجاد می‌شود.
  • firstOrNew: این method همانند firstOrCreate عمل می‌کند، با فرق اینکه در صورت پیدا نشدن record، به جای ایجاد آن record، یک record از نمونه یا instance آن Model را برمی‌گرداند.
// Retrieve flight by name, or create it if it doesn't exist...
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);

// Retrieve flight by name, or create it with the name, delayed, and arrival_time attributes...
$flight = App\Flight::firstOrCreate(
    ['name' => 'Flight 10'],
    ['delayed' => 1, 'arrival_time' => '11:30']
);

// Retrieve by name, or instantiate...
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);

// Retrieve by name, or instantiate with the name, delayed, and arrival_time attributes...
$flight = App\Flight::firstOrNew(
    ['name' => 'Flight 10'],
    ['delayed' => 1, 'arrival_time' => '11:30']
);
  • updateOrCreate : این method زمانی برای بروزرسانی یک داده استفاده می‌شود که بخواهیم در صورت وجود نداشتن آن داده برای بروزرسانی، به‌صورت خودکار همان داده در Database ایجاد شود.
// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.
$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99, 'discounted' => 1]
);

حذف Model

برای حذف یک Model از Database می‌توانیم یک instance از آن Model را توسط ()delete حذف کنیم.

$flight = App\Flight::find(1);

$flight->delete();

حذف تنها یک یا چند داده‌ی Model

اگر بخواهیم به جای حذف کلی Model، تنها چند داده‌ی آن را حذف نماییم، می‌توانیم از ()destroy استفاده کنیم. برای همین باید Primary Key تمام داده‌هایی که می‌خواهیم حذف کنیم را به ‌صورت یک آرایه در ()destroy وارد کنیم.

App\Flight::destroy(1);

App\Flight::destroy(1, 2, 3);

App\Flight::destroy([1, 2, 3]);

App\Flight::destroy(collect([1, 2, 3]));

حذف یک یا چند داده‌ی خاص از Model توسط Query

برای حذف یک یا چند داده‌ی خاص از Model می‌توانیم از Query، به شکل زیر استفاده کنیم.

$deletedRows = App\Flight::where('active', 0)->delete();

در دستور بالا، تمام پرواز‌هایی که غیرفعال هستند را از Database حذف می‌کنیم.

Soft Deleting

برای حذف داده‌های مهم از Database، قطعا حذف کلی و بدون بازگشت آن‌ها راهی پرخطر است. برای حذف این نوع داده‌ها از Soft Deleting استفاده می‌کنیم.

eloquent laravel

در این‌صورت پس حذف نرم یا همان Soft Delete کردن یک داده، یک ستون جدید به‌نام deleted_at به Database اضافه می‌شود، که این ستون مقداری برابر با زمان Soft Delete کردن داده را می‌گیرد. برای فعال‌سازی این قابلیت، Trait آن یعنی SoftDeletes را در use ,Model می‌کنیم.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Flight extends Model
{
    use SoftDeletes;
}

پس از use کردن SoftDeletes باید ستون deleted_at را به جدول Database اضافه کنیم. این ستون به‌ صورت پیش‌فرض از نوع DateTime و با پشتیبانی از Carbon است.

Schema::table('flights', function (Blueprint $table) {
    $table->softDeletes();
});

حال وقتی از delete برای حذف داده استفاده کنیم، به ‌صورت خودکار ستون deleted_add مقداری برابر با زمان Soft Delete کردن داده را می‌گیرد و پس از آن وقتی یک Query بر روی آن Model بزنیم، می‌بینیم که آن Model مانند حذف کردن‌های معمولی، دیگر وجود ندارد با این تفاوت که با استفاده از trashed می‌توانیم همان Model که Soft Delete شده بود را بررسی کنیم.

if ($flight->trashed()) {
    //
}

Query زدن برروی Model‌های Soft Delete شده

همان‌طور که در بالا گفتیم، Model‌های Soft Delete شده، به‌طور خودکار از Database به‌صورت غیر دائم حذف می‌شوند و هنگام زدن Query‌های معمولی، هیچ نتیجه‌ای را به ما برنمی‌گرداند. حال اگر بخواهیم از این Model در جایی استفاده کنیم، می‌توانیم از withTrashed استفاده کنیم.

$flights = App\Flight::withTrashed()
                ->where('account_id', 1)
                ->get();

هم‌چنین از withTrashed می‌توانیم در Query‌های ارتباطی یا relationship Query‌ها نیز استفاده کنیم.

$flight->history()->withTrashed()->get();

Restore کردن Model‌های Soft Delete شده

برای این‌که Model هایی که Soft Delete کردیم، دوباره قابل دسترسی باشد می‌توانیم از ()restore استفاده کنیم.

$flight->restore();

هم‌چنین برای restore کردن چند داده‌ی خاص از Model، می‌توانیم به شکل زیر عمل کنیم.

App\Flight::withTrashed()
        ->where('airline_id', 1)
        ->restore();

حذف کردن دائم Model‌های Soft Delete شده

اگر بخواهیم یک داده Soft Delete شده را به‌صورت دائم از Database حذف کنیم، می‌توانیم از forceDelete استفاده کنیم.

// Force deleting a single model instance...
$flight->forceDelete();

// Force deleting all related models...
$flight->history()->forceDelete();

کپی برداری از Model ها

اگر بخواهیم تعدادی Model که بعضی از Attribute‌های آن یکسان است، ایجاد کنیم، می‌توانیم از یکی از آن‌ها کپی بگیریم و بقیه Model‌ها را با توجه به Model اصلی که کپی شده است ایجاد کنیم. برای این‌کار از replicate استفاده می‌کنیم.

$shipping = App\Address::create([
    'type' => 'shipping',
    'line_1' => '123 Example Street',
    'city' => 'Victorville',
    'state' => 'CA',
    'postcode' => '90001',
]);

$billing = $shipping->replicate()->fill([
    'type' => 'billing'
]);

$billing->save();

Scope Queries

برای Query‌ها می‌توانیم یک دامنه، حریم یا همان Scope تعریف کنیم. به‌طور مثال Scope‌های جهانی یا Global، محلی یا Local و پویا یا Dynamic، که در ادامه به شرح کامل هر کدام از آن‌ها می‌پردازیم.

scope query - eloquent laravel

Global Scopes

هنگامی که از Global Scope‌ها استفاده می‌کنیم، می‌توانیم بر روی Query هایی که به سمت Model، برای دریافت داده‌ها ارسال می‌کنیم، یک محدودیت خاصی اعمال کنیم. عملکرد Soft Deleting یا همان حذف نرم در لاراول، زمانی که از Global Scope‌ها استفاده می‌کنیم، تنها Model‌های پاک نشده را به ما برمی‌گرداند. هم‌چنین می‌توانیم یک Global Scope خاص و سفارشی را، که در ادامه به آموزش ایجاد این Scope می‌پردازیم، برای خود ایجاد کنیم، که ایجاد این Scope باعث می‌شود که به‌ راحتی محدودیت‌هایی که می‌خواهیم را بر روی همه‌ی Model ها، همزمان اعمال کنیم.

ایجاد Global Scope‌ها :

ایجاد یک Global Scope بسیار ساده است. برای این‌کار باید در ابتدا Illuminate\Database\Eloquent\Scope را use کنیم. سپس این interface، که ابتدای دستوراتمان use کردیم، از ما می‌خواهد که یک method به‌نام apply را مانند دستورات زیر پیاده‌سازی یا implement کنیم.

<?php

namespace App\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class AgeScope implements Scope
{
    /**
     * Apply the scope to a given Eloquent query builder.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        $builder->where('age', '>', 200);
    }
}

استفاده از Global Scope ها

برای اختصاص دادن یک Global Scope به یک Model، باید در تابع booted از آن Model، از addGlobalScope استفاده کنیم.

<?php

namespace App;

use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The "booted" method of the model.
     *
     * @return void
     */
    protected static function booted()
    {
        static::addGlobalScope(new AgeScope);
    }
}

در این صورت به ‌طور مثال، درخواست دریافت تمام کاربران یک سایت یا همان ()User::all برابر با کد SQL زیر می‌شود.

select * from `users` where `age` > 200

Global Scope‌های ناشناس :

laravel eloquent به شما این امکان را می‌دهد که یک Global Scope را توسط Closure‌ها ایجاد کنید. از این روش هنگام ایجاد Scope‌های ساده‌ای که از یک Class که کلاس‌های دیگر را warrant یا تضمین نمی‌کند تشکیل شده‌ است، می‌توانیم استفاده کنیم.

<?php

namespace App;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The "booted" method of the model.
     *
     * @return void
     */
    protected static function booted()
    {
        static::addGlobalScope('age', function (Builder $builder) {
            $builder->where('age', '>', 200);
        });
    }
}

حذف Global Scope ها

اگر بخواهید یک Global Scope را حذف کنید، باید از withoutGlobalScope استفاده کنید. این Method تنها یک Argument که نام Class هست را می‌پذیرد.

User::withoutGlobalScope(AgeScope::class)->get();

اگر Global Scope را توسط Closure ایجاد کردید، برای حذف آن باید به شکل زیر عمل کنید.

User::withoutGlobalScope('age')->get();

هم‌چنین اگر می‌خواهید به‌ جای حذف تنها یک Global Scope، همه یا چند تا از آن‌ها را حذف کنید، می‌توانید از دستورات زیر استفاده کنید.

// Remove all of the global scopes...
User::withoutGlobalScopes()->get();

// Remove some of the global scopes...
User::withoutGlobalScopes([
    FirstScope::class, SecondScope::class
])->get();

Local Scopes

هنگامی که از Local Scope‌ها استفاده می‌کنیم، می‌توانیم بر روی Query هایی که به سمت Model، برای دریافت داده‌ها ارسال می‌کنیم، محدودیت‌های خاصی که امکان دارد در برنامه چندین بار استفاده کنیم را اعمال کنیم. به‌ طور مثال ممکن است بخواهید در قسمت‌‌های مختلفی از برنامه، کاربرانی که "Popular"هستند را ده‌ها بار دریافت کنید. برای این نوع عملیات و کار‌ها از Local Scope‌ها می‌توانیم استفاده کنیم و برای تعریف یک Local Scope باید در ابتدای Method‌ها از پیشوند scope استفاده کنیم.

Scope‌ها همیشه باید یک instance از query builder را return کنند.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Scope a query to only include popular users.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }

    /**
     * Scope a query to only include active users.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeActive($query)
    {
        return $query->where('active', 1);
    }
}

استفاده از Local Scope ها

هنگامی که Scope را در Model تعریف کردیم، برای استفاده از آن، هنگام Query زدن باید آن Method را فراخوانی کنیم.

$users = App\User::popular()->active()->orderBy('created_at')->get();

هم‌چنین برای ترکیب چند Local Scope از or به‌ شکل زیر در ابتدای Query استفاده می‌کنیم.

$users = App\User::popular()->orWhere(function (Builder $query) {
    $query->active();
})->get();

با این حال، از آن‌ جا که این روش می‌تواند کمی دشوار باشد، لاراول یک روش بهتر یعنی orWhere را ارائه می‌دهد که از طریق این Method بدون استفاده از Closure ها، می‌توانید این Scope‌ها را مانند زنجیر به هم متصل کنید.

$users = App\User::popular()->orWhere->active()->get();

Dynamic Scopes

گاهی اوقات ممکن است بخواهید یک Scope تعریف کنید که پارامترهای مختلف را بپذیرد. برای این کار کافی است پارامترهای مورد نظر خود را به Scope اضافه کنید. توجه کنید که پارامترهای یک Scope باید مانند دستورات زیر، پس از query$ تعریف شوند.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Scope a query to only include users of a given type.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @param  mixed  $type
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeOfType($query, $type)
    {
        return $query->where('type', $type);
    }
}

حال می‌توانید هنگام فراخوانی Scope، به شکل زیر به آن یک پارامتر اختصاص دهید.

$users = App\User::ofType('admin')->get();

مقایسه Model ها

گاهی اوقات ممکن است بخواهید تعیین کنید که آیا دو Model یکسان هستند یا خیر. متد is برای این استفاده می‌شود که شناسایی کنید آیا این دو Model دارای Primary Key، جدول‌ها و Database مشترکی هستند یا خیر.

if ($post->is($anotherPost)) {
    //
}

Events

Model‌های laravel eloquent شامل Event‌های زیادی می‌شوند که این Event‌ها به شما این امکان را می‌دهند تا در Model‌ها به جست‌و ‌جو بپردازید. بعضی از این موارد شامل retrieved ,creating ,created ,updating ,saving ,saved ,deleting ,deleted ,restoring ,restored می‌شوند. علاوه بر این Event‌ها به شما این امکان را می‌دهد که هر بار که یک model class در Database ذخیره یا بروزرسانی شود، به راحتی یک کد دل‌خواه را اجرا کنید. هم‌چنین تمام Event‌ها instance یک Model را از طریق Constructor آن دریافت می‌کنند.

  • Event : retrieved یا رویداد retrieved زمانی اجرا می‌شود که Model از Database بازیابی یا همان retrieved شود.
  • creating / created:  زمانی که یک Model جدید برای اولین بار Save می‌شود، Event‌های creating و created اجرا می‌شوند.
  • updating / updated : اگر یک Model از قبل در Database وجود داشته باشد و برای آن Model متد save فرا خوانده شود، Event‌های updating و updated اجرا می‌شوند. هم‌چنین برای هر دوی این Event ها، saving و saved نیز اجرا می‌شوند.

برای شروع، ابتدا یک Property به‌نام dispatchesEvents$ را در Model تعریف می‌کنیم.

<?php

namespace App;

use App\Events\UserDeleted;
use App\Events\UserSaved;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The event map for the model.
     *
     * @var array
     */
    protected $dispatchesEvents = [
        'saved' => UserSaved::class,
        'deleted' => UserDeleted::class,
    ];
}

حال مانند دستور بالا، Index‌های دل‌خواهی را در آرایه dispatchesEvents$ همراه با Value هایی برابر با Class‌های نظیر آن تعریف می‌کنیم. پس از آن شما می‌توانید از event listnere‌ها برای مدیریت Event‌های خود استفاده کنید.

استفاده از Closure ها

به جای استفاده از Event Class‌های سفارشی ،می توانید از Closure‌ها استفاده کنید. این Closure‌ها را باید در متد booted وارد کنیم.

<?php

namespace App;

use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The "booted" method of the model.
     *
     * @return void
     */
    protected static function booted()
    {
        static::created(function ($user) {
            //
        });
    }
}

ناظران (Observers)

زمانی در یک Model خاص در حال listening رویدادها یا Event‌های زیادی هستید، می‌توانید از Observer‌ها برای گروه‌بندی همه listener‌های خود در یک Class واحد استفاده کنید. Class‌های Observer دارای Method هایی با نام‌های مختلف هستند، که این  Method‌ها نمایانگر همان Event هایی هستند که می‌توانید listen کنید. همه‌ی این Method‌ها تنها یک Argument، یعنی Model را می‌توانند به‌عنوان پارامتر ورودی دریافت کنند.

ایجاد Observer ها

برای ایجاد Observer‌ها از دستور زیر استفاده می‌کنیم.

php artisan make:observer UserObserver --model=User

دستور بالا یک Observer جدید به‌نام UserObserver در App/Observers ایجاد می‌کند. در صورتی که پوشه‌ی Observers از قبل موجود نباشد، با اجرای دستور بالا این پوشه به‌صورت خودکار ایجاد می‌شود.

Observer‌ها در ابتدا به شکل زیر هستند:

<?php

namespace App\Observers;

use App\User;

class UserObserver
{
    /**
     * Handle the User "created" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function created(User $user)
    {
        //
    }

    /**
     * Handle the User "updated" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function updated(User $user)
    {
        //
    }

    /**
     * Handle the User "deleted" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function deleted(User $user)
    {
        //
    }

    /**
     * Handle the User "forceDeleted" event.
     *
     * @param  \App\User  $user
     * @return void
     */
    public function forceDeleted(User $user)
    {
        //
    }
}

برای register کردن یک Observer، از متد observe در Model استفاده می‌کنیم. برای این‌ کار باید به شکل زیر دستور observe را در متد boot یکی از Service Provider ها، به‌طور مثال AppServiceProvider تعریف کنیم.

<?php

namespace App\Providers;

use App\Observers\UserObserver;
use App\User;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        User::observe(UserObserver::class);
    }
}

جمع‌بندی:

همیشه یکی از اصلی‌ترین دغدغه‌های برنامه‌ نویسانی که با زبان‌ PHP کار می‌کردند، ایجاد ارتباط بین برنامه و Database، زدن Query‌های مختلف برای اضافه کردن، حذف کردن، بروزرسانی و اعمال تغییراتی هر چند کوچک بر روی داده‌های Database و مشکلاتی از این دست بود. همان‌طور که در مقدمه این مقاله گفتیم، یکی از بزرگترین مزایای فریم ورک لاراول، استفاده از Eloquent ORM است که به تمام دغدغه‌هایی که گفتیم پایان داده است. آشنا بودن هر چه بهتر با laravel eloquent ، سرعت کار ما را چندین برابر افزایش و کیفیت برنامه‌های ما را به مراتب بهتر می‌کند.

اگر می‌خواهید بیشتر در مورد لاراول مطالعه کنید مقالات زیر را دنبال کنید

اگر به یادگیری بیشتر لاراول علاقه داری می‌تونی در دوره آموزشی لاراول کاربردی (بسته پروژه محور) شرکت کنی، این دوره شامل ۱۲ پروژه کاربردی و پر استفاده در دنیای واقعی است، که تمامی پروژه‌ها به صورت کامل برنامه‌ نویسی خواهند شد، تا دانشجو بتواند با روند ایجاد و تکمیل پروژه به صورت کامل آشنا شود.

چه امتیازی به این مقاله می دید؟
نویسنده علی مجیدی
Backend Developer | PHP & Laravel Developer

نظرات کاربران

MAHDI7

خیلی ممنون که اینقدر واضح توضیح دادید.

ارسال دیدگاه
خوشحال میشیم دیدگاه و یا تجربیات خودتون رو با ما در میون بذارید :