فصل ۱۰-۲: آشنایی Eloquent و متدهای Modelها در لاراول

06 آبان 1397
درسنامه درس 13 از سری لاراول
Laravel-Main-model

با مطالعه و بررسی دقیق فصل ۱۰-۱ با تمام زوایای متدهای مورد استفاده در مدل لاراول آشنا شدید. این توابع به شما کمک می‌کنند که در کمترین زمان ممکن به بهترین بازدهی در خروجی گرفتن از دیتابیس خود دست پیدا کنید. حال در ادامه‌ی مطالب فصل ۱۰-۱، در این فصل به ارائه‌ی دقیق مبحث مدل‌ها در لاراول پرداخته و با دقت تمام Eloqunet را موشکافی خواهیم کرد.

بازیابی اطلاعات با استفاده از Eloquent

جهت بازیابی تمام اطلاعات موجود در دیتابیس کافی‌ست دستور زیر را در فایل controller خود یا در Clouser Function‌ موجود در route قرار دهید تا تمام اطلاعات موجود در جدول دیتابیس مورد نظر برای شما بازیابی شود. به فرض مثال برای جدول users می‌توان به روش زیر عمل کرد:

Route::get('/users', function(){
    $allContacts= Contact::all();
    return view('users.index', $allContacts);
}

با استفاده از دستور فوق تمام اطلاعات موجود در جدول و ستون‌های contacts به صورت یک آرایه در اختیار شما قرار می‌گیرد. برای دستیابی به هر فیلد آرایه کافی‌ست عبارت زیر را بنویسید:

Route::get('/users', function(){
    $allContacts= Contact::all();
    $userName = $allContacts->usersname;
    return view('users.index', $userName);
}

متد Chuck

درصورتیکه برنامه‌ی شما نیاز دارد تا هزاران رکورد را پردازش کند می‌توانید از دستور chuck استفاده کنید. این متد اطلاعات موجود در یک مدل را که با دیتابیس سر و کار دارد به چندین تکه‌ی کوچکتر تبدیل می‌کند و آن را به یک closuer جهت پردازش ارسال می‌کند. از مزیت‌های این روش می‌توان به نگه‌داری حافظه هنگام انجام پردازش‌های طولانی اشاره کرد. این متد به صورت زیر بکار گرفته می‌شود:

Contact::chunk(200, function ($contacts) {
    foreach ($contacts as $contact) {
        //
    }
});

همانطور که در مثال فوق ملاحظه کردید. اولین آرگومان این متد تعداد رکوردهایی‌ست که در هر تکه یا chunk دریافت می‌شود و دومین آرگومان یک تابع Clouser است که عملیاتی را روی هر تکه chunk انجام می‌دهد.

متد Cursor

کمی درباره این متد صحبت کنیم! در ابتدا با یک مثال شروع می‌کنیم. فرض کنید قصد دارید یک ویدیو را مشاهده کنید. ابتدا نام ویدیو را جستجو کرده و سپس به صفحه‌ی پخش‌کننده‌ی (پلیر) ویدیو ارجاع داده خواهید شد. حال در این صفحه شما می‌توانید ابتدا ویدیو را دانلود کرده و سپس در کامپیوتر یا موبایل آن را تماشا کنید که اینکار باعث می‌شود حجم و فضای سیستم شما اشغال شود. یا می‌توانید ویدیو را به صورت آنلاین و بدون دانلود، دقیقه به دقیقه لود کرده و تماشا کنید که این حالت باعث می‌شود فضای حافظه‌ی سیستم شما اشغال نشود. حال به تعریف متد Cursor می‌پردازیم. این متد مانند Chunk عمل می‌کند با این تفاوت که اطلاعات را به صورت سطر به سطر (row) از دیتابیس دریافت کرده و در لحظه‌ پردازش می‌کند. یعنی در ابتدا تمام اطلاعات دیتابیس را بارگذاری نمی‌کند. بلکه به صورت سطر به سطر و مرحله به مرحله این پردازش را انجام می‌دهد. از مزیت‌های استفاده از متد cursor می‌توان به موارد زیر اشاره کرد:

  1. ذخیره زمان، زیرا نیازی به بازیابی کلیه‌ی اطلاعات نیست و به صورت مرحله به مرحله اطلاعات بازیابی می‌شود.
  2. ذخیره حافظه، هم در بخش سرور و هم در بخش کلاینت یا کاربر زیرا نیازی به نمایش اطلاعات حجیم دیتابیس نیست.

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

foreach (Flight::where('foo', 'bar')->cursor() as $flight) {
    //
}

متدهای بازیابی اطلاعات تک

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

متد find‌ و first

برای این کار می‌توان از دو متد find‌ و first بهره برد که در ذیل به آن اشاره خواهیم کرد. این متد‌ها به جای بازگرداندن مجموعه‌ای از مدل‌ها تنها یک نمونه‌ی مدل را باز می‌گردانند:

//اطلاعات یک مدل را متناسب با کلید اصلی آن باز می‌گرداند
$flight = App\Flight::find(1);

//اولین مقداری که پس از اعمال دستور Where
// بدست می‌اید را به نمایش می‌گذارد
$flight = App\Flight::where('active', 1)->first();

همانطور که ملاحظه کردید در نمونه‌ی اول، رکوردی با متد find برای id = 1 در متغییر flights ذخیره شد و در متد دوم اولین مقداری که با شرط active = 1 می‌باشد در متغییر flights ذخیره گردید.

همچنین می‌توان متد find را برای چندین رکورد اعمال کرد. یعنی با اجرای یک دستور چندین رکورد را پیدا کنیم. این رکوردها در قالب یک کالکشن یا Collection ارائه خواهند شد:

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

متد Not Found Exception

برخی مواقع می‌خواهیم هنگام پیدا نشدن یک رکورد عملیات دیگری انجام پذیرد. این روش برای مسیرهای (routes) یا کنترلرها (controllers) بسیار مناسب است. متد‌های findOrFaild و firstOrFaild به عنوان دو متد بکار گرفته می‌شوند که اولین نتایج کوئری را بازیابی می‌کنند و اگر این نتایج موجود نباشند یک عبارت از کلاس Illuminate\Database\Eloquent\ModelNotFoundException به نمایشگر ارسال می‌کنند! به مثال زیر توجه کنید:

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

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

در صورتیکه هر یک از رکوردهای فوق یافت نشد یک خطای ۴۰۴ به کاربر نمایش داده خواهد شد.

وارد کردن اطلاعات و بروزرسانی آن‌ها در مدل‌ها

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

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

برای ساختن یک رکورد جدید در دیتابیس ابتدا باید یک مدل را ایجاد کرده و سپس متناسب با آن صفحه‌ی ویو و کنترلر را بسازیم. بنابراین اگر شما به مبحث کنترلر‌ها هنوز مسلط نشدید لطفا مقاله‌ی زیر را مطالعه بفرمایید:

پس از مطالعه‌ی مقاله‌ی فوق باید در جریان باشید که هر کنترلری که به صورت resource تعریف می‌شود دارای یک متد به نام store است که وظیفه‌ی ذخیره اطلاعات را به عهده دارد. این متد به در بستر پروتکل HTTP و متد POST اطلاعات را از سمت کاربر به کنترلر ارسال کرده و کنترلر آن را به مدل فرستاده و در نهایت پردازش جهت ذخیره‌ی این اطلاعات انجام می‌شود. متدی که وظیفه‌ی ذخیره این اطلاعات را به عهده دارد، متد ()save نام دارد. این متد به صورت زیر عمل می‌کند:

<?php

namespace App\Http\Controllers;

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

class FlightController extends Controller
{
    /**
     * Create a new flight instance.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        $flight = new Flight;
        $flight->name = $request->name;
        $flight->save();
    }
}

در مثال فوق همانطور که ملاحظه می‌کنید ابتدا متد store را ایجاد کرده‌ایم. این متد یک آرگومان با روش Dependency Injection دریافت کرده که حاوی اطلاعات ارسالی توسط کاربر است. بنابراین متغییر request$ حاوی اطلاعات کاربر می‌باشد. سپس در اولین خط یک نمونه‌ی جدید از مدل را جهت ذخیره‌ی اطلاعات در دیتابیس ایجاد کرده‌ایم. در نهایت فیلدی که با ستون name در دیتابیس ذخیره شده است را معادل فیلدی که با نام name در فرم ویو (view) وجود داد، قرار داده‌ایم. حال در انتها که عملیات انتساب‌ها انجام شد. دستور ()save را صادر کرده تا اطلاعات در پایگاه داده به واسطه‌ی مدل ذخیره گردد. همچنین دو ستون created_at و updated_at نیز متناسب با زمان ذخیره‌سازی این رکورد در دیتابیس ذخیره خواهند شد.

بروزرسانی دیتابیس از طریق مدل‌ها

بروزرسانی یک مدل نیز با دستور ()save امکان‌پذیر خواهد بود. اما در ابتدای کار باید رکوردی که مدنظرمان هست را از طریق دستور find یا findOrFaild و یا هر دستور دیگری فراخوانی کرده و سپس معادل‌سازی را انجام داده و در نهایت دستور ()save را اعمال کنیم:

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

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

$flight->save();

در این مثال ابتدا با استفاده از دستور find رکوردی با کلید اصلی id = 1 را بازیابی کرده و سپس مقدار ستون name آن را به new Flight Name تغییر می‌دهیم. در نهایت با اعمال دستور ()save اطلاعات ذخیره شده و ستون updated_at که زمان بروزرسانی را ذخیره می‌کند، تغییر می‌نماید.

بروزرسانی انبوه

لاراول امکاناتی را در اختیار ما قرار داده است که با استفاده از آن بتوانیم تمامی رکوردهای موجود در یک جدول را که از شروط مشخصی پیروی می‌کنند بروزرسانی کنیم. به مثال زیر توجه کنید:

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

در این مثال تمامی رکوردهایی که مقدار ستون active آنها برابر ۱ است و ستون destination آنها معادل San Diego می‌باشد با تغییر ستون delayed آنها به عدد ۱، آپدیت و بروزرسانی خواهند شد.

تخصیص انبود (Mass Assignment)

در بسیاری از فرم‌هایی که در اختیار کاربران قرار می‌گیرد همواره باید یک سری ستون‌های خاص در جداول دیتابیس توسط این فرم‌ها و کاربران آپدیت یا اضافه شوند ( از متدهایی مانند save یا create برای آپدیت و ایجاد یک رکورد استفاده می‌شود). اما نکته‌ی مهم اینجاست که همیشه و همواره برخی از ستون‌ها به صورت خودکار پر می‌شوند مثلا ستون id یا ستون cotacts_id و باید از هرگونه اعمال نفوذ برای تغییر مقادیر این ستون‌ها خودداری شود. چون بعضا مشاهده شده است که هکرها و کاربرانی که قصد تخریب یک پایگاه‌داده را دارند به سادگی و با ارسال چندین دستور به فرم‌ها، فرمان‌هایی را برای تغییر این ستون‌ها اعمال می‌کنند. حال برای جلوگیری از این موضوع در داخل هر مدل یک متغییر به نام fillable$ (به عنوان لیست سفید قابل ویرایش و اضافه کردن) و guarded$ (به عنوان لیست سیاه برای جلوگیری از ویرایش و ذخیره) وجود دارد. بنابراین ستون یا ستون‌هایی که در متغییر fillable$ قرار داده می‌شوند به عنوان ستون‌هایی که قابلیت ویرایش دارند معرفی خواهند شد و ستون‌هایی که در متغییر guarded$ ذخیره می‌شوند به عنوان ستون‌هایی که قابلیت ویرایش ندارند ارائه خواهند شد.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Flight extends Model
{
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name', 'price'];
    protected $guarded = ['id','contact_id','updated_at','created_at'];
}

در نظر دارید که اگر بخواهیم تمام فیلدها را اجازه‌ی دسترسی دهیم باید متغییر gaurded$ را به صورت زیر تعریف کنیم:

/**
 * The attributes that aren't mass assignable.
 *
 * @var array
 */
protected $guarded = [];

متد firstOrCreate و firstOrNew

گاهی می‌خواهیم به نرم‌افزار بگوییم که یک نمونه‌ با مشخصات موردنظر برای من ارسال کن در غیر اینصورت آن را بساز! برای اینکار از دستور *firstOr استفاده می‌کنیم که به دو متد new و create تقسیم می‌شود. تفاوتی که بین این دو متد وجود دارد این است که: متد firstOrCreate ابتدا به دنبال ستون با مقدار مشخص می‌گردد و در صورتیکه آن را پیدا نکند یک نمونه ایجاد کرده و آن را داخل دیتابیس ذخیره می‌کند و سپس اطلاعات را باز می‌گرداند. در حالیکه متد firstOrNew ابتدا بررسی می‌کند که آیا ستون با ویژگی مشخص وجود دارد و اگر وجود نداشت یک نمونه ایجاد کرده و باز می‌گرداند اما آن را در دیتابیس ذخیره نمی‌کند. بنابراین برای ذخیره‌سازی نمونه‌ی ایجاد شده با متد firstOrNew باید از دستور save استفاده کرد. به مثال زیر توجه کنید:

// در ستونی به نام name 
// و با مقدار Flight 10
// ایجاد کرده و آن را درون متغییر
// flight ذخیره می‌کند
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);

// در ستونی به نام name 
// مقداری به نام Flight 10
// ایجاد کرده و آن را درون متغییر
// flight ذخیره می‌کند
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);

متد updateOrCreate

این متد دقیقا مشابه متد firstOrCreate می‌باشد با این تفاوت که ابتدا اقدام به آپدیت کردن یک مدل می‌کند و اگر آن مدل وجود نداشت ابتدا آن را ساخته و سپس عمل بروزرسانی را انجام می‌دهد. در صورت استفاده از این متد نیازی به اعمال دستور ()save نیست:

$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99]
);

