laravel-specialist について
Laravelアプリケーションの設計、構築、保守を支援するスキルです。アーキテクチャ、実装、テスト、最適化を扱い、Eloquent、Blade、Livewire、キュー、イベント、Pestの実践方法をまとめています。
主な機能
- 01Laravelプロジェクトと構成のレビュー
- 02Eloquentモデルとリレーション
- 03BladeとLivewireのパターン
- 04キュー、ジョブ、イベント、ミドルウェア
- 05各層のPestテスト
- 06マイグレーション、シーダー、Artisan
活用シーン
- 01新規Laravelアプリの設計
- 02既存プロジェクトの構成レビュー
- 03データモデルと非同期ジョブの実装
- 04ルート、DB動作、ユーザーフローのテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ pixel-process-ug/ superkit-agents --skill laravel-specialistインストール後、Claude Code で/laravel-specialistを入力して読み込まれたか確認します。
詳細
Laravel スペシャリスト
概要
フレームワークの規約とベストプラクティスに従って、本番環境に対応した Laravel アプリケーションを設計、構築、保守します。このスキルでは、Eloquent ORM の高度なリレーションシップパターン、Blade テンプレートと Livewire によるインタラクティブ機能、キューおよびイベントシステム、ミドルウェアパイプライン、サービスプロバイダー、あらゆるレイヤーでの Pest テスト、マイグレーション、シーダー、ファクトリーのための Artisan ツールなど、Laravel エコシステム全体を扱います。
新規開発か既存アプリケーションの改修かを問わず、アプリケーションフレームワークが Laravel の場合は、このスキルを適用してください。
複数フェーズのプロセス
フェーズ 1:コンテキストの把握
- Laravel のバージョンを特定します(
composer.json->laravel/framework) config/を調べ、有効なパッケージとカスタム設定を確認します- 既存のモデル、リレーションシップ、マイグレーション履歴を把握します
routes/を確認し、API、Web、コンソール、チャネルの定義を調べます- インストール済みのファーストパーティパッケージ(Sanctum、Horizon、Telescope、Pulse、Pennant、Scout、Cashier)を一覧にします
- Livewire、Inertia、または Blade のみのフロントエンドスタックを確認します
停止 — Laravel のバージョンとインストール済みパッケージを確認するまでは、アーキテクチャのレビューを開始しないでください。
ドキュメント確認プロトコル
[HARD-GATE] Laravel API に不確かな点がある場合は、推測せずに確認してください。mcp__context7__resolve-library-id に続けて mcp__context7__query-docs を使用します(推奨)。代替手段として、https://github.com/laravel/docs から取得してください。Livewire、Pest、Inertia については、それぞれを context7 で個別に解決してください。取得したドキュメントは、記憶している知識より優先されます。
フェーズ 2:アーキテクチャのレビュー
- ディレクトリ構造が Laravel の規約に従っていることを確認します(下記のセクションを参照)
- サービスプロバイダーの登録と遅延読み込みを評価します
- ミドルウェアスタックの順序とグループを確認します
- キュー接続の設定とワーカーのトポロジーを評価します
- キャッシュ戦略(設定、ルート、ビュー、アプリケーションレベル)を確認します
停止 — アーキテクチャ上の課題を文書化するまでは、実装を開始しないでください。
フェーズ 3:実装
- 最初にマイグレーションを作成します — スキーマが信頼できる唯一の情報源です
- リレーションシップ、スコープ、キャスト、アクセサーを備えた Eloquent モデルを構築します
- 専用の Action クラスまたは Service クラスにビジネスロジックを実装します
- ルートにバインドするコントローラー(単一アクションまたはリソースコントローラー)を作成します
- バリデーションには Form Request を、認可には Policy を追加します
- 非同期ワークフローのためにイベント、リスナー、ジョブを連携させます
停止 — Form Request と Policy を省略しないでください。インラインでのバリデーションと認可はアンチパターンです。
フェーズ 4:テスト
- 分離されたロジック(Action、値オブジェクト、Cast)のユニットテスト
- HTTP エンドポイントとミドルウェアの動作に対するフィーチャーテスト
- 重要なユーザーフローに対する Laravel Dusk を使ったブラウザーテスト
assertDatabaseHas、assertSoftDeletedを使ったデータベースアサーション- 副作用の検証に使うキューとイベントのフェイク
停止 — すべてのレイヤーでテストに合格するまでは、最適化に進まないでください。
フェーズ 5:最適化
- Eager Loading を適用して N+1 クエリを解消します
- コストの高い計算と設定/ルート/ビューをキャッシュします
- 頻繁にクエリされるカラムにインデックスを付与し、
EXPLAINで検証します - 開発環境で Telescope または Debugbar を使ってプロファイリングします
- 本番環境のキュー監視用に Horizon を設定します
Eloquent のパターン
リレーションシップ
| リレーションシップ | メソッド | 逆方向 | 使用例 |
|---|---|---|---|
| 1 対 1 | hasOne | belongsTo | User -> Profile |
| 1 対多 | hasMany | belongsTo | Post -> Comments |
| 多対多 | belongsToMany | belongsToMany | User <-> Roles(中間テーブル) |
| Has-Many-Through | hasManyThrough | — | Country -> Posts(Users 経由) |
| ポリモーフィック | morphMany / morphTo | morphTo | Posts と Videos に対する Comments |
| 多対多ポリモーフィック | morphToMany | morphedByMany | Posts と Videos に付与する Tags |
スコープ
// Local scope — reusable query constraint
public function scopeActive(Builder $query): Builder
{
return $query->where('status', 'active');
}
// Usage: User::active()->where('role', 'admin')->get();
// Global scope — applied to all queries on the model
protected static function booted(): void
{
static::addGlobalScope('published', function (Builder $builder) {
$builder->whereNotNull('published_at');
});
}
アクセサー、ミューテーター、キャスト
// Attribute accessor/mutator (Laravel 11+ syntax)
protected function fullName(): Attribute
{
return Attribute::make(
get: fn () => "{$this->first_name} {$this->last_name}",
);
}
// Custom cast
protected function casts(): array
{
return [
'options' => AsCollection::class,
'address' => AddressCast::class,
'status' => OrderStatus::class, // Backed enum
'metadata' => 'array',
'is_active' => 'boolean',
'amount' => MoneyCast::class,
];
}
Eager Loading によるクエリ最適化
// BAD — N+1 problem: 1 query for posts + N queries for authors
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // Triggers lazy load each iteration
}
// GOOD — Eager load: 2 queries total
$posts = Post::with('author')->get();
// Nested eager loading
$posts = Post::with(['author', 'comments.user'])->get();
// Constrained eager loading
$posts = Post::with(['comments' => function ($query) {
$query->where('approved', true)->latest()->limit(5);
}])->get();
// Prevent lazy loading in development
Model::preventLazyLoading(! app()->isProduction());
Blade テンプレートと Livewire コンポーネント
Blade の規約
- レイアウト:
@yield/@sectionを使用するresources/views/layouts/app.blade.php、またはコンポーネントベースの<x-app-layout> - コンポーネント:
resources/views/components/— 匿名クラスまたはクラスベース - パーシャル:再利用可能な断片には
@include('partials.sidebar')を使用します - エスケープされた出力には
{{ }}を使用し、HTML が明示的に信頼できる場合に限り{!! !!}を使用します - 生の PHP 条件分岐より、Blade ディレクティブ(
@auth、@can、@env)を優先します
Livewire のパターン
// Full-page Livewire component (Livewire 3+)
#[Layout('layouts.app')]
#[Title('Dashboard')]
class Dashboard extends Component
{
public string $search = '';
#[Computed]
public function users(): LengthAwarePaginator
{
return User::where('name', 'like', "%{$this->search}%")->paginate(15);
}
public function render(): View
{
return view('livewire.dashboard');
}
}
フロントエンドスタックの選択表
| 判断基準 | Livewire を選択 | Inertia を選択 |
|---|---|---|
| 既存の Blade コードベース | はい | いいえ |
| SPA のような操作性が必要 | 部分的(wire:navigate を使用) | はい |
| チームに Vue/React の経験がある | いいえ | はい |
| サーバーサイドレンダリングを優先 | はい | アダプターによります |
| リアルタイムのリアクティブ性 | はい(ポーリング、ストリーム) | Echo の設定が必要 |
| SEO が重要なページ | どちらでも対応可能 | どちらでも対応可能(SSR アダプター) |
キュー、ジョブ、イベントのパターン
ジョブの設計
class ProcessInvoice implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $backoff = 60;
public int $timeout = 120;
public string $queue = 'invoices';
public function __construct(public readonly Invoice $invoice) {}
public function handle(PdfGenerator $generator): void
{
$generator->generate($this->invoice);
}
public function failed(Throwable $exception): void
{
// Notify admin, log to error tracker
}
}
イベント/リスナーのパターン
// Dispatch event
OrderPlaced::dispatch($order);
// Listener (queued)
class SendOrderConfirmation implements ShouldQueue
{
public function handle(OrderPlaced $event): void
{
Mail::to($event->order->user)->send(new OrderConfirmationMail($event->order));
}
}
同期処理と非同期処理の選択表
| タスク | キュー処理 | 同期処理 |
|---|---|---|
| メール/通知の送信 | はい | リクエストサイクル内では行いません |
| PDF の生成 | はい | 2 秒未満で、ユーザーが待つ場合に限ります |
| 決済処理 | 場合によります — Webhook 駆動を推奨 | ゲートウェイが 5 秒未満で応答する場合 |
| キャッシュのウォームアップ | はい | 行いません |
| 監査ログの記録 | はい(大量の場合)、または同期(少量の場合) | 確実な配信が必要な場合 |
| 検索インデックスの作成 | はい | 行いません |
ミドルウェアとサービスプロバイダー
ミドルウェアのスタック順序
ミドルウェアの順序は重要です。bootstrap/app.php のデフォルトスタック(Laravel 11 以降):
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
HandleInertiaRequests::class, // After session, before response
]);
$middleware->api(prepend: [
EnsureFrontendRequestsAreStateful::class, // Sanctum SPA auth
]);
$middleware->alias([
'role' => EnsureUserHasRole::class,
'verified' => EnsureEmailIsVerified::class,
]);
})
サービスプロバイダーのベストプラクティス
- バインディングは
register()に登録し、そこでコンテナから解決しないでください - 起動時の処理(イベントリスナー、ルートモデルバインディング、マクロ)は
boot()に記述します - すべてのリクエストで必要になるわけではないバインディングには、遅延プロバイダーを使用します
- プロバイダーに重い処理を記述せず、専用クラスに委譲します
Pest によるテスト
ユニットテスト
test('order total calculates tax correctly', function () {
$order = Order::factory()->make(['subtotal' => 10000, 'tax_rate' => 0.08]);
expect($order->total)->toBe(10800);
});
フィーチャーテスト
test('authenticated user can create a post', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)
->postJson('/api/posts', [
'title' => 'My Post',
'body' => 'Content here.',
]);
$response->assertCreated()
->assertJsonPath('data.title', 'My Post');
$this->assertDatabaseHas('posts', [
'user_id' => $user->id,
'title' => 'My Post',
]);
});
キューとイベントのフェイク
test('placing an order dispatches confirmation job', function () {
Queue::fake();
$order = Order::factory()->create();
PlaceOrder::dispatch($order);
Queue::assertPushed(SendOrderConfirmation::class, function ($job) use ($order) {
return $job->order->id === $order->id;
});
});
ブラウザーテスト(Dusk)
test('user can complete checkout flow', function () {
$this->browse(function (Browser $browser) {
$browser->loginAs(User::factory()->create())
->visit('/cart')
->press('Checkout')
->waitForText('Order Confirmed')
->assertSee('Thank you');
});
});
Artisan コマンド、マイグレーション、シーダー、ファクトリー
マイグレーションの規約
// Always include down() for rollback capability
public function up(): void
{
Schema::create('invoices', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('number')->unique();
$table->integer('amount'); // Store money as cents
$table->string('currency', 3);
$table->string('status')->default('draft');
$table->timestamp('due_at')->nullable();
$table->timestamps();
$table->softDeletes();
$table->index(['user_id', 'status']);
});
}
ファクトリーのパターン
class InvoiceFactory extends Factory
{
public function definition(): array
{
return [
'user_id' => User::factory(),
'number' => $this->faker->unique()->numerify('INV-####'),
'amount' => $this->faker->numberBetween(1000, 100000),
'currency' => 'USD',
'status' => 'draft',
'due_at' => now()->addDays(30),
];
}
public function paid(): static
{
return $this->state(fn () => ['status' => 'paid']);
}
public function overdue(): static
{
return $this->state(fn () => [
'status' => 'sent',
'due_at' => now()->subDays(7),
]);
}
}
Laravel のディレクトリー構成の規約
app/
├── Actions/ # Single-purpose action classes
├── Casts/ # Custom Eloquent casts
├── Console/Commands/ # Artisan commands
├── Enums/ # PHP backed enums
├── Events/ # Event classes
├── Exceptions/ # Custom exception classes
├── Http/
│ ├── Controllers/ # Resourceful or single-action controllers
│ ├── Middleware/ # Request/response middleware
│ └── Requests/ # Form Request validation
├── Jobs/ # Queued job classes
├── Listeners/ # Event listener classes
├── Mail/ # Mailable classes
├── Models/ # Eloquent models
├── Notifications/ # Notification classes
├── Observers/ # Model observers
├── Policies/ # Authorization policies
├── Providers/ # Service providers
├── Rules/ # Custom validation rules
├── Services/ # Domain service classes
└── View/Components/ # Blade view components
database/
├── factories/ # Model factories
├── migrations/ # Schema migrations (timestamped)
└── seeders/ # Database seeders
resources/views/
├── components/ # Blade components
├── layouts/ # Layout templates
├── livewire/ # Livewire component views
└── mail/ # Email templates
routes/
├── api.php # API routes
├── channels.php # Broadcast channels
├── console.php # Artisan closures
└── web.php # Web routes
tests/
├── Feature/ # Feature (integration) tests
├── Unit/ # Unit tests
└── Browser/ # Dusk browser tests
選択表
認証方式
| シナリオ | 推奨される方法 |
|---|---|
| SPA + 同一ドメイン | Sanctum(Cookie ベース、CSRF) |
| SPA + 異なるドメイン | Sanctum(トークンベース) |
| モバイルアプリ | Sanctum(トークンベース) |
| サードパーティーの API 利用者 | Passport(OAuth2) |
| シンプルな API トークン | Sanctum(プレーンテキストのハッシュ) |
| ソーシャルログイン | Socialite + Sanctum |
キャッシュ層
| データの種類 | キャッシュドライバー | TTL | 無効化 |
|---|---|---|---|
| 設定/ルート/ビュー | File(artisan cache) | 次回のデプロイまで | artisan optimize:clear |
| データベースのクエリー結果 | Redis / Memcached | 5~60 分 | イベント駆動または TTL |
| 全ページ/フラグメント | Redis | 1~15 分 | キャッシュタグ |
| セッションデータ | Redis | セッションの有効期間 | 自動 |
| レート制限 | Redis | ウィンドウの期間 | 自動 |
ファイルストレージ
| シナリオ | ディスク | ドライバー |
|---|---|---|
| ユーザーのアップロード(本番環境) | s3 | Amazon S3/互換サービス |
| ユーザーのアップロード(ローカル開発環境) | local | ローカルファイルシステム |
| 公開アセット | public | シンボリックリンクを使用したローカル |
| 一時ファイル | local | スケジュールにより整理されるローカル |
アンチパターン/よくある間違い
| アンチパターン | 失敗する理由 | 代わりに行うこと |
|---|---|---|
| 肥大化したコントローラー | ビジネスロジックのテストや保守が困難になります | ロジックをアクションまたはサービスクラスに移します |
| コントローラー内の生 SQL | SQL インジェクションのリスクがあり、移植性もありません | Eloquent またはクエリビルダーを使用します |
| マスアサインメント保護の欠如 | データ改ざんの脆弱性につながります | 必ず $fillable または $guarded を定義します |
| コントローラー内のインラインバリデーション | バリデーションが HTTP 層に結び付きます | フォームリクエストを使用します |
| 再試行/バックオフ設定のないジョブ | 失敗が通知されず、復旧できません | $tries、$backoff、failed() を設定します |
| グローバルスコープの過剰な使用 | クエリの挙動が隠れ、開発者を困惑させます | ローカルスコープを優先します |
| 金額を浮動小数点数で保存する | 浮動小数点数の精度誤差が発生します | 整数のセント単位で保存し、表示時に変換します |
| データベースインデックスの欠如 | 規模が大きくなるとクエリが遅くなります | WHERE と ORDER BY 用の複合インデックスを追加します |
| 設定ファイル内のシークレット | バージョン管理で認証情報が漏えいします | .env のみを使用します |
| 本番データベースに対するテスト | データ破損や不安定なテストにつながります | SQLite のインメモリデータベースまたは専用のテストデータベースを使用します |
| API レスポンスでの遅延読み込み | N+1 クエリが発生し、API レスポンスが遅くなります | 開発環境で preventLazyLoading() を有効にします |
不合理な正当化を防ぐ
- マイグレーションを省略してデータベースを直接編集してはいけません。マイグレーションが信頼できる唯一の情報源です。
- 「その方が速いから」といってビジネスロジックをコントローラーに記述してはいけません。アクションクラスを使用してください。
- 「バリデーションが簡単だから」といってフォームリクエストを省略してはいけません。バリデーションは必ず増えていきます。
- 「面倒だから」といって
preventLazyLoading()を無効にしてはいけません。N+1 クエリを修正してください。 - 「金額が小さいから」といって金額を浮動小数点数で保存してはいけません。精度誤差は積み重なります。
連携ポイント
| スキル | 連携方法 |
|---|---|
php-specialist | 最新の PHP 8.x のパターンが、すべての Laravel コードの基盤となります |
laravel-boost | AI 支援開発のガイドラインと MCP ツール |
senior-backend | API 設計、キャッシュ戦略、イベント駆動アーキテクチャ |
test-driven-development | RED-GREEN-REFACTOR による Pest テストのワークフロー |
database-schema-design | マイグレーション計画、インデックス戦略、データモデリング |
security-review | Sanctum/Passport の設定、CSRF、入力バリデーション |
performance-optimization | クエリのプロファイリング、キャッシュの調整、キューワーカーのスケーリング |
deployment | Forge/Vapor/Envoyer によるデプロイ、artisan optimize |
context7 MCP | 情報が不確かな場合に、最新の Laravel ドキュメントを取得します |
laravel/docs GitHub | Laravel API リファレンスの信頼できる情報源 |
スキルの種類
FLEXIBLE — 作業の範囲に応じて、複数フェーズのプロセスを調整します。モデルを 1 つ変更するだけならフェーズ 2 全体を省略できますが、新しいモジュールでは 5 つすべてのフェーズに従う必要があります。熱心な読み込み、フォームリクエスト、Pest テスト、マイグレーションを優先したスキーマ変更といった中核的な規約は、作業範囲にかかわらず必ず守ってください。