همیشه برای برنامه نویسان ایجاد ارتباط بین یک برنامه و Database آن کمی دردسر ساز بوده است. فریم ورک لاراول نسبت به سایر رقبای خود، همانطور که در مقاله “فریم ورک لاراول چیست” گفتیم، یک ویژگی منحصر به فرد یعنی Eloquent ORM دارد. با بهرهگیری از قابلیت laravel eloquent ، ایجاد ارتباط بین هر برنامهای با Database مربوط به آن، تنها چند ثانیه زمان میبرد.
اگر شما هم به فریم ورک لاراول علاقهمندید و تمایل دارید از این فریم ورک کسب درآمد کنید پیشنهاد میکنیم در دوره آموزش لاراول شرکت نمایید و از مزایای پشتیبانی دوره بهرهمند شوید.
هر کدام از جدولهای Database با Model هایی نظیر آن در برنامه ارتباط برقرار میکند. بهطور مثال اگر ما در Database یک جدول بهنام Flights بسازیم، برای ایجاد ارتباط بین برنامهی خود و این Database، کافی است یک Model به نام Flight بسازیم. در این صورت به طور خودکار برنامه بین جدول Flights از Database و Model نظیر آن، یعنی Flight یک ارتباط ایجاد میکند. با استفاده از این Modelها میتوانیم تمام Query هایی که میخواهیم را بر روی جدولهایمان اعمال کنیم.
همانطور که گفتیم برای ایجاد ارتباط با جدولهای Database لازم است برای هرکدام از از جدولهای موجود در Database یک 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، امکان دارد که نتوانید بهصورت صحیح با 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';
}
همانطور که گفتیم، 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;
}
بهصورت پیشفرض 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';
}
بهطور پیشفرض تمام 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 ها، یک مقدار پیشفرض در نظر بگیریم، از دستور زیر استفاده میکنیم.
<?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$ وارد کنیم.
زمانی که یک 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();
با استفاده از دستورات 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های مختلف موجود در لاراول نیز استفاده کنیم. بهطور مثال فرض کنید میخواهید تمام داده در یک آرایه دریافت شوند، که این آرایه دارای چند Index و هر کدام از این Index ها، دارای ۲۰۰ مقدار یا Value باشد، یا به عبارتی دیگر، بهطور مثال اگر ۱۱۵۰ داده بخواهد دریافت شود و بخواهیم آنها را در بخشهای ۲۰۰ تایی قرار دهیم، کافی است از Chunk Collection استفاده کنیم.
Flight::chunk(200, function ($flights) {
foreach ($flights as $flight) {
//
}
});
اگر دادههای ما ۱۰۰۰ تا باشد، آن دادهها بهصورت دقيق در ۵ دسته یا Index قرار میگیرند. حال اگر مانند مثال بالا ۱۱۵۰ داده داشته باشیم، ۵ Index که هر کدام ۲۰۰ مقدار یا Value دارند و ۱ Index که ۱۵۰ مقدار دارد، دریافت میشود.
در دستور بالا ما دادهها را در بخشهای ۲۰۰ تایی دریافت میکنیم و بر روی هر کدام از آنها توسط حلقه foreach میتوانیم یک عمل انجام دهیم.
هنگام استفاده از 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();
گاهی اوقات بهجای دریافت همه یا چند داده، تنها یکی از آنها را میخواهیم دریافت کنیم. برای دریافت یک داده میتوانیم از 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 () {
// ...
});
اگر بخواهیم در صورت پیدا نشدن Model، یک exception یا یک Error به کاربر نشان دهیم، میتوانیم از findOrFail و firstOrFail استفاده کنیم.
$model = App\Flight::findOrFail(1);
$model = App\Flight::where('legs', '>', 100)->firstOrFail();
اگر در دستورات بالا Model پیدا نشود، برای کاربر یک Error 404 نمایش داده میشود.
هنگامی که دادهها را از 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 ایجاد کنیم و سپس توسط ()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']);
از ()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ها میتوانیم از isDirty ,isClean و wasChanged استفاده کنیم.
$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
$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
$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 یکی از مهمترین بخشهای این آموزش است که رعایت نکردن آن باعث به خطر افتادن جدی امنیت سایت میشود.
فرض کنید یک form با بخشهای نام و نام خانوادگی، شماره همراه، آدرس و رمز عبور داریم که کاربر باید آن را تکمیل کنید. حال برای همین form در Database علاوه بر بخشهای بالا چند بخش دیگر مانند نقش کاربری که شامل Admin یا User، دسترسیها و بخشهای امنیتی مهم دیگری میشود داریم. در این صورت هر کاربری که دانش برنامهنویسی داشته باشد، میتواند هنگام تکمیل form با اعمال چند تغییر کوچک نقش کاربری خود را Admin وارد کند و به تمام اطلاعات سایت دسترسی پیدا کند.
برای جلوگیری از این Bugهای امنیتی، باید در همان ابتدا تعیین کنیم که چه field هایی میتواند مقدار بگیرد. بدین منظور میتوانیم از guarded یا fillable استفاده کنیم.
هنگامی که یک 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، میتوانیم از firstOrCreate و firstOrNew استفاده کنیم.
// 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']
);
// 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 از Database میتوانیم یک instance از آن Model را توسط ()delete حذف کنیم.
$flight = App\Flight::find(1);
$flight->delete();
اگر بخواهیم به جای حذف کلی 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، به شکل زیر استفاده کنیم.
$deletedRows = App\Flight::where('active', 0)->delete();
در دستور بالا، تمام پروازهایی که غیرفعال هستند را از Database حذف میکنیم.
برای حذف دادههای مهم از Database، قطعا حذف کلی و بدون بازگشت آنها راهی پرخطر است. برای حذف این نوع دادهها از Soft Deleting استفاده میکنیم.
در اینصورت پس حذف نرم یا همان 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()) {
//
}
همانطور که در بالا گفتیم، Modelهای Soft Delete شده، بهطور خودکار از Database بهصورت غیر دائم حذف میشوند و هنگام زدن Queryهای معمولی، هیچ نتیجهای را به ما برنمیگرداند. حال اگر بخواهیم از این Model در جایی استفاده کنیم، میتوانیم از withTrashed استفاده کنیم.
$flights = App\Flight::withTrashed()
->where('account_id', 1)
->get();
همچنین از withTrashed میتوانیم در Queryهای ارتباطی یا relationship Queryها نیز استفاده کنیم.
$flight->history()->withTrashed()->get();
برای اینکه Model هایی که Soft Delete کردیم، دوباره قابل دسترسی باشد میتوانیم از ()restore استفاده کنیم.
$flight->restore();
همچنین برای restore کردن چند دادهی خاص از Model، میتوانیم به شکل زیر عمل کنیم.
App\Flight::withTrashed()
->where('airline_id', 1)
->restore();
اگر بخواهیم یک داده Soft Delete شده را بهصورت دائم از Database حذف کنیم، میتوانیم از forceDelete استفاده کنیم.
// Force deleting a single model instance...
$flight->forceDelete();
// Force deleting all related models...
$flight->history()->forceDelete();
اگر بخواهیم تعدادی 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();
برای Queryها میتوانیم یک دامنه، حریم یا همان Scope تعریف کنیم. بهطور مثال Scopeهای جهانی یا Global، محلی یا Local و پویا یا Dynamic، که در ادامه به شرح کامل هر کدام از آنها میپردازیم.
هنگامی که از Global Scopeها استفاده میکنیم، میتوانیم بر روی Query هایی که به سمت Model، برای دریافت دادهها ارسال میکنیم، یک محدودیت خاصی اعمال کنیم. عملکرد Soft Deleting یا همان حذف نرم در لاراول، زمانی که از Global Scopeها استفاده میکنیم، تنها Modelهای پاک نشده را به ما برمیگرداند. همچنین میتوانیم یک Global Scope خاص و سفارشی را، که در ادامه به آموزش ایجاد این Scope میپردازیم، برای خود ایجاد کنیم، که ایجاد این Scope باعث میشود که به راحتی محدودیتهایی که میخواهیم را بر روی همهی Model ها، همزمان اعمال کنیم.
ایجاد یک 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 به یک 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
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 را حذف کنید، باید از 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 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);
}
}
هنگامی که 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();
گاهی اوقات ممکن است بخواهید یک 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 یکسان هستند یا خیر. متد is برای این استفاده میشود که شناسایی کنید آیا این دو Model دارای Primary Key، جدولها و Database مشترکی هستند یا خیر.
if ($post->is($anotherPost)) {
//
}
Modelهای laravel eloquent شامل Eventهای زیادی میشوند که این Eventها به شما این امکان را میدهند تا در Modelها به جستو جو بپردازید. بعضی از این موارد شامل retrieved ,creating ,created ,updating ,saving ,saved ,deleting ,deleted ,restoring ,restored میشوند. علاوه بر این Eventها به شما این امکان را میدهد که هر بار که یک model class در Database ذخیره یا بروزرسانی شود، به راحتی یک کد دلخواه را اجرا کنید. همچنین تمام Eventها instance یک Model را از طریق Constructor آن دریافت میکنند.
برای شروع، ابتدا یک 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های خود استفاده کنید.
به جای استفاده از 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) {
//
});
}
}
زمانی در یک Model خاص در حال listening رویدادها یا Eventهای زیادی هستید، میتوانید از Observerها برای گروهبندی همه listenerهای خود در یک Class واحد استفاده کنید. Classهای Observer دارای Method هایی با نامهای مختلف هستند، که این Methodها نمایانگر همان Event هایی هستند که میتوانید listen کنید. همهی این Methodها تنها یک Argument، یعنی Model را میتوانند بهعنوان پارامتر ورودی دریافت کنند.
برای ایجاد 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 ، سرعت کار ما را چندین برابر افزایش و کیفیت برنامههای ما را به مراتب بهتر میکند.
اگر به یادگیری بیشتر لاراول علاقه داری میتونی در دوره آموزش لاراول کاربردی (بسته پروژه محور) شرکت کنی، این دوره شامل ۱۲ پروژه کاربردی و پر استفاده در دنیای واقعی است، که تمامی پروژهها به صورت کامل برنامه نویسی خواهند شد، تا دانشجو بتواند با روند ایجاد و تکمیل پروژه به صورت کامل آشنا شود.