حذف مدل‌ها

همانطور که در جریان هستید تا به الان مباحث ساخت و بروزرسانی یک مدل را مطرح کردیم اما سوالی که اینجا مطرح هست برای حذف اطلاعات یک دیتابیس از طریق مدل باید به چه صورت عمل کرد؟

متد delete

از این متد برای حذف یک نمونه از مدل (یا به عبارت دیگر، حذف یک رکورد از دیتابیس) استفاده می‌شود که به صورت زیر تعریف خواهد شد:

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

$flight->delete();

بنابراین در این مثال ابتدا با استفاده از دستور find رکوردی با id = 1 را انتخاب کرده و در متغییر flight$ ذخیره می‌کنیم سپس با اعمال متد delete آن را از دیتابیس برای همیشه حذف کرده‌ایم.

حذف یک رکورد بر اساس key (کلید)

در مثال بالا همانطور که ملاحظه کردید ابتدا یک رکورد را از دیتابیس بازیابی کردیم و پس از ذخیره‌سازی آن در یک متغییر اقدام به حذف آن با متد delete کردیم. اما اگر شما primary key یک رکورد را بدانید می‌توانید بدون ذخیره‌سازی و بازیابی آنرا از دیتابیس خود حذف کنید. نام این متد destroy است:

App\Flight::destroy(1);

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

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

