Eloquent ORM: รูปแบบและการเพิ่มประสิทธิภาพสำหรับ Laravel

เชี่ยวชาญ Eloquent ORM ด้วยรูปแบบขั้นสูงและเทคนิคการเพิ่มประสิทธิภาพ ทั้ง eager loading, query scope, accessor, mutator และประสิทธิภาพสำหรับแอป Laravel

รูปแบบและการเพิ่มประสิทธิภาพ Eloquent ORM สำหรับ Laravel

Eloquent ORM แปลงการโต้ตอบกับฐานข้อมูลให้กลายเป็นการดำเนินการที่ลื่นไหลและเข้าใจง่าย นอกเหนือจากไวยากรณ์ที่สวยงามแล้ว การเชี่ยวชาญรูปแบบขั้นสูงและเทคนิคการเพิ่มประสิทธิภาพยังเป็นตัวกำหนดประสิทธิภาพของแอปพลิเคชัน Laravel ในระบบจริง

ประสิทธิภาพต้องมาก่อน

ปัญหา N+1 เป็นสาเหตุหลักของความช้าในแอปพลิเคชัน Eloquent ทุกความสัมพันธ์ที่ไม่ได้รับการเพิ่มประสิทธิภาพจะสร้างคำสั่ง SQL เพิ่มขึ้นหนึ่งคำสั่งต่อหนึ่งระเบียน

แก้ไขปัญหา N+1 ด้วย eager loading

ปัญหา N+1 เกิดขึ้นเมื่อทุกการวนลูปบนคอลเลกชันกระตุ้นให้เกิดคำสั่งเพิ่มเติมเพื่อโหลดความสัมพันธ์ ด้วยบทความ 100 บทความและผู้เขียน นั่นหมายถึง 101 คำสั่งแทนที่จะเป็นคำสั่งเดียวที่ผ่านการเพิ่มประสิทธิภาพแล้ว

Eager loading ดึงความสัมพันธ์ทั้งหมดในคำสั่งสูงสุดหนึ่งหรือสองคำสั่ง ลดเวลาตอบสนองอย่างมาก

app/Http/Controllers/ArticleController.phpphp
// Demonstration of N+1 problem and its solution

namespace App\Http\Controllers;

use App\Models\Article;
use Illuminate\Http\Request;

class ArticleController extends Controller
{
    // ❌ N+1 problem: 1 articles query + N author queries
    public function indexWithProblem()
    {
        $articles = Article::all(); // 1 query

        foreach ($articles as $article) {
            echo $article->author->name; // N additional queries
        }
    }

    // ✅ Eager loading: 2 queries maximum
    public function indexOptimized()
    {
        $articles = Article::with('author')->get(); // 2 queries total

        foreach ($articles as $article) {
            echo $article->author->name; // No additional queries
        }
    }

    // ✅ Nested eager loading for multiple relationships
    public function indexWithNestedRelations()
    {
        // Loads articles → authors → profiles + articles → comments → users
        $articles = Article::with([
            'author.profile',
            'comments.user'
        ])->get();

        return view('articles.index', compact('articles'));
    }
}

Eager loading ด้วย with() คาดการณ์ความต้องการและโหลดข้อมูลล่วงหน้า ความแตกต่างของประสิทธิภาพจะน่าทึ่งมากบนคอลเลกชันขนาดใหญ่

Eager loading แบบมีเงื่อนไขและถูกจำกัด

ความสัมพันธ์ขนาดใหญ่บางครั้งต้องการการโหลดเพียงบางส่วน การจำกัด eager loading จะลดข้อมูลที่ดึงมาในขณะที่ยังคงหลีกเลี่ยง N+1

app/Http/Controllers/UserController.phpphp
// Eager loading with constraints to optimize queries

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Database\Eloquent\Builder;

class UserController extends Controller
{
    public function showWithRecentOrders(int $id)
    {
        // Load only the 5 most recent paid orders
        $user = User::with(['orders' => function (Builder $query) {
            $query->where('status', 'paid')
                  ->orderByDesc('created_at')
                  ->limit(5);
        }])->findOrFail($id);

        return view('users.show', compact('user'));
    }

