mcpField
L
laravel-best-practices

laravel-best-practices

github.com/asyrafhussin開発952.3k 回インストール9 週間前に更新
リポジトリ

laravel-best-practices について

Laravel 13 アプリケーションを開発・保守する人向けのプラクティス集です。アーキテクチャ、Eloquent、コントローラー、バリデーション、セキュリティのルールを例とともに紹介します。

主な機能

  1. 01アーキテクチャと構成の指針
  2. 02Eloquent とデータベースの実践
  3. 03コントローラーとルーティングのパターン
  4. 04バリデーションとリクエストのルール
  5. 05一括代入のセキュリティ指針

活用シーン

  1. 01Laravel アプリの構成設計
  2. 02N+1 データベースクエリの削減
  3. 03フォームリクエストのバリデーション実装
  4. 04モデルの一括代入リスクへの対処

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/asyrafhussin/agent-skills --skill laravel-best-practices

インストール後、Claude Code で/laravel-best-practicesを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

AGENTS.md
metadata.json
README.md
rules/
_sections.md
_template.md
arch-action-classes.md
arch-dto-pattern.md
arch-event-driven.md
arch-feature-folders.md
arch-queue-routing.md
arch-repository-pattern.md
arch-service-classes.md
arch-value-objects.md
controller-api-resources.md
controller-dependency-injection.md
controller-form-requests.md
controller-middleware.md
controller-resource-controllers.md
controller-resource-methods.md
controller-single-action.md
eloquent-accessors-mutators.md
eloquent-casts.md
eloquent-chunking.md
eloquent-eager-loading.md
eloquent-model-events.md
eloquent-pruning.md
eloquent-query-scopes.md
eloquent-relationships.md
eloquent-soft-deletes.md
eloquent-vector-search.md

詳細

Laravel 13 のベストプラクティス

Laravel 13 アプリケーション向けの包括的なベストプラクティスガイドです。スケーラブルで保守しやすい Laravel アプリケーションを構築するための、7 つのカテゴリにわたる 31 のルールを収録しています。

適用する場面

以下を行う際に、これらのガイドラインを参照してください。

  • コントローラー、モデル、サービスの作成
  • マイグレーションとデータベースクエリの作成
  • バリデーションとフォームリクエストの実装
  • Laravel を使った API の構築
  • Laravel アプリケーションの構成

優先度別のルールカテゴリ

優先度カテゴリ影響度プレフィックス
1アーキテクチャと構造CRITICALarch-
2Eloquent とデータベースCRITICALeloquent-
3コントローラーとルーティングHIGHcontroller-, ctrl-
4バリデーションとリクエストHIGHvalidation-, valid-
5セキュリティHIGHsec-
6パフォーマンスMEDIUMperf-
7API 設計MEDIUMapi-

クイックリファレンス

1. アーキテクチャと構造(CRITICAL)
  • arch-service-classes - ビジネスロジックをサービスに切り出す
  • arch-action-classes - 単一の目的を持つアクションクラス
  • arch-repository-pattern - リポジトリを使うべき場面
  • arch-dto-pattern - データ転送オブジェクト
  • arch-value-objects - ドメイン概念をカプセル化する
  • arch-event-driven - イベントとリスナーで疎結合にする
  • arch-feature-folders - ドメイン/機能ごとに整理する
  • arch-queue-routing - ジョブキューのルーティングを一元化する(Laravel 13 以降)
2. Eloquent とデータベース(CRITICAL)
  • eloquent-eager-loading - N+1 クエリを防ぐ
  • eloquent-chunking - 大規模なデータセットを処理する
  • eloquent-query-scopes - 再利用可能なクエリロジック
  • eloquent-model-events - 副作用にはオブザーバーを使う
  • eloquent-relationships - リレーションシップを適切に定義する
  • eloquent-casts - 属性を自動的にキャストする
  • eloquent-accessors-mutators - 属性を変換する
  • eloquent-soft-deletes - 復元可能な安全な削除
  • eloquent-pruning - 古いレコードを自動的にクリーンアップする
  • eloquent-vector-search - pgvector によるセマンティック検索(Laravel 13 以降)
3. コントローラーとルーティング(HIGH)
  • controller-resource-controllers - リソースコントローラーを使う
  • controller-single-action - 単一アクションの呼び出し可能なコントローラー
  • controller-resource-methods - RESTful なリソースメソッド
  • controller-form-requests - フォームリクエストを使う
  • controller-api-resources - API レスポンスを変換する
  • controller-middleware - ミドルウェアを適切に適用する
  • controller-dependency-injection - 依存関係を注入する
4. バリデーションとリクエスト(HIGH)
  • validation-form-requests - フォームリクエストクラスを使う
  • validation-custom-rules - カスタムルールを作成する
  • validation-conditional-rules - 条件付きバリデーション
  • validation-array-validation - ネストされた配列を検証する
  • validation-after-hooks - 複雑なバリデーションロジック