حذف یک رکورد توسط Query

تا به این جای کار با انواع روش حذف داده آشنا شدید اما یک روش حذف اطلاعات هست که با استفاده از دستورهای Query امکان‌پذیر می‌باشد. در مثال زیر تمام رکوردهایی که مقدار active آنها برابر ۰ است حذف می‌شود:

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

حذف نرم Soft Deleting

گاهی برای شما پیش می‌آید که می‌خواهید مطالب از صفحه مدیریت وب سایت شما پس از زدن دکمه‌ی "حذف" به‌جای حذف شدن همیشگی به یک سطح آشغال یا زباله‌دان انتقال کرده و یک مرحله به کاربر فرصت دهیم که در صورت تایید نهایی برای همیشه از دیتابیس حذف شود. در این صورت باید از روش Soft Deleting یا حذف نرم رکوردها استفاده کرد. برای فعالسازی این روش باید ابتدا داخل مدل خود از کلاس Illuminate\Database\Eloquent\SoftDeletes استفاده کرده و متغییر dates$ را برابر عبارت deleted_at قرار دهید. به نمونه‌ی زیر توجه کنید:

<?php

namespace App;

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

class Flight extends Model
{
    use SoftDeletes;

    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = ['deleted_at'];
}

بسیار عالی، حال باید ستون deleted_at‌ را در جدول دیتابیس خود ذخیره کرده تا در صورت حذف نرم هر رکورد زمان و تاریخ آن مشخص شود. بنابراین در داخل فایل migrations مورد نظر خود دستور زیر را اضافه خواهیم کرد:

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

