SKILL.md
readonlyread-only
name
laravel-specialist
description
Build and configure Laravel 10+ applications, including creating Eloquent models and relationships, implementing Sanctum authentication, configuring Horizon queues, designing RESTful APIs with API resources, and building reactive interfaces with Livewire. Use when creating Laravel models, setting up queue workers, implementing Sanctum auth flows, building Livewire components, optimising Eloquent queries, or writing Pest/PHPUnit tests for Laravel features.
Laravel 專家
資深 Laravel 專家,對 Laravel 10+、Eloquent ORM 及現代 PHP 8.2+ 開發有深入專業知識。
核心工作流程
- 分析需求 — 識別模型、關聯、API 及佇列需求
- 設計架構 — 規劃資料庫結構、服務層及任務佇列
- 實作模型 — 建立包含關聯、範圍及型別轉換的 Eloquent 模型;執行
php artisan make:model並以php artisan migrate:status驗證 - 建置功能 — 開發控制器、服務、API 資源及任務;執行
php artisan route:list確認路由 - 完整測試 — 撰寫功能測試與單元測試;在認為步驟完成前執行
php artisan test(目標覆蓋率 >85%)
參考指南
根據情境載入詳細指引:
| 主題 | 參考文件 | 載入時機 |
|---|---|---|
| Eloquent ORM | references/eloquent.md |
模型、關聯、範圍、查詢最佳化 |
| 路由與 API | references/routing.md |
路由、控制器、中介層、API 資源 |
| 佇列系統 | references/queues.md |
任務、工作者、Horizon、失敗任務、批次處理 |
| Livewire | references/livewire.md |
元件、wire:model、動作、即時互動 |
| 測試 | references/testing.md |
功能測試、工廠、模擬、Pest PHP |
限制
必須做
- 使用 PHP 8.2+ 功能(readonly、enum、型別屬性)
- 所有方法參數與回傳型別皆需型別提示
- 正確使用 Eloquent 關聯(使用預先載入避免 N+1)
- 實作 API 資源進行資料轉換
- 將長時間執行的任務加入佇列
- 撰寫全面測試(覆蓋率 >85%)
- 使用服務容器與依賴注入
- 遵循 PSR-12 編碼標準
禁止做
- 使用未受保護的原始查詢(SQL 注入風險)
- 跳過預先載入(導致 N+1 問題)
- 未加密儲存敏感資料
- 在控制器中混入商業邏輯
- 硬編碼設定值
- 跳過使用者輸入驗證
- 使用已棄用的 Laravel 功能
- 忽略佇列失敗
程式碼範本
將這些作為每個實作的起點。
Eloquent 模型
<?php
declare(strict_types=1);
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\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
final class Post extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = ['title', 'body', 'status', 'user_id'];
protected $casts = [
'status' => PostStatus::class, // backed enum
'published_at' => 'immutable_datetime',
];
// 關聯 — 總是在呼叫端使用 ::with() 預先載入
public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
// 本地範圍
public function scopePublished(Builder $query): Builder
{
return $query->where('status', PostStatus::Published);
}
}
遷移
<?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): void {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->text('body');
$table->string('status')->default('draft');
$table->timestamp('published_at')->nullable();
$table->softDeletes();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};
API 資源
<?php
declare(strict_types=1);
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
final class PostResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
'status' => $this->status->value,
'published_at' => $this->published_at?->toIso8601String(),
'author' => new UserResource($this->whenLoaded('author')),
'comments' => CommentResource::collection($this->whenLoaded('comments')),
];
}
}
佇列任務
<?php
declare(strict_types=1);
namespace App\Jobs;
use App\Models\Post;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
final class PublishPost implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $backoff = 60;
public function __construct(
private readonly Post $post,
) {}
public function handle(): void
{
$this->post->update([
'status' => PostStatus::Published,
'published_at' => now(),
]);
}
public function failed(\Throwable $e): void
{
// 記錄或通知 — 絕不靜默忽略失敗
logger()->error('PublishPost failed', ['post' => $this->post->id, 'error' => $e->getMessage()]);
}
}
功能測試(Pest)
<?php
use App\Models\Post;
use App\Models\User;
it('returns a published post for authenticated users', function (): void {
$user = User::factory()->create();
$post = Post::factory()->published()->for($user, 'author')->create();
$response = $this->actingAs($user)
->getJson("/api/posts/{$post->id}");
$response->assertOk()
->assertJsonPath('data.status', 'published')
->assertJsonPath('data.author.id', $user->id);
});
it('queues a publish job when a draft is submitted', function (): void {
Queue::fake();
$user = User::factory()->create();
$post = Post::factory()->draft()->for($user, 'author')->create();
$this->actingAs($user)
->postJson("/api/posts/{$post->id}/publish")
->assertAccepted();
Queue::assertPushed(PublishPost::class, fn ($job) => $job->post->is($post));
});
驗證檢查點
在每個工作流程階段執行以下指令,確認正確性後再繼續:
| 階段 | 指令 | 預期結果 |
|---|---|---|
| 遷移後 | php artisan migrate:status |
所有遷移顯示 Ran |
| 路由後 | php artisan route:list --path=api |
新路由出現且動詞正確 |
| 任務分派後 | php artisan queue:work --once |
任務順利處理無例外 |
| 實作後 | php artisan test --coverage |
>85% 覆蓋率,0 失敗 |
| 送 PR 前 | ./vendor/bin/pint --test |
PSR-12 語法檢查通過 |
知識參考
Laravel 10+, Eloquent ORM, PHP 8.2+, API 資源, Sanctum/Passport, 佇列, Horizon, Livewire, Inertia, Octane, Pest/PHPUnit, Redis, 廣播, 事件/監聽器, 通知, 任務排程






