سلام دوستان لاراولی! می‌خوایم هشتمین و آخرین قسمت مجموعه سوالات ۸۰ مصاحبه لاراول رو با هم بررسی کنیم 🔥

 

سوالاتی که توی این قسمت بررسی می‌کنیم:

  1. چطوری توابعی بصورت سراسری داشته باشیم؟
  2. خطای کد زیر چیه؟
  3. یک فریم‌ورک MVC چیه؟
  4. Event و Listener چطوری کار می‌کنن؟
  5. Passport چیه؟
  6. Scope ها توی مدل برای چه کاری استفاده میشن؟
  7. Implicit Binding چیه؟
  8. Explicit Binding چیه؟
  9. چطوری از Transaction ها استفاده کنیم؟
  10. کلید aliases توی فایل app.php چیه؟

 

71. چطوری توابعی بصورت سراسری داشته باشیم؟

راه‌های زیادی وجود داره برای اینکه توابعی بصورت سراسری توی برنامه داشته باشیم. یکی از اونها ساختن کلاس‌ها با متدهای استاتیک هست. اما اگه بخوایم از توابع معمولی PHP استفاده کنیم چطور؟ چطوری میشه اونها رو به برنامه اضافه کرد؟

خب ابتدا یک فایل درست می‌کنیم و توی اون توابعمون رو تعریف می‌کنیم. این فایل رو با اسم دلخواه مثلاً general.php رو توی مسیر app/Helpers قرار می‌دیم:

<?php

function clear_all_caches() {
    // ...
}

function set_theme($theme) {
    // ...
}

مرحله بعد باید این فایل رو به composer.json اضافه کنیم. توی قسمت autoload، یک کلید درست می‌کنیم به اسم files. این کلید بصورت یک آرایه هست و توی اون می‌تونیم بی‌نهایت فایل اضافه کنیم. مسیر فایل general.php رو داخل این آرایه قرار می‌دیم:

{
    // ...
    "autoload": {
        "psr-4": {
            // ...
        },
        "files": [
            "app/Helpers/general.php"
        ]
    },
    // ...
}

و نهایتاً دستور composer dump-autoload رو توی خط فرمان می‌زنیم تا این فایل شناسایی بشه.

 

72. خطای کد زیر چیه؟

public function handle(Request $request)
{
    $user = User::create($request->all());

    if ($user instanceof User) {
        redirect('profile');
    } else {
        redirect()->back();
    }
}

خطای این کد توی خط‌های ۶ و ۸ هست. با اجرای این کد می‌بینیم که هدایت (redirect) انجام نمیشه. دلیلش هم اینه که قبل تابع redirect() از return استفاده نکردیم. پس یادمون باشه که همیشه از return قبل از این تابع استفاده کنیم:

return redirect()->back();

 

73. یک فریم‌ورک MVC چیه؟

MVC یک مخفف از سه کلمه هست:

Model - View - Controller

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

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

Model: مدیریت داده‌ها توی یک برنامه به عهده‌ی این بخش هست. داده چیه؟ همون چیزایی که ما از کاربر می‌گیریم یا به اون نشون میدیم. توی الگوی MVC، نحوه‌ی ذخیره، ویرایش و حذف داده‌ها وظیفه‌ی مدل هست. بطور کلی، هسته‌ی برنامه مدل در نظر گرفته میشه.

Controller: کنترلر که اسمش روی اون  هست، وظیفه کنترل کردن جریان اطلاعات رو داره. فرض کنیم توی View یک فرم برای ثبت خبر داریم. وقتی کاربر فرم رو ارسال می‌کنه، وظیفه کنترلر گرفتن این ورودی و ارسال اون به مدل هست. کنترلر، اطلاعات رو طبق قوانینی که مدل تعریف کرده به مدل ارسال میکنه.

برای درک مفصل این الگو، پست زیر رو پیشنهاد می‌کنم:

 

74. Event و Listener چطوری کار می‌کنن؟

توی برنامه رویداد‌ (Event) های فراوانی رخ میده. مثلاً ثبت نام یک کاربر یا ثبت شدن یک نظر جدید. خب چطور می‌تونیم این رویدادها رو مدیریت کنیم؟ مثلاً بعد از ثبت نام یک کاربر جدید، می‌خوایم یک ایمیل خوش‌آمدگویی برای کاربر و یک ایمیل اطلاع‌رسانی برای مدیر ارسال بشه. با مکانیزم Event/Listener می‌تونیم رویدادها رو خیلی راحت مدیریت کنیم.

توی مثال بالا، "ثبت نام کاربر" میشه یک Event. به کارهایی که باید بعد از ثبت‌ نام کاربر انجام میشه میگن Listener. در واقع برای یک رویداد می‌تونیم چندین Listener داشته باشیم. ما رویدادها رو توی کلاس EventServiceProvider و توی پراپرتی $listen ثبت می‌کنیم و به اون می‌تونیم بی‌نهایت Listener ضمیمه کنیم:

class EventServiceProvider
{
    // ...
    protected $listen = [
        Registered::class => [
            sendWelcomeMessage::class,
            NotifyAdmin::class,
            ClearUsersCache::class,
            UpdateStats::class,
        ],
    ];

}

توی کد بالا، Registered یک رویداد و آیتم‌های توی آرایه Listener هایی هستن که با رخ دادن رویداد اجرا میشن.

صدا زدن یک رویداد توی برنامه هم بصورت زیر هست:

event(new Registered());

 

75. Passport چیه؟

اگه برای قسمت‌هایی از برنامه که بصورت API قابل دسترسی هست نیاز به احراز هویت داریم، با استفاده از Laravel Passport خیلی راحت می‌تونیم این کار رو انجام بدیم. احراز هویت توی API ها معمولاً با تبادل رشته‌هایی به اسم توکن بین کاربر و سرور انجام میشه. پاسپورت وظیفه تولید، مدیریت این توکن‌ها و بطور کلی عملیات احراز هویت از API رو به عهده می‌گیره.

 

76. Scope ها توی مدل برای چه کاری استفاده میشن؟

فرض کنیم توی کوئری‌هایی که با مدل Post می‌زنیم، همیشه یک شرط رو داریم بررسی می‌کنیم. مثلاً وضعیت Post "منتشر شده" باشه:

Post::where('status', 'published')->get();
Post::where('status', 'published')->where('category', 9)->get();
Post::where('status', 'published')->where('id', 429)->first();

همونطور که می‌بینیم شرط where('status', 'published') داره توی همه کوئری‌های ما تکرار میشه. خب اگه این شرط قراره توی همه کوئری‌ها تکرار بشه، بهتره که این شرط رو توی Scope (حوزه) سراسری تعریف کنیم. شرط‌هایی که توی حوزه سراسری تعریف می‌کنیم، بصورت خودکار روی همه کوئری‌ها اعمال میشن.

یکی از راه‌های نوشتن این شرط‌ها، نوشتن اونها توی مدل بصورت زیر هست:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;

class Post extends Model
{
    protected static function booted()
    {
        static::addGlobalScope('is_published', function (Builder $builder) {
            $builder->where('status', 'published');
        });
    }
}

با این کار، کد SQL برای کوئری‌ای مثل Post::all() بصورت زیر خواهد بود:

select * from `posts` where `status` = "published"

یک نوع دیگه Scope داریم که به اونها میگن محلی یا Local. این Scope ها رو فقط در زمان نیاز استفاده می‌کنیم. نحوه نوشتن اونها بصورت زیر هست:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    public function scopeActive($query)
    {
        return $query->where('active', 1)
                     ->where('status', 'published')
                     ->whereNull('deleted_at');
    }
}

اینجا یک Scope به اسم active ساختیم که نحوه استفاده از اون بصورت زیر هست:

$post = Post::active()->get();

 

77. Implicit Binding چیه؟

برای این سوال و سوال بعدی، لازمه که اطلاعاتی از Route Model Binding داشته باشیم که پیشنهاد می‌کنم سوال ۱۴ رو بخونید.

روت زیر رو در نظر بگیرید:

Route::get('/users/{user}', function (User $user) {
    return $user->email;
});

توی این کد، پارامتر {user} رو تعریف کردیم و توی کلاژر گفتیم که این پارامتر مربوط به مدل User هست. یعنی با باز کردن آدرس /users/1 فریم‌ورک بطور خودکار یک کوئری بصورت User::find(1) میزنه و خروجی رو می‌ریزه توی آرگومان $user یا به اصلاح اون رو Bind می‌کنه به $user.  به این روش Bind کردن میگن Implicit Binding. توی این روش، برای پارامتر {user}، توی کلاژر باید یک آرگومان با همین اسم و که با مدل مربوطه type-hint شده، وجود داشته باشه.

 

78. Explicit Binding چیه؟

توی روش قبل، برای یک پارامتر روت، باید همیشه اسم مدل رو بصورت type-hint ذکر می‌کردیم. اما یه روشی وجود داره که به بصورت صریح (Explicit) فریم‌ورک بگیم که پارامتر {user} همیشه برای مدل User هست. برای این کار، به اول متد boot توی RouteServiceProvider کد زیر رو اضافه می‌کنیم:

Route::model('user', \App\Models\User::class);

اینطوری توی هر روت که پارامتر {user} وجود داشته باشه، فریم‌ورک بصورت خودکار در نظر می‌گیره که این پارامتر برای مدل User هست و بصورت خودکار عملیات Binding رو انجام میده:

Route::get('/users/{user}', function ($customer) {
    return $customer->email;
});

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

 

79. چطوری از Transaction ها استفاده کنیم؟

Transaction ها به عملیات دیتابیس مربوط میشن و اگه با اونها آشنایی ندارین پیشنهاد می‌کنم قسمت Atomicity پست زیر رو بخونین:

خب برای پیاده‌سازی این قابلیت توی لاراول، ۲ روش خودکار و دستی وجود داره. روش اول که عملیات Commit و Rollback رو خودکار انجام میده بصورت زیر هست:

DB::transaction(function () {
    DB::table('users')->update(['votes' => 1]);

    DB::table('posts')->delete();
});

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

try {
    DB::beginTransaction();

    DB::table('users')->update(['votes' => 1]);
    DB::table('posts')->delete();

    DB::commit();
} catch(Exception $e) {
    DB::rollBack();
}

 

80. کلید aliases توی فایل app.php چیه؟

اگه فایل config/app.php رو ببینیم، یک کلید به اسم aliases وجود داره با محتویات زیر:

'aliases' => [

    'App' => Illuminate\Support\Facades\App::class,
    'Arr' => Illuminate\Support\Arr::class,
    'Artisan' => Illuminate\Support\Facades\Artisan::class,
    'Auth' => Illuminate\Support\Facades\Auth::class,
    'Blade' => Illuminate\Support\Facades\Blade::class,
    // ...
],

برای مثال کلید Auth توی این آرایه رو در نظر بگیرید. اگه هر جایی از برنامه بصورت زیر از Auth استفاده کنیم:

\Auth::user();
\Auth::id();
\Auth::check();

فریم‌ورک بصورت خودکار در نظر می‌گیره که منظور ما کلاسی هست که توی aliases جلوی کلید Auth قرار گرفته. یعنی:

Illuminate\Support\Facades\Auth::class;

همونطور که می‌بینیم، دیگه لازم نیست به نیم‌اسپیس کامل اشاره کنیم.

می‌تونیم کلاس‌های شخصی خودمون رو هم توی این آرایه قرار بدیم:

'aliases' => [
    // ...
    'MyClass' => Full\Path\To\A\Long\ClassName::class
],

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

\MyClass::raise();

 

و این هم از قسمت آخر! خوشحالم که تونستیم ۸۰ سوال فریم‌ورک لاراول رو بررسی کنیم. امیدوارم به اطلاعاتتون اضافه شده باشه و استفاده کرده باشین. نظرتون رو درباره این ۸۰ سوال بهم بگین. روزاتون قشنگ و جذاب 💪😉

 

منابعی که برای این قسمت استفاده کردم: