SEPEHR.SYS

Starting SEPEHR.SYS…

Loading

سپهر محسنی

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

NOTES.TXT ×

تسلط بر لاراول مدرن: قابلیت‌های پیشرفته، معماری تمیز، تست و کارایی خیره‌کننده در Laravel 12+

4′ · ۶ اسفند ۱۴۰۴

لاراول در سال‌های اخیر تحول چشمگیری داشته است. لاراول ۱۱ و ۱۲ اسکلتی سبک‌تر، bootstrapping صریح، بهبودهای قدرتمند در کوئری‌ها، starter kitهای مدرن داخلی و ابزارهای پرکارایی first-party ارائه می‌کنند که به شما امکان می‌دهند اپلیکیشن‌هایی تمیزتر، قابل‌نگهداری‌تر و فوق‌العاده سریع بنویسید. این راهنما سراغ تکنیک‌های بسیار پیشرفته، مدرن و اغلب کمترشناخته‌شده‌ای می‌رود که هر توسعه‌دهندهٔ جدی لاراول باید در سال ۲۰۲۶ به آن‌ها مسلط باشد.

قابلیت‌هایی از Laravel 12+ که همین حالا باید استفاده کنید

nestedWhere() – کوئری‌های پیچیدهٔ تمیز بدون Closure (Laravel 12)

دیگر خبری از closureهای تودرتوی زشت برای شرط‌های ترکیبی AND/OR نیست.

<?php
// Laravel 12+
$products = DB::table('products')
    ->where('status', 'active')
    ->nestedWhere('price', '<', 1000, 'or', 'discount', '>', 30)
    ->where('stock', '>', 0)
    ->get();

// Equivalent to the old messy version:
$products = DB::table('products')
    ->where('status', 'active')
    ->where(function ($query) {
        $query->where('price', '<', 1000)
              ->orWhere('discount', '>', 30);
    })
    ->where('stock', '>', 0)
    ->get();

<Callout type="info"> nestedWhere() خوانایی منطق فیلترکردن در سناریوهای واقعی (بازهٔ قیمت + تخفیف + موجودی) را به‌شکل چشمگیری بهتر می‌کند. هر چند تا که لازم دارید پشت هم زنجیره کنید. </Callout>

کلیدهای اصلی UUID / ULID (داخلی از Laravel 9، تکمیل‌شده در Laravel 11+)

در سیستم‌های توزیع‌شده استفاده از IDهای auto-increment را کنار بگذارید.

<?php
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Concerns\HasUlids;

class Order extends Model
{
    use HasUuids; // or HasUlids for lexicographically sortable

    // Optional: ordered UUIDs
    public function newUniqueId(): string
    {
        return (string) Str::orderedUuid();
    }
}

<Note title="Pro Tip"> ULIDها برای دیتابیس‌های shardشده و مرتب‌سازی مبتنی بر زمان عالی‌اند، بدون اینکه IDهای ترتیبی را افشا کنند. </Note>

مدل‌های Prunable و MassPrunable – پاک‌سازی خودکار دیتابیس

<?php
use Illuminate\Database\Eloquent\Prunable;
use Illuminate\Database\Eloquent\MassPrunable;
use Illuminate\Database\Eloquent\Builder;

class TemporaryUpload extends Model
{
    use MassPrunable; // faster, no events fired

    public function prunable(): Builder
    {
        return static::where('created_at', '<=', now()->subDays(7));
    }
}

زمان‌بندی‌اش کنید:

// app/Console/Kernel.php or routes/console.php
Schedule::command('model:prune')->daily();

اسکوپ‌های محلی مبتنی بر Attribute ‏(Laravel 10.20+ / 11+)

<?php
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;

class Post extends Model
{
    #[Scope]
    public function published(Builder $query): void
    {
        $query->where('published_at', '<=', now())
              ->where('is_draft', false);
    }
}

// Usage
Post::published()->get();

تکثیر مدل (جواهری کمترشناخته‌شده)

$order = Order::find(1);
$copy = $order->replicate(['total', 'paid_at']); // exclude sensitive fields
$copy->save();

الگوهای معماری مدرن در Laravel 12+

Repository + Service Layer (همچنان پادشاهِ ۲۰۲۶)

<?php
// app/Repositories/OrderRepository.php
interface OrderRepositoryInterface
{
    public function createWithItems(CreateOrderDTO $dto): Order;
}

// app/Services/OrderService.php
class OrderService
{
    public function __construct(
        private readonly OrderRepositoryInterface $repository,
        private readonly InventoryService $inventory,
    ) {}

    public function create(CreateOrderDTO $dto): Order
    {
        return DB::transaction(fn () => $this->repository->createWithItems($dto));
    }
}

آبجکت‌های انتقال داده (DTO) با Constructor Property Promotion + Readonly

<?php
namespace App\Data;