    public function indexActiveWithStats()
    {
        // Conditional eager loading with withCount
        $users = User::query()
            ->where('active', true)
            ->with(['profile', 'subscription'])
            ->withCount(['orders', 'reviews']) // Adds orders_count and reviews_count
            ->withSum('orders', 'total')       // Adds orders_sum_total
            ->get();

        return view('users.index', compact('users'));
    }

    public function showWithConditionalRelation(int $id)
    {
        // Load relationship only if user is premium
        $user = User::findOrFail($id);

        $user->loadMissing(
            $user->isPremium() ? ['premiumFeatures', 'analytics'] : []
        );

        return view('users.show', compact('user'));
    }
}

เมธอด withCount() และ withSum() เพิ่มการรวมค่าโดยไม่ต้องโหลดคอลเลกชันทั้งหมด เหมาะอย่างยิ่งสำหรับสถิติบนแดชบอร์ด

Query scope สำหรับคำสั่งที่นำกลับมาใช้ใหม่ได้

Query scope ห่อหุ้มตรรกะการกรองไว้ภายในโมเดล Scope ระดับท้องถิ่นมอบความยืดหยุ่น ในขณะที่ scope ระดับโลกจะถูกนำไปใช้กับทุกคำสั่งโดยอัตโนมัติ

แนวทางการตั้งชื่อ

Scope ระดับท้องถิ่นใช้คำนำหน้า scope ในโมเดลแต่จะเรียกใช้โดยไม่มีคำนำหน้านั้น: scopeActive() กลายเป็น User::active()

app/Models/Article.phpphp
// Local and global scopes to encapsulate business logic

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Attributes\ScopedBy;

#[ScopedBy([PublishedScope::class])] // Global scope via PHP 8 attribute
class Article extends Model
{
    // Simple local scope: Article::published()
    public function scopePublished(Builder $query): Builder
    {
        return $query->whereNotNull('published_at')
                     ->where('published_at', '<=', now());
    }

    // Local scope with parameter: Article::byCategory('tech')
    public function scopeByCategory(Builder $query, string $category): Builder
    {
        return $query->where('category', $category);
    }

    // Local scope with optional parameter
    public function scopePopular(Builder $query, int $minViews = 1000): Builder
    {
        return $query->where('views_count', '>=', $minViews)
                     ->orderByDesc('views_count');
    }

    // Dynamic scope for flexible search
    public function scopeSearch(Builder $query, ?string $term): Builder
    {
        if (empty($term)) {
            return $query;
        }

        return $query->where(function (Builder $q) use ($term) {
            $q->where('title', 'like', "%{$term}%")
              ->orWhere('content', 'like', "%{$term}%")
              ->orWhereHas('tags', fn($t) => $t->where('name', 'like', "%{$term}%"));
        });
    }
}
app/Models/Scopes/PublishedScope.phpphp
// Reusable global scope across models

namespace App\Models\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class PublishedScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        // Automatically applied to all Article queries
        $builder->whereNotNull('published_at')
                ->where('published_at', '<=', now());
    }
}
php
// Using scopes in a controller
$articles = Article::query()
    ->byCategory('technology')
    ->popular(500)
    ->search($request->input('q'))
    ->with('author')
    ->paginate(20);

// Disable a global scope temporarily
$allArticles = Article::withoutGlobalScope(PublishedScope::class)->get();

การเชื่อมโยง scope ทำให้คำสั่งอ่านง่ายและบำรุงรักษาง่าย โดยรวบรวมตรรกะธุรกิจไว้ในโมเดล

พร้อมที่จะพิชิตการสัมภาษณ์ Laravel แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

Accessor และ mutator ด้วย Attribute

Laravel 9+ นำเสนอไวยากรณ์แบบรวมศูนย์สำหรับ accessor และ mutator ผ่านคลาส Attribute แนวทางสมัยใหม่นี้ทดแทนเมธอด get*Attribute และ set*Attribute