5. セキュリティ(HIGH)
  • sec-mass-assignment - 一括代入から保護する
6. パフォーマンス(MEDIUM)

このカテゴリには、まだルールファイルがありません。

7. API 設計(MEDIUM)

このカテゴリには、まだルールファイルがありません。

重要なパターン

フォームリクエストを使用するコントローラー
<?php

namespace App\Http\Controllers;

use App\Http\Requests\StorePostRequest;
use App\Http\Requests\UpdatePostRequest;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;

class PostController extends Controller
{
    public function store(StorePostRequest $request): RedirectResponse
    {
        // Validation happens automatically
        $validated = $request->validated();

        $post = Post::create($validated);

        return redirect()
            ->route('posts.show', $post)
            ->with('success', 'Post created successfully.');
    }

    public function update(UpdatePostRequest $request, Post $post): RedirectResponse
    {
        $post->update($request->validated());

        return redirect()
            ->route('posts.show', $post)
            ->with('success', 'Post updated successfully.');
    }
}
フォームリクエストクラス
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user()->can('create', Post::class);
    }

    public function rules(): array
    {
        return [
            'title' => ['required', 'string', 'max:255'],
            'body' => ['required', 'string', 'min:100'],
            'category_id' => ['required', 'exists:categories,id'],
            'tags' => ['nullable', 'array'],
            'tags.*' => ['exists:tags,id'],
            'published_at' => ['nullable', 'date', 'after:now'],
        ];
    }

    public function messages(): array
    {
        return [
            'body.min' => 'The post body must be at least 100 characters.',
        ];
    }
}
サービスクラスのパターン
<?php

namespace App\Services;

use App\Models\User;
use App\Models\Post;
use App\Events\PostPublished;
use Illuminate\Support\Facades\DB;

class PostService
{
    public function __construct(
        private readonly NotificationService $notifications,
    ) {}

    public function publish(Post $post): Post
    {
        return DB::transaction(function () use ($post) {
            $post->update([
                'published_at' => now(),
                'status' => 'published',
            ]);

            event(new PostPublished($post));

            $this->notifications->notifyFollowers($post->author, $post);

            return $post->fresh();
        });
    }
}
Eloquent モデル
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Builder;

class Post extends Model
{
    use HasFactory;

    protected $fillable = [
        'title',
        'slug',
        'body',
        'category_id',
        'published_at',
    ];

    protected $casts = [
        'published_at' => 'datetime',
    ];

    // Relationships
    public function author(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    public function category(): BelongsTo
    {
        return $this->belongsTo(Category::class);
    }

    public function tags(): BelongsToMany
    {
        return $this->belongsToMany(Tag::class)->withTimestamps();
    }

    // Scopes
    public function scopePublished(Builder $query): Builder
    {
        return $query->whereNotNull('published_at')
            ->where('published_at', '<=', now());
    }

    public function scopeByCategory(Builder $query, int $categoryId): Builder
    {
        return $query->where('category_id', $categoryId);
    }

    // Accessors & Mutators
    protected function title(): Attribute
    {
        return Attribute::make(
            set: fn (string $value) => ucfirst($value),
        );
    }
}
マイグレーションのベストプラクティス
<?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('posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
            $table->foreignId('category_id')->constrained()->cascadeOnDelete();
            $table->string('title');
            $table->string('slug')->unique();
            $table->text('body');
            $table->timestamp('published_at')->nullable();
            $table->timestamps();

            // Indexes for common queries
            $table->index(['user_id', 'published_at']);
            $table->index('category_id');
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};
イーガーローディング
// N+1 Problem
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->author->name;  // Query per post
}

// Eager loading — only 3 queries total
$posts = Post::with(['author', 'category', 'tags'])->get();
foreach ($posts as $post) {
    echo $post->author->name;  // No additional queries
}

// Nested eager loading
$posts = Post::with([
    'author.profile',
    'comments.user',
    'tags',
])->get();

// Constrained eager loading
$posts = Post::with([
    'comments' => fn ($query) => $query->latest()->limit(5),
])->get();

使用方法

詳しい説明とコード例については、個々のルールファイルをお読みください。

rules/arch-service-classes.md
rules/eloquent-eager-loading.md
rules/validation-form-requests.md
rules/_sections.md

各ルールファイルには、以下が含まれています。

  • メタデータ(タイトル、影響度、タグ)を含む YAML フロントマター
  • 重要である理由の簡単な説明
  • 解説付きの悪い例
  • 解説付きの良い例
  • Laravel 13 と PHP 8.3 に固有の背景情報および参考資料

コンパイル済みドキュメント全文

すべてのルールを展開した完全なガイドについては、AGENTS.md をご覧ください。

コメント