Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Patrones de arquitectura Laravel, routing/controladores, Eloquent ORM, capas de servicio, colas, eventos, caché y API resources para aplicaciones en producción.
.claude/skills/affaan-m-laravel-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 73% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 68% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 127% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 105% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 50% | 0% |
适用于可扩展、可维护应用的生产级 Laravel 架构模式。
config/* 中,并保持环境配置显式化。使用具有清晰层级边界(HTTP、服务/操作、模型)的常规 Laravel 布局。
app/
├── Actions/ # 单一用途的用例
├── Console/
├── Events/
├── Exceptions/
├── Http/
│ ├── Controllers/
│ ├── Middleware/
│ ├── Requests/ # 表单请求验证
│ └── Resources/ # API 资源
├── Jobs/
├── Models/
├── Policies/
├── Providers/
├── Services/ # 协调领域服务
└── Support/
config/
database/
├── factories/
├── migrations/
└── seeders/
resources/
├── views/
└── lang/
routes/
├── api.php
├── web.php
└── console.php保持控制器精简。将编排逻辑放在服务中,将单一职责逻辑放在操作中。
phpfinal class CreateOrderAction { public function __construct(private OrderRepository $orders) {} public function handle(CreateOrderData $data): Order { return $this->orders->create($data); } } final class OrdersController extends Controller { public function __construct(private CreateOrderAction $createOrder) {} public function store(StoreOrderRequest $request): JsonResponse { $order = $this->createOrder->handle($request->toDto()); return response()->json([ 'success' => true, 'data' => OrderResource::make($order), 'error' => null, 'meta' => null, ], 201); } }
为了清晰起见,优先使用路由模型绑定和资源控制器。
phpuse Illuminate\Support\Facades\Route; Route::middleware('auth:sanctum')->group(function () { Route::apiResource('projects', ProjectController::class); });
使用作用域绑定来防止跨租户访问。
phpRoute::scopeBindings()->group(function () { Route::get('/accounts/{account}/projects/{project}', [ProjectController::class, 'show']); });
conversation 与 conversations)。{conversation} 对应 Conversation)。phpuse App\Http\Controllers\Api\ConversationController; use App\Http\Controllers\Api\MessageController; use Illuminate\Support\Facades\Route; Route::middleware('auth:sanctum')->prefix('conversations')->group(function () { Route::post('/', [ConversationController::class, 'store'])->name('conversations.store'); Route::scopeBindings()->group(function () { Route::get('/{conversation}', [ConversationController::class, 'show']) ->name('conversations.show'); Route::post('/{conversation}/messages', [MessageController::class, 'store']) ->name('conversation-messages.store'); Route::get('/{conversation}/messages/{message}', [MessageController::class, 'show']) ->name('conversation-messages.show'); }); });
如果希望参数解析为不同的模型类,请定义显式绑定。对于自定义绑定逻辑,请使用 Route::bind() 或在模型上实现 resolveRouteBinding()。
phpuse App\Models\AiConversation; use Illuminate\Support\Facades\Route; Route::model('conversation', AiConversation::class);
在服务提供者中将接口绑定到实现,以实现清晰的依赖关系连接。
phpuse App\Repositories\EloquentOrderRepository; use App\Repositories\OrderRepository; use Illuminate\Support\ServiceProvider; final class AppServiceProvider extends ServiceProvider { public function register(): void { $this->app->bind(OrderRepository::class, EloquentOrderRepository::class); } }
phpfinal class Project extends Model { use HasFactory; protected $fillable = ['name', 'owner_id', 'status']; protected $casts = [ 'status' => ProjectStatus::class, 'archived_at' => 'datetime', ]; public function owner(): BelongsTo { return $this->belongsTo(User::class, 'owner_id'); } public function scopeActive(Builder $query): Builder { return $query->whereNull('archived_at'); } }
使用枚举或值对象进行严格类型化。
phpuse Illuminate\Database\Eloquent\Casts\Attribute; protected $casts = [ 'status' => ProjectStatus::class, ];
phpprotected function budgetCents(): Attribute { return Attribute::make( get: fn (int $value) => Money::fromCents($value), set: fn (Money $money) => $money->toCents(), ); }
php$orders = Order::query() ->with(['customer', 'items.product']) ->latest() ->paginate(25);
phpfinal class ProjectQuery { public function __construct(private Builder $query) {} public function ownedBy(int $userId): self { $query = clone $this->query; return new self($query->where('owner_id', $userId)); } public function active(): self { $query = clone $this->query; return new self($query->whereNull('archived_at')); } public function builder(): Builder { return $this->query; } }
使用全局作用域进行默认筛选,并使用 SoftDeletes 处理可恢复的记录。 对于同一筛选器,请使用全局作用域或命名作用域中的一种,除非你打算实现分层行为。
phpuse Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Builder; final class Project extends Model { use SoftDeletes; protected static function booted(): void { static::addGlobalScope('active', function (Builder $builder): void { $builder->whereNull('archived_at'); }); } }
phpuse Illuminate\Database\Eloquent\Builder; final class Project extends Model { public function scopeOwnedBy(Builder $query, int $userId): Builder { return $query->where('owner_id', $userId); } } // In service, repository etc. $projects = Project::ownedBy($user->id)->get();
phpuse Illuminate\Support\Facades\DB; DB::transaction(function (): void { $order->update(['status' => 'paid']); $order->items()->update(['paid_at' => now()]); });
YYYY_MM_DD_HHMMSS_create_users_table.phpsnake_case 且为复数形式phpuse 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('orders', function (Blueprint $table): void { $table->id(); $table->foreignId('customer_id')->constrained()->cascadeOnDelete(); $table->string('status', 32)->index(); $table->unsignedInteger('total_cents'); $table->timestamps(); }); } public function down(): void { Schema::dropIfExists('orders'); } };
将验证逻辑放在表单请求中,并将输入转换为 DTO。
phpuse App\Models\Order; final class StoreOrderRequest extends FormRequest { public function authorize(): bool { return $this->user()?->can('create', Order::class) ?? false; } public function rules(): array { return [ 'customer_id' => ['required', 'integer', 'exists:customers,id'], 'items' => ['required', 'array', 'min:1'], 'items.*.sku' => ['required', 'string'], 'items.*.quantity' => ['required', 'integer', 'min:1'], ]; } public function toDto(): CreateOrderData { return new CreateOrderData( customerId: (int) $this->validated('customer_id'), items: $this->validated('items'), ); } }
使用资源和分页保持 API 响应一致。
php$projects = Project::query()->active()->paginate(25); return response()->json([ 'success' => true, 'data' => ProjectResource::collection($projects->items()), 'error' => null, 'meta' => [ 'page' => $projects->currentPage(), 'per_page' => $projects->perPage(), 'total' => $projects->total(), ], ]);
.env 中,将配置保存在 config/*.php 中config:cache| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-05 | pass→pass | 7,567 | 5,559 | -27% | 1 | 1 | 0% | 1,458 | 3,719 | +155% | 0 | 0 | — |
case-01 | fail→pass | 16,623 | 14,260 | -14% | 1 | 1 | 0% | 3,518 | 6,081 | +73% | 0 | 0 | — |
case-02 | fail→pass | 15,232 | 13,532 | -11% | 1 | 1 | 0% | 3,469 | 5,811 | +68% | 0 | 0 | — |
case-03 | fail→fail | 17,549 | 14,042 | -20% | 1 | 1 | 0% | 3,676 | 5,880 | +60% | 0 | 0 | — |
case-04 | pass→pass | 7,423 | 5,856 | -21% | 1 | 1 | 0% | 1,460 | 3,785 | +159% | 0 | 0 | — |
case-06 | pass→pass | 7,285 | 4,978 | -32% | 1 | 1 | 0% | 1,620 | 3,668 | +126% | 0 | 0 | — |
case-07 | pass→pass | 10,951 | 7,042 | -36% | 1 | 1 | 0% | 2,088 | 4,033 | +93% | 0 | 0 | — |
case-08 | pass→pass | 10,559 | 5,161 | -51% | 1 | 1 | 0% | 1,839 | 3,757 | +104% | 0 | 0 | — |
case-09 | fail→fail | 6,561 | 5,811 | -11% | 1 | 1 | 0% | 1,392 | 3,570 | +156% | 0 | 0 | — |
case-10 | pass→pass | 9,475 | 7,843 | -17% | 1 | 1 | 0% | 1,931 | 4,349 | +125% | 0 | 0 | — |
case-11 | pass→pass | 13,776 | 8,770 | -36% | 1 | 1 | 0% | 2,906 | 4,780 | +64% | 0 | 0 | — |
case-12 | fail→pass | 8,173 | 4,934 | -40% | 1 | 1 | 0% | 1,613 | 3,663 | +127% | 0 | 0 | — |
case-13 | fail→pass | 9,311 | 4,529 | -51% | 1 | 1 | 0% | 1,677 | 3,436 | +105% | 0 | 0 | — |
case-14 | pass→pass | 12,504 | 8,018 | -36% | 1 | 1 | 0% | 2,343 | 4,174 | +78% | 0 | 0 | — |
case-15 | pass→pass | 7,329 | 5,221 | -29% | 1 | 1 | 0% | 1,515 | 3,697 | +144% | 0 | 0 | — |
case-16 | fail→fail | 8,937 | 10,000 | +12% | 1 | 1 | 0% | 1,983 | 4,752 | +140% | 0 | 0 | — |
case-17 | pass→pass | 11,000 | 6,921 | -37% | 1 | 1 | 0% | 2,056 | 3,881 | +89% | 0 | 0 | — |
case-18 | pass→pass | 14,607 | 16,100 | +10% | 1 | 1 | 0% | 2,692 | 5,653 | +110% | 0 | 0 | — |
case-19 | fail→pass | 10,560 | 4,015 | -62% | 1 | 1 | 0% | 2,326 | 3,493 | +50% | 0 | 0 | — |
case-20 | fail→pass | 9,686 | 7,132 | -26% | 1 | 1 | 0% | 1,848 | 4,080 | +121% | 0 | 0 | — |
case-21 | pass→pass | 12,523 | 14,088 | +12% | 1 | 1 | 0% | 2,815 | 5,408 | +92% | 0 | 0 | — |
case-22 | pass→pass | 15,677 | 13,292 | -15% | 1 | 1 | 0% | 3,184 | 5,552 | +74% | 0 | 0 | — |
case-23 | pass→pass | 8,057 | 7,836 | -3% | 1 | 1 | 0% | 1,654 | 4,400 | +166% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +26 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.