app/Models/User.phpphp
// Modern accessors and mutators with the Attribute class

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

class User extends Model
{
    protected $fillable = ['first_name', 'last_name', 'email', 'password'];

    // Accessor: generates a virtual attribute from other fields
    protected function fullName(): Attribute
    {
        return Attribute::make(
            get: fn () => "{$this->first_name} {$this->last_name}",
        );
    }

    // Mutator: transforms value before saving
    protected function password(): Attribute
    {
        return Attribute::make(
            set: fn (string $value) => Hash::make($value),
        );
    }

    // Combined accessor + mutator
    protected function email(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => Str::lower($value),
            set: fn (string $value) => Str::lower(trim($value)),
        );
    }

    // Cached accessor to avoid recalculations
    protected function initials(): Attribute
    {
        return Attribute::make(
            get: fn () => Str::upper(
                Str::substr($this->first_name, 0, 1) .
                Str::substr($this->last_name, 0, 1)
            ),
        )->shouldCache(); // Caches result during request
    }

    // Computed attribute based on a relationship
    protected function ordersTotal(): Attribute
    {
        return Attribute::make(
            get: fn () => $this->orders->sum('total'),
        );
    }
}
php
// Transparent usage of accessors and mutators
$user = new User();
$user->first_name = 'John';
$user->last_name = 'Doe';
$user->email = '  JOHN@EXAMPLE.COM  '; // Automatically normalized
$user->password = 'secret123';          // Automatically hashed
$user->save();

echo $user->full_name;  // "John Doe"
echo $user->initials;   // "JD"
echo $user->email;      // "john@example.com"

เมธอด shouldCache() เพิ่มประสิทธิภาพให้กับ accessor ที่มีค่าใช้จ่ายสูงโดยหลีกเลี่ยงการคำนวณซ้ำหลายครั้งบนโมเดลเดียวกัน

Cast แบบกำหนดเองสำหรับชนิดข้อมูลที่ซับซ้อน

Cast แปลงค่าระหว่าง PHP และฐานข้อมูลโดยอัตโนมัติ Cast แบบกำหนดเองห่อหุ้มตรรกะการ serialize สำหรับชนิดข้อมูลทางธุรกิจ

app/Casts/MoneyCast.phpphp
// Custom cast for handling monetary amounts

namespace App\Casts;

use App\ValueObjects\Money;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
use InvalidArgumentException;

class MoneyCast implements CastsAttributes
{
    public function __construct(
        protected string $currency = 'USD'
    ) {}

    // DB → PHP conversion: cents to Money object
    public function get(Model $model, string $key, mixed $value, array $attributes): ?Money
    {
        if ($value === null) {
            return null;
        }

        return new Money(
            amount: (int) $value,
            currency: $this->currency
        );
    }

    // PHP → DB conversion: Money object to cents
    public function set(Model $model, string $key, mixed $value, array $attributes): ?int
    {
        if ($value === null) {
            return null;
        }

        if ($value instanceof Money) {
            return $value->getAmountInCents();
        }

        if (is_numeric($value)) {
            return (int) ($value * 100);
        }

        throw new InvalidArgumentException('Value must be Money instance or numeric');
    }
}
app/ValueObjects/Money.phpphp
// Immutable Value Object representing amounts

namespace App\ValueObjects;

use JsonSerializable;

final readonly class Money implements JsonSerializable
{
    public function __construct(
        private int $amount,     // Stored in cents
        private string $currency
    ) {}

    public function getAmountInCents(): int
    {
        return $this->amount;
    }

    public function getAmountInUnits(): float
    {
        return $this->amount / 100;
    }

    public function format(): string
    {
        return number_format($this->getAmountInUnits(), 2) . ' ' . $this->currency;
    }

    public function add(Money $other): self
    {
        return new self($this->amount + $other->amount, $this->currency);
    }

    public function jsonSerialize(): array
    {
        return [
            'amount' => $this->getAmountInUnits(),
            'currency' => $this->currency,
        ];
    }
}
app/Models/Order.phpphp
// Using the custom cast