readonly class CreateOrderDTO
{
    public function __construct(
        public readonly int $userId,
        public readonly Collection $items, // collection of OrderItemDTO
        public readonly ?string $notes = null,
    ) {}

    public static function fromRequest(Request $request): self
    {
        return new self(
            userId: $request->user()->id,
            items: OrderItemDTO::collect($request->validated('items')),
            notes: $request->validated('notes'),
        );
    }
}

<Callout type="info"> برای قدرت باز هم بیشتر، آن را با پکیج laravel-data از Spatie ترکیب کنید (اعتبارسنجی، casting، صفحه‌بندی) – استاندارد de-facto در سال ۲۰۲۶. </Callout>

بهترین شیوه‌های تست – Pest در Laravel 11+ پیش‌فرض است

تست‌های واحد با Pest (تمیز و گویا)

<?php
// tests/Unit/OrderServiceTest.php
it('creates order and decrements stock', function () {
    $user = User::factory()->create();
    $product = Product::factory()->create(['stock' => 10]);

    $dto = new CreateOrderDTO(
        userId: $user->id,
        items: collect([new OrderItemDTO($product->id, 2)])
    );

    $order = app(OrderService::class)->create($dto);

    expect($order)
        ->toBeInstanceOf(Order::class)
        ->and($product->fresh()->stock)->toBe(8);
});

it('throws when stock insufficient')->throws(InsufficientStockException::class, function () {
    // ...
});

تست‌های Feature با RefreshDatabase

<?php
// tests/Feature/OrdersApiTest.php
it('allows authenticated user to create order', function () {
    $user = User::factory()->create();
    $product = Product::factory()->create(['stock' => 5]);

    $response = $this->actingAs($user)
        ->postJson('/api/orders', [
            'items' => [['product_id' => $product->id, 'quantity' => 2]],
        ]);

    $response->assertCreated();
    expect($product->fresh()->stock)->toBe(3);
});

بهینه‌سازی کارایی – Octane همه‌چیز را عوض می‌کند

Laravel Octane ‏(Swoole / RoadRunner / FrankenPHP)

composer require laravel/octane
php artisan octane:install
php artisan octane:start --server=frankenphp --watch

تسک‌های همزمان (موازی‌سازی واقعی)

use Laravel\Octane\Facades\Octane;

[$users, $products, $orders] = Octane::concurrently([
    fn () => User::with('orders')->get(),
    fn () => Product::where('featured', true)->get(),
    fn () => Order::recent()->get(),
]);

Cache و Tableهای Octane (درون‌حافظه‌ای، مشترک بین Workerها)

// config/octane.php
'tables' => [
    'leaderboard:5000' => ['user_id' => 'int', 'score' => 'int'],
],

// Usage
Octane::table('leaderboard')->set('user:123', ['user_id' => 123, 'score' => 9999]);

// Interval cache (auto-refreshes)
Cache::store('octane')->interval('daily-stats', fn () => calculateStats(), seconds: 86400);

Tickها برای کارهای پس‌زمینه

Octane::tick('cleanup', fn () => TemporaryUpload::prune())
    ->seconds(60)
    ->immediate();

<Callout type="info"> Octane + FrankenPHP استاندارد طلایی سال ۲۰۲۶ است – HTTP/3، HTTPS بدون نیاز به پیکربندی، و توان عملیاتی ۱۰ تا ۱۰۰ برابر در مقایسه با PHP-FPM سنتی. </Callout>

جمع‌بندی

لاراول مدرن (نسخه‌های ۱۰ تا ۱۲) دیگر «فقط یک فریم‌ورک MVC» نیست؛ یک قدرت تمام‌عیار فول‌استک است با bootstrapping صریح، قابلیت real-time رسمی (Reverb)، مسیریابی مبتنی بر صفحه (Folio)، کامپوننت‌های تابعی (Volt) و کارایی در سطح production ‏(Octane).

نکات کلیدی:

  • از nestedWhere()، UUID/ULID، مدل‌های Prunable و اسکوپ‌های مبتنی بر attribute استفاده کنید
  • برای کدی قابل‌نگهداری، الگوی DTO + Service Layer + Repository را بپذیرید
  • تست‌ها را با Pest بنویسید – هم سریع‌تر است و هم لذت‌بخش‌تر
  • برای production با Octane دیپلوی کنید (FrankenPHP پیشنهاد می‌شود)
  • برای توسعهٔ سریع فول‌استک از starter kitهای جدید همراه با Volt + Flux + shadcn/ui بهره بگیرید

از همین امروز ارتقا را شروع کنید. Laravel 12+ توسعه‌دهنده‌پسندترین نسخهٔ تا به امروز است – و دستاورد کارایی با Octane باعث می‌شود از خودتان بپرسید چطور تا حالا بدون آن سر می‌کردید.

این یادداشت ترجمهٔ فارسی نوشتهٔ خودم است — نسخهٔ اصلی (انگلیسی) در dev.to