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

روابط در Eloquent: تجربه شخصی من

اولین باری که با روابط Eloquent کار کردم، داشتم یه سیستم وبلاگ می‌ساختم و می‌خواستم کامنت‌ها رو به پست‌ها وصل کنم. فکر می‌کردم باید کلی کوئری پیچیده بنویسم، اما Eloquent با سینتکس ساده و شیءگراش این کار رو برام مثل آب خوردن کرد. یه بار تو یه پروژه به اشتباه رابطه رو برعکس تعریف کردم و کلی گیج شدم که چرا داده‌ها درست لود نمی‌شن! تو لاراول ۱۲، روابط حتی بهینه‌تر شدن و ابزارهای جدید برای مدیریت کوئری‌های پیچیده اضافه شده. حالا بیاید با یه مثال عملی وارد بشیم!

پیش‌نیازها

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

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

  • دیتابیس‌تون تو فایل .env تنظیم شده.

  • مدل Post و جدول posts رو از قسمت سوم دارید.

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

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

ساخت مدل و Migration برای کامنت‌ها

بیاید یه مدل و Migration برای کامنت‌ها بسازیم:

php artisan make:model Comment -m

این دستور مدل Comment رو تو app/Models/Comment.php و یه فایل Migration تو database/migrations می‌سازه. فایل Migration رو باز کنید (مثلاً database/migrations/xxxx_create_comments_table.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('comments', function (Blueprint $table) {
            $table->id();
            $table->foreignId('post_id')->constrained()->onDelete('cascade');
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->text('content');
            $table->timestamps();
        });
    }

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

اینجا:

  • foreignId(‘post_id’) یه کلید خارجی برای اتصال کامنت به پست تعریف می‌کنه.

  • foreignId(‘user_id’) کامنت رو به کاربر وصل می‌کنه.

  • onDelete(‘cascade’) یعنی اگه پست یا کاربر حذف بشه، کامنت‌ها هم به صورت خودکار حذف می‌شن.

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

php artisan migrate

یه تجربه از خودم: یه بار فراموش کردم کلیدهای خارجی رو درست تنظیم کنم و وقتی یه پست رو حذف کردم، کامنت‌هاش تو دیتابیس موندن و کلی مشکل درست شد. همیشه از cascade برای روابط حساس استفاده کنید!

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

حالا بیاید روابط رو تو مدل‌ها تعریف کنیم.

رابطه یک به چند (One-to-Many) بین Post و Comment

یه پست می‌تونه چند کامنت داشته باشه. تو فایل app/Models/Post.php این رابطه رو اضافه کنید:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = ['title', 'content'];

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

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

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    protected $fillable = ['post_id', 'user_id', 'content'];

    public function post()
    {
        return $this->belongsTo(Post::class);
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

اینجا:

  • hasMany یعنی هر پست می‌تونه چند کامنت داشته باشه.

  • belongsTo یعنی هر کامنت به یه پست و یه کاربر تعلق داره.

نمایش پست‌ها و کامنت‌ها

بیاید یه ویو بسازیم که پست و کامنت‌هاش رو نشون بده. فایل resources/views/posts/show.blade.php رو اینجوری بسازید:

@extends('layouts.app')

@section('title', 'نمایش پست')

@section('content')
    <h1>{{ $post->title }}</h1>
    <p>{{ $post->content }}</p>
    <h2>کامنت‌ها</h2>
    <ul>
        @foreach ($post->comments as $comment)
            <li>
                <strong>{{ $comment->user->full_name }}</strong>: {{ $comment->content }}
            </li>
        @endforeach
    </ul>
@endsection

و تو PostController متد show رو اینجوری اصلاح کنید:

public function show(Post $post)
{
    return view('posts.show', compact('post'));
}

اینجا به لطف Eloquent، با $post->comments می‌تونید همه کامنت‌های پست رو لود کنید. من تو یه پروژه داشتم یه سیستم وبلاگ می‌ساختم و این روش برای نمایش کامنت‌ها خیلی تمیز و سریع بود.

افزودن کامنت

بیاید یه فرم برای افزودن کامنت به پست اضافه کنیم. تو فایل show.blade.php این فرم رو اضافه کنید:

<h3>افزودن کامنت</h3>
@if (session('success'))
    <x-alert>{{ session('success') }}</x-alert>
@endif
<form method="POST" action="{{ route('comments.store', $post) }}">
    @csrf
    <div>
        <label for="content">کامنت:</label>
        <textarea name="content" id="content" required></textarea>
        @error('content')
            <p style="color: red;">{{ $message }}</p>
        @enderror
    </div>
    <button type="submit">ارسال کامنت</button>
</form>

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

php artisan make:controller CommentController

تو فایل app/Http/Controllers/CommentController.php متد store رو اینجوری بنویسید:

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use App\Models\Comment;
use Illuminate\Http\Request;

class CommentController extends Controller
{
    public function store(Request $request, Post $post)
    {
        $request->validate([
            'content' => 'required|string',
        ]);

        $post->comments()->create([
            'user_id' => auth()->id(),
            'content' => $request->content,
        ]);

        return redirect()->route('posts.show', $post)->with('success', 'کامنت با موفقیت اضافه شد!');
    }
}

و تو routes/web.php مسیر رو اضافه کنید:

use App\Http\Controllers\CommentController;

Route::post('/posts/{post}/comments', [CommentController::class, 'store'])->middleware('auth')->name('comments.store');

اینجا فقط کاربرهای لاگین‌شده می‌تونن کامنت بذارن. یه تجربه از خودم: یه بار فراموش کردم middleware auth رو بذارم و کاربرهای ناشناس کامنت اسپم گذاشتن. همیشه مسیرهای حساس رو با middleware محافظت کنید!

روابط پیشرفته‌تر: Eager Loading

اگه بخواید تعداد زیادی پست و کامنت لود کنید، ممکنه با مشکل N+1 مواجه بشید (جایی که برای هر پست، یه کوئری جدا برای کامنت‌ها اجرا می‌شه). برای حل این مشکل، از Eager Loading استفاده کنید. مثلاً تو PostController متد index:

public function index()
{
    $posts = Post::with('comments')->get();
    return view('posts.index', compact('posts'));
}

اینجا with(‘comments’) همه کامنت‌ها رو یه جا لود می‌کنه و عملکرد رو بهتر می‌کنه. من تو یه پروژه داشتم یه داشبورد سنگین می‌ساختم و Eager Loading کلی سرعت سایتم رو بالا برد.

نکات تکمیلی

  • انواع روابط: Eloquent روابط دیگه‌ای مثل hasOne، manyToMany و polymorphic هم داره. مثلاً می‌تونید یه رابطه چند به چند بین کاربرها و نقش‌ها تعریف کنید که تو قسمت‌های بعدی بهش می‌رسیم.

  • اشکال‌زدایی: اگه داده‌های رابطه لود نشدن، از dd($post->comments) استفاده کنید. من خودم بارها با این روش مشکلاتم رو پیدا کردم.

  • لاراول ۱۲ و بهبودها: تو نسخه ۱۲، Eloquent برای روابط پیچیده بهینه‌تر شده و ابزارهای جدید برای دیباگ کوئری‌ها اضافه شده.

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

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