namespace App\Models;

use App\Casts\MoneyCast;
use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    protected function casts(): array
    {
        return [
            'total' => MoneyCast::class,           // USD by default
            'shipping_cost' => MoneyCast::class . ':USD',
            'tax_amount' => MoneyCast::class . ':USD',
            'paid_at' => 'datetime',
            'metadata' => 'array',
        ];
    }
}
php
// Natural usage with the cast
$order = Order::find(1);
echo $order->total->format();              // "149.99 USD"
echo $order->total->getAmountInCents();    // 14999

$order->total = 199.99;  // Automatically converted
$order->save();          // Stored as 19999 in DB

Value Object ที่ผสมกับ cast แบบกำหนดเองรับประกันความถูกต้องของข้อมูลทางธุรกิจในขณะที่ยังคงรักษา API ที่สวยงามไว้

เพิ่มประสิทธิภาพคำสั่งขนาดใหญ่

การดำเนินการกับข้อมูลหลายล้านระเบียนต้องใช้เทคนิคเฉพาะเพื่อหลีกเลี่ยงการใช้หน่วยความจำจนหมด Chunking และ cursor ประมวลผลข้อมูลเป็นชุด

ระวังเรื่องหน่วยความจำ

Model::all() โหลดระเบียนทั้งหมดเข้าสู่หน่วยความจำ บนตารางที่มี 100,000 แถว สิ่งนี้สามารถใช้ RAM หลายกิกะไบต์และทำให้แอปพลิเคชันล่ม

app/Console/Commands/ProcessUsersCommand.phpphp
// Batch processing techniques for large tables

namespace App\Console\Commands;

use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class ProcessUsersCommand extends Command
{
    protected $signature = 'users:process';

    public function handle(): int
    {
        // ✅ Chunk: processes in batches of 1000, reloads from DB
        User::query()
            ->where('needs_processing', true)
            ->chunk(1000, function ($users) {
                foreach ($users as $user) {
                    $user->processAccount();
                }
            });

        // ✅ Chunk with updates: avoids infinite loop during modifications
        User::query()
            ->where('status', 'pending')
            ->chunkById(1000, function ($users) {
                foreach ($users as $user) {
                    $user->update(['status' => 'processed']);
                }
            });

        // ✅ Lazy collection: single record in memory at a time
        foreach (User::lazy(1000) as $user) {
            $user->sendNewsletter();
        }

        // ✅ Cursor: for read-only operations, minimal memory
        foreach (User::cursor() as $user) {
            $this->info("Processing: {$user->email}");
        }

        // ✅ Mass update without Eloquent: maximum performance
        User::query()
            ->where('last_login_at', '<', now()->subYear())
            ->update(['status' => 'inactive']);

        // ✅ Optimized mass deletion
        User::query()
            ->where('deleted_at', '<', now()->subMonths(6))
            ->forceDelete();

        return self::SUCCESS;
    }
}

การเลือกระหว่าง chunk(), lazy() และ cursor() ขึ้นอยู่กับสถานการณ์การใช้งาน: chunk() สำหรับการแก้ไข, lazy() สำหรับการดำเนินการระหว่างกลาง และ cursor() สำหรับการอ่านอย่างเดียวด้วยหน่วยความจำขั้นต่ำ

ความสัมพันธ์ polymorphic ขั้นสูง

ความสัมพันธ์แบบ polymorphic อนุญาตให้โมเดลหนึ่งสามารถสังกัดโมเดลหลายประเภทผ่านความสัมพันธ์เดียว ความยืดหยุ่นนี้เหมาะสำหรับความคิดเห็น แท็ก หรือไฟล์แนบ

