سلام من آرش فدایی هستم و تو این قسمت از سری آموزشی لاراول ۱۲ قراره درباره Middleware و مدیریت نقش‌ها (Roles) صحبت کنیم. Middleware مثل یه نگهبان عمل می‌کنه که درخواست‌ها رو قبل یا بعد از رسیدن به مقصد بررسی می‌کنه، و مدیریت نقش‌ها به شما کمک می‌کنه دسترسی کاربران رو کنترل کنید (مثلاً فقط ادمین‌ها بتونن چیزی رو حذف کنن). تو این مقاله، یاد می‌گیرید چطور Middleware بسازید، ازش تو مسیرها استفاده کنید و یه سیستم ساده برای مدیریت نقش‌ها پیاده کنید. بریم شروع کنیم!

Middleware: تجربه شخصی من

وقتی اولین بار با Middleware کار کردم، داشتم یه سیستم مدیریت محتوا می‌ساختم و نیاز داشتم فقط ادمین‌ها به بخش‌های خاصی دسترسی داشته باشن. اول فکر می‌کردم باید کلی کد دستی بنویسم، اما Middlewareهای لاراول این کار رو خیلی راحت کردن. یه بار به اشتباه Middleware رو روی یه مسیر اشتباه گذاشتم و کاربرا به صفحه‌ای که نباید، دسترسی پیدا کردن! تو لاراول ۱۲، Middlewareها حتی انعطاف‌پذیرتر شدن و ابزارهای جدید برای دیباگ اضافه شده. حالا بیاید با یه مثال عملی وارد بشیم!

پیش‌نیازها

قبل از شروع، مطمئن بشید که:

  • پروژه لاراول‌تون درست تنظیم شده (مثل قسمت اول).

  • سیستم احراز هویت (مثل چیزی که تو قسمت پنجم با Breeze نصب کردیم) فعاله.

  • مدل User و دیتابیس‌تون آماده‌ست.

ما تو این آموزش یه سیستم نقش‌ها اضافه می‌کنیم تا کاربرا رو به ادمین و کاربر معمولی تقسیم کنیم.

ساخت جدول برای نقش‌ها

اول بیاید یه جدول roles و یه جدول واسطه برای اتصال کاربرها به نقش‌ها بسازیم. دستور زیر رو اجرا کنید:

php artisan make:migration create_roles_and_user_roles_tables

فایل Migration (مثل database/migrations/xxxx_create_roles_and_user_roles_tables.php) رو اینجوری اصلاح کنید:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        Schema::create('roles', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });

        Schema::create('user_roles', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->foreignId('role_id')->constrained()->onDelete('cascade');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('user_roles');
        Schema::dropIfExists('roles');
    }
};

حالا Migration رو اجرا کنید:

php artisan migrate

این کد دو جدول می‌سازه: roles برای ذخیره نقش‌ها (مثل ادمین، کاربر) و user_roles برای اتصال کاربرها به نقش‌ها. یه تجربه از خودم: یه بار فراموش کردم onDelete(‘cascade’) رو بذارم و وقتی یه کاربر رو حذف کردم، نقش‌هاش تو دیتابیس موند و کلی مشکل درست شد!

تعریف مدل و روابط

بیاید مدل Role رو بسازیم:

php artisan make:model Role

فایل app/Models/Role.php رو اینجوری تنظیم کنید:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Role extends Model
{
    protected $fillable = ['name'];

    public function users()
    {
        return $this->belongsToMany(User::class, 'user_roles');
    }
}

و تو فایل app/Models/User.php رابطه معکوس رو اضافه کنید:

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    protected $fillable = ['full_name', 'email', 'password'];

    public function roles()
    {
        return $this->belongsToMany(Role::class, 'user_roles');
    }

    public function hasRole($role)
    {
        return $this->roles()->where('name', $role)->exists();
    }
}

اینجا رابطه belongsToMany یه رابطه چند به چند بین کاربرها و نقش‌ها تعریف می‌کنه. متد hasRole هم چک می‌کنه که کاربر نقش خاصی (مثل admin) داره یا نه.

ساخت Middleware برای چک کردن نقش‌ها

بیاید یه Middleware بسازیم که فقط به ادمین‌ها اجازه دسترسی بده:

php artisan make:middleware RestrictToAdmins