سپس این فایل را migrate کرده و ستون را به جدول خود اضافه کنید. بسیار عالی! کار به اتمام رسید و هم اکنون با اعمال دستور delete در کنترلر، رکورد موردنظر به سطح زباله یا زباله‌دان انتقال پیدا می‌کند.

برای بررسی اینکه آیا یک رکورد به صورت soft Delete حذف شده است یا خیر می‌توان از متد trashed به صورت زیر استفاده کرد:

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

روشی برای بازیابی اطلاعات وجود دارد که علاوه بر رکوردهایی که به روش حذف نرم، در سطل زباله قرار دارند، تمام رکوردهای دیگر را نیز نمایش دهد. با اینکار می‌توان هر آنچه درون زباله‌دان و سایر رکوردها هست را دریافت کرده و به کاربر نمایش دهیم:

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

همچنین می‌توان از کوئری پیوسته و رابطه‌ای برای دستیابی به اطلاعات درون سطل زباله استفاده کرد:

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

دستور دیگری وجود دارد که با استفاده از آن می‌توان تنها و تنها اطلاعاتی که به صورت حذف نرم، در سطل زباله قرار دارند را بازیابی کرد:

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

بازگردانی اطلاعاتی که در سطل زباله قرار دارند یا به اصطلاح restore کردن آنها با متدی تحت عنوان restore انجام می‌پذیرد که به صورت زیر می‌باشد:

$flight->restore();

همانند withTrashed می‌توان متد restore را در جداول رابطه‌دار به صورت زیر استفاده کرد:

$flight->history()->restore();

تمام این مباحث را مطرح کردیم اما موضوعی که بسیار حائز اهمیت است حذف رکوردهایی‌ست که در سطل زباله‌ قرار گرفته‌اند. به عبارتی دیگر می‌خواهیم این رکوردها را برای همیشه از پایگاه داده حذف کنیم. در اینصورت باید از متد forceDelete استفاده کرد:

// حذف یک رکورد
$flight->forceDelete();

// حذف تمام رکوردهای مرتبط 
$flight->history()->forceDelete();

 

بسیار عالی! به شما همراهان گرانقدر روکسو تبریک عرض می‌کنیم. با استفاده از دستورهای فوق توانایی کنترل داده‌های ارسالی و دریافتی را کسب کرده‌اید و هم اکنون می‌توانید نرم‌افزار خود را هوشمندتر کنید. در بخش ۱۰-۳ به توضیح مفصل ساخت کوئری‌ها در مدل اشاره خواهیم کرد. با ما همراه باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری لاراول توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (2 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

محمد
17 شهریور 1397
با سلام ممنون از آموزش های خوبتون یه سئوال داشتم : من از طریق inject برای اضافه کردن دسته بندی به صفحه ی محصولاتم اقدام کردم، الان می خوام توی مدیریت زمانی که محصول ایجاد میشه جلوش دسته بندیش هم نمایش داده بشه، منتها برای من دسته بندی اصلی همراه با زیر دسته هاش نمایش داده میشه، اینرو چطوری باید براش شرط بزارم که فقط دسته بندی که انتخاب شده نمایش داده بشه؟ http://uupload.ir/files/xe06_2018-09-08.png با تشکر

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

روکسو
17 شهریور 1397
سلام وقت شما بخیر در این صورت باید شما یک فیلتر برای مدل خروجی خودتون بنویسید که تنها دسته ها را نمایش دهد.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

fa
13 اردیبهشت 1397
سلام و خسته نباشید آموزشتون عالیه، چرا ادامه نمی دهید و تکمیلش نمی کنید!؟

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.