app/Models/Comment.phpphp
// Model with inverse polymorphic relationship

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;

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

    // A comment can belong to Article, Video, or any other model
    public function commentable(): MorphTo
    {
        return $this->morphTo();
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
app/Models/Article.phpphp
// Parent model with polymorphic relationship

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;

class Article extends Model
{
    public function comments(): MorphMany
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}
app/Models/Video.phpphp
// Another parent model using the same relationship

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;

class Video extends Model
{
    public function comments(): MorphMany
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}
php
// Migration for polymorphic comments table
// database/migrations/2026_01_15_create_comments_table.php

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

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->text('body');
            $table->foreignId('user_id')->constrained();
            $table->morphs('commentable'); // Creates commentable_type and commentable_id
            $table->timestamps();

            // Composite index for polymorphic queries
            $table->index(['commentable_type', 'commentable_id']);
        });
    }
};
php
// Using polymorphic relationships
$article = Article::find(1);
$article->comments()->create([
    'body' => 'Excellent article!',
    'user_id' => auth()->id(),
]);

$video = Video::find(1);
$video->comments()->create([
    'body' => 'Very instructive video',
    'user_id' => auth()->id(),
]);

// Retrieve parent from comment
$comment = Comment::with('commentable')->find(1);
echo get_class($comment->commentable); // App\Models\Article or App\Models\Video

ความสัมพันธ์ polymorphic หลีกเลี่ยงการทำซ้ำตารางและรวบรวมตรรกะสำหรับฟังก์ชันที่ใช้ร่วมกัน

พร้อมที่จะพิชิตการสัมภาษณ์ Laravel แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

Trait และ observer สำหรับตรรกะที่นำกลับมาใช้ใหม่ได้

Trait ห่อหุ้มพฤติกรรมที่นำกลับมาใช้ใหม่ได้ระหว่างโมเดล Observer รวมศูนย์ hook บนเหตุการณ์วงจรชีวิต

app/Models/Concerns/HasSlug.phpphp
// Trait for automatic slug generation

namespace App\Models\Concerns;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;

trait HasSlug
{
    public static function bootHasSlug(): void
    {
        static::creating(function (Model $model) {
            if (empty($model->slug)) {
                $model->slug = $model->generateUniqueSlug();
            }
        });
    }

    protected function generateUniqueSlug(): string
    {
        $slug = Str::slug($this->getSlugSource());
        $originalSlug = $slug;
        $counter = 1;

        // Check uniqueness and add suffix if needed
        while (static::where('slug', $slug)->exists()) {
            $slug = "{$originalSlug}-{$counter}";
            $counter++;
        }

        return $slug;
    }

    // Can be overridden in the model
    protected function getSlugSource(): string
    {
        return $this->title ?? $this->name;
    }
}
app/Models/Concerns/HasUuid.phpphp
// Trait for using UUIDs as primary key

namespace App\Models\Concerns;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;

trait HasUuid
{
    public static function bootHasUuid(): void
    {
        static::creating(function (Model $model) {
            if (empty($model->{$model->getKeyName()})) {
                $model->{$model->getKeyName()} = (string) Str::uuid();
            }
        });
    }

    public function getIncrementing(): bool
    {
        return false;
    }

    public function getKeyType(): string
    {
        return 'string';
    }
}
app/Observers/ArticleObserver.phpphp
// Observer to centralize hooks on Article model

namespace App\Observers;

use App\Models\Article;
use App\Jobs\NotifySubscribersJob;
use App\Services\SearchIndexService;
use Illuminate\Support\Facades\Cache;

class ArticleObserver
{
    public function __construct(
        private SearchIndexService $searchIndex
    ) {}

    public function created(Article $article): void
    {
        // Invalidate recent articles cache
        Cache::tags(['articles', 'recent'])->flush();

        // Index for search
        $this->searchIndex->index($article);
    }

    public function updated(Article $article): void
    {
        // Update search index
        $this->searchIndex->update($article);

        // Notify subscribers if article was just published
        if ($article->wasChanged('published_at') && $article->published_at !== null) {
            NotifySubscribersJob::dispatch($article);
        }

        Cache::tags(['articles'])->flush();
    }

    public function deleted(Article $article): void
    {
        $this->searchIndex->remove($article);
        Cache::tags(['articles'])->flush();
    }