فایل app/Http/Middleware/RestrictToAdmins.php رو اینجوری اصلاح کنید:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class RestrictToAdmins
{
    public function handle(Request $request, Closure $next)
    {
        if (!auth()->user() || !auth()->user()->hasRole('admin')) {
            return redirect()->route('posts.index')->with('error', 'فقط ادمین‌ها دسترسی دارن!');
        }

        return $next($request);
    }
}

حالا Middleware رو تو فایل app/Http/Kernel.php ثبت کنید:

protected $middlewareAliases = [
    'admin' => \App\Http\Middleware\RestrictToAdmins::class,
];

یه تجربه از خودم: یه بار Middleware رو ثبت نکردم و کلی دنبال خطای “Middleware پیدا نشد” گشتم. همیشه مطمئن شید Middleware تو Kernel.php ثبت شده!

استفاده از Middleware در مسیرها

حالا بیاید یه مسیر فقط برای ادمین‌ها بسازیم. تو فایل routes/web.php:

use App\Http\Controllers\PostController;

Route::get('/admin/posts', [PostController::class, 'index'])
    ->middleware('auth', 'admin')
    ->name('admin.posts');

اینجا فقط کاربرهایی که لاگین کردن و نقش admin دارن می‌تونن به /admin/posts دسترسی داشته باشن. اگه کاربر ادمین نباشه، به صفحه اصلی هدایت می‌شه.

افزودن نقش به کاربر

برای تست، بیاید یه کاربر رو ادمین کنیم. می‌تونید این کار رو با یه دستور Artisan یا یه کنترلر انجام بدید. برای سادگی، یه دستور Artisan می‌سازیم:

php artisan make:command MakeUserAdmin

فایل app/Console/Commands/MakeUserAdmin.php رو اینجوری اصلاح کنید:

<?php

namespace App\Console\Commands;

use App\Models\Role;
use App\Models\User;
use Illuminate\Console\Command;

class MakeUserAdmin extends Command
{
    protected $signature = 'user:make-admin {email}';
    protected $description = 'Make a user an admin';

    public function handle()
    {
        $email = $this->argument('email');
        $user = User::where('email', $email)->first();

        if (!$user) {
            $this->error('کاربر پیدا نشد!');
            return;
        }

        $adminRole = Role::firstOrCreate(['name' => 'admin']);
        $user->roles()->syncWithoutDetaching([$adminRole->id]);

        $this->info('کاربر با موفقیت ادمین شد!');
    }
}

حالا با این دستور یه کاربر رو ادمین کنید:

php artisan user:make-admin user@example.com

این دستور نقش admin رو به کاربر با ایمیل مشخص اضافه می‌کنه. من تو یه پروژه داشتم یه سیستم مدیریت کاربر می‌ساختم و از دستورهای Artisan برای اینجور کارها خیلی استفاده کردم.

نمایش نقش کاربر در ویو

بیاید تو ویوی پروفایل (resources/views/profile.blade.php) نقش کاربر رو نشون بدیم:

@extends('layouts.app')

@section('title', 'پروفایل کاربر')

@section('content')
    <h1>پروفایل کاربر</h1>
    <p>نام کامل: {{ auth()->user()->full_name }}</p>
    <p>ایمیل: {{ auth()->user()->email }}</p>
    <p>نقش‌ها: {{ auth()->user()->roles->pluck('name')->join(', ') }}</p>
    <a href="{{ route('logout') }}">خروج</a>
@endsection

اینجا pluck(‘name’) نام نقش‌ها رو استخراج می‌کنه و با join اونا رو با کاما جدا می‌کنه.

نکات تکمیلی

  • Middlewareهای سفارشی: می‌تونید Middlewareهای دیگه برای نقش‌های خاص (مثل editor) بسازید. من تو یه پروژه برای نقش‌های مختلف Middleware جداگونه ساختم و کارم خیلی راحت شد.

  • اشکال‌زدایی: اگه Middleware کار نکرد، از dd(auth()->user()) استفاده کنید تا مطمئن شید کاربر لاگین‌شده و نقش درست داره.

  • لاراول ۱۲ و بهبودها: تو نسخه ۱۲، Middlewareها برای مدیریت درخواست‌های پیچیده بهینه‌تر شدن و ابزارهای دیباگ بهتری دارن.

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

تا قسمت بعدی، موفق باشید!