    // Prevent deletion if article has comments
    public function deleting(Article $article): bool
    {
        if ($article->comments()->exists()) {
            return false; // Cancel deletion
        }
        return true;
    }
}
app/Models/Article.phpphp
// Model using traits and observer

namespace App\Models;

use App\Models\Concerns\HasSlug;
use App\Models\Concerns\HasUuid;
use App\Observers\ArticleObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Model;

#[ObservedBy(ArticleObserver::class)]
class Article extends Model
{
    use HasSlug, HasUuid;

    protected $fillable = ['title', 'content', 'published_at'];
}

เมธอด boot* ของ trait จะถูกดำเนินการโดยอัตโนมัติระหว่างการเริ่มต้นโมเดล ทำให้การผสานรวมเป็นไปอย่างโปร่งใส

บทสรุป

การเชี่ยวชาญ Eloquent ORM ขึ้นอยู่กับการเข้าใจกลไกพื้นฐานและการประยุกต์ใช้รูปแบบที่เหมาะสม เทคนิคที่นำเสนอเปลี่ยนคำสั่งที่ไร้เดียงสาให้กลายเป็นโค้ดที่มีประสิทธิภาพและบำรุงรักษาได้

เช็กลิสต์การเพิ่มประสิทธิภาพ Eloquent:

✅ ใช้ with() อย่างเป็นระบบสำหรับความสัมพันธ์ที่แสดง ✅ ใช้ withCount() แทนการโหลดคอลเลกชันเพื่อนับจำนวน ✅ ห่อหุ้มตรรกะการกรองใน query scope ✅ เลือกใช้ accessor มากกว่าการคำนวณซ้ำใน view ✅ ใช้งาน cast แบบกำหนดเองสำหรับ Value Object ทางธุรกิจ ✅ ใช้ chunk() หรือ lazy() สำหรับการดำเนินการบนตารางขนาดใหญ่ ✅ รวมศูนย์ผลข้างเคียงไว้ใน observer ✅ แยกพฤติกรรมร่วมออกเป็น trait

เครื่องมือ php artisan telescope หรือ laravel-debugbar ช่วยให้สามารถมองเห็นคำสั่ง SQL ที่ถูกสร้างขึ้นและระบุการเพิ่มประสิทธิภาพที่ยังขาดอยู่

เริ่มฝึกซ้อมเลย!

ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ

แท็ก

#laravel
#eloquent
#php
#orm
#database

แชร์

บทความที่เกี่ยวข้อง

คำถามสัมภาษณ์งาน Laravel และ PHP - คู่มือฉบับสมบูรณ์

25 คำถามสัมภาษณ์งาน Laravel และ PHP ยอดนิยมในปี 2026

รวม 25 คำถามสัมภาษณ์งาน Laravel และ PHP ที่พบบ่อยที่สุด ครอบคลุม Service Container, Eloquent ORM, middleware, queues และการ deploy ระบบ production พร้อมคำตอบและตัวอย่างโค้ดครบถ้วน

บทเรียน Laravel 11 สร้างแอปพลิเคชันสมบูรณ์

Laravel 11: สร้างแอปพลิเคชันสมบูรณ์ตั้งแต่เริ่มต้น

คู่มือครบวงจรสำหรับสร้างแอปพลิเคชัน Laravel 11 พร้อมระบบยืนยันตัวตน, REST API, Eloquent ORM และการ Deploy บทเรียนเชิงปฏิบัติสำหรับนักพัฒนาระดับเริ่มต้นถึงกลาง

คู่มือเจาะลึก Laravel Middleware ครอบคลุม Authentication, Rate Limiting และ Custom Middleware

Laravel Middleware เจาะลึก: Authentication, Rate Limiting และ Custom Middleware ฉบับสมบูรณ์

คู่มือเจาะลึก Laravel Middleware ครอบคลุมการทำงานของ Authentication Middleware, Rate Limiting ด้วย Throttle, การสร้าง Custom Middleware และการลงทะเบียน Middleware ใน Laravel 12 พร้อมตัวอย่างโค้ดที่ใช้งานได้จริงในโปรดักชัน