Skip to main content

laravel

Use when building or extending a Laravel 11/12 app — Eloquent models, migrations and relationships, routing with controllers and Form Requests, queues and background jobs, framework-native security (validation, mass-assignment, policies, signed URLs, rate limiting), and Pest/PHPUnit feature tests. NOT pure PHP language or toolchain work (that is `php`).

ソース情報

リポジトリ
ericrisco/rsc-harness
ソースの最終更新活動
2026年7月29日 23:18
検出された SKILL.md の言語
英語
スター
142
フォーク
11

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

ファイルエクスプローラー
6 ファイル

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
laravel
description
Use when building or extending a Laravel 11/12 app — Eloquent models, migrations and relationships, routing with controllers and Form Requests, queues and background jobs, framework-native security (validation, mass-assignment, policies, signed URLs, rate limiting), and Pest/PHPUnit feature tests. NOT pure PHP language or toolchain work (that is `php`).
tags
["laravel","php","eloquent","queues","pest","artisan"]
recommends
["php","mysql","redis","api-design","secure-coding","testing-web"]
origin
risco
# Laravel (11/12 era) Build Laravel the way the framework ships **today** — Laravel 12 (released 2025-02-24, PHP 8.2–8.4), continuing the slimmed Laravel 11 skeleton. That means **no `app/Http/Kernel.php` and no `app/Console/Kernel.php`**: middleware, routing, exception handling and providers are all configured in **`bootstrap/app.php`**, and scheduled tasks live in **`routes/console.php`**. Generate that structure, not 2019 Laravel. This skill owns the **framework surface**: Eloquent, routing/controllers, queues, the artisan-shaped project, and in-framework Pest tests. The PHP *language and toolchain underneath* (types, enums, Composer/PSR-4, PHPStan, Pint) is the [../php/SKILL.md](../php/SKILL.md) skill — its boundary line literally reads "NOT Eloquent/Blade/Artisan (that is laravel)"; this is the mirror. ## Orient before you scaffold A wrong version assumption generates dead code. Detect the version and skeleton first. ```bash composer show laravel/framework # exact installed version ls bootstrap/app.php # present -> L11/12 slim skeleton ls app/Http/Kernel.php 2>/dev/null # present -> L10 or older (Kernel era) php artisan --version # confirms runtime + version ``` If `bootstrap/app.php` exists and `app/Http/Kernel.php` does **not**, you are on L11/12 — use the modern idioms below. Then pick your path: | Situation | Do | |---|---| | Greenfield | `laravel new <app>`, then a starter kit: React (React 19 + TS + Inertia 2 + shadcn/ui), Vue (Vue 3 + Inertia 2 + shadcn-vue), or Livewire (Livewire 3 + Volt + Flux UI). Optional WorkOS AuthKit for auth. | | Brownfield L11/12 | Follow the repo's existing conventions; do not "reorganize" into folders it dropped. | | Pre-11 (Kernel era) | Migrate to `bootstrap/app.php` deliberately; move Kernel middleware to `withMiddleware()`, schedule to `routes/console.php`. Treat as a project, not an inline tweak. | ## The slim structure (where things live now) ```text bootstrap/app.php # withRouting(), withMiddleware(), withExceptions(), withProviders() routes/web.php # web routes (CSRF-protected, session) routes/api.php # api routes (stateless) — present once you opt in routes/console.php # closures + the SCHEDULER (Schedule::command(...)->daily()) app/Models/ # Eloquent models app/Http/Controllers/ # thin controllers app/Http/Requests/ # Form Requests (validation + authorization) app/Jobs/ # queued jobs app/Policies/ # authorization policies ``` ```php // Bad: hunting for or editing app/Http/Kernel.php to register middleware (L11/12 deleted it). // Good: bootstrap/app.php return Application::configure(basePath: dirname(__DIR__)) ->withRouting(web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php') ->withMiddleware(function (Middleware $middleware) { $middleware->alias(['subscribed' => EnsureUserIsSubscribed::class]); $middleware->web(append: [TrackLastSeen::class]); }) ->withExceptions(function (Exceptions $exceptions) { $exceptions->dontReport(InvalidOrderException::class); })->create(); ``` ## Eloquent **Mass assignment is the #1 Laravel footgun.** A model must declare an allowlist; an open blocklist on user input lets a request set any column (`is_admin`, `balance`). ```php // Bad: anything in the request body can be written, including columns you never meant to expose. class Post extends Model { protected $guarded = []; } // Good: explicit allowlist. Casts give you typed attributes back out of the DB. class Post extends Model { protected $fillable = ['title', 'body', 'published_at']; protected function casts(): array { return [ 'published_at' => 'datetime', 'status' => PostStatus::class, // backed-enum cast 'meta' => 'array', // json <-> array 'api_token' => 'encrypted', // at-rest encryption ]; } public function tags(): BelongsToMany { return $this->belongsToMany(Tag::class); } } ``` **N+1 is the most common Laravel performance bug** — a query per row instead of one. Eager load. ```php // Bad: 1 query for posts + 1 per post for its author = N+1. foreach (Post::all() as $post) { echo $post->author->name; } // Good: eager load up front -> 2 queries total. foreach (Post::with('author')->get() as $post) { echo $post->author->name; } ``` Migrations define the schema; the `casts()` method (L11+ method form, not the old `$casts` array) maps columns to enums/arrays/encrypted values. Relationship deep dives (polymorphic, has-many-through, scopes, observers, custom casts, accessors via the `Attribute` class) live in [references/eloquent-patterns.md](references/eloquent-patterns.md). ## Routing, controllers, validation Keep controllers thin: they take a validated request, call a service/model, return a response. Push branching logic out of routes — **business logic in `routes/web.php` is untestable and unreusable**. **Form Requests do validation AND authorization in one object** — the `authorize()` gate runs before `rules()`, so an unauthorized request never reaches your validation or controller. ```php // app/Http/Requests/StoreOrderRequest.php final class StoreOrderRequest extends FormRequest { public function authorize(): bool { return $this->user()->can('create', Order::class); // policy check, before rules } public function rules(): array { return [ 'sku' => ['required', 'string', 'exists:products,sku'], 'quantity' => ['required', 'integer', 'min:1', 'max:100'], ]; } } // Controller: route-model binding + the Form Request = a tiny method. public function store(StoreOrderRequest $request): RedirectResponse { $order = Order::create($request->validated()); // only allow-listed, validated data ProcessOrder::dispatch($order); return to_route('orders.show', $order); } ``` For JSON APIs, shape output with **API Resources** (`JsonResource`). The transport *contract* — resource naming, versioning, status-code semantics, pagination shape — is the **api-design** skill, not this one; this skill covers implementing it in Laravel. ## Queues & scheduling A job that implements `ShouldQueue` is pushed to the queue and runs out-of-band instead of inline — that is what gets a slow email/payment off the request cycle. ```php final class ProcessOrder implements ShouldQueue { use Queueable, Batchable; public int $tries = 3; // attempts before it lands in failed_jobs public int $timeout = 120; // seconds before the worker kills it public function backoff(): array { return [10, 60, 300]; } // exponential-ish retry spacing public function __construct(public Order $order) {} public function handle(): void { if ($this->batch()?->cancelled()) return; // respect a cancelled batch // ... charge, fulfil ... } } ProcessOrder::dispatch($order); // queued (or inline if driver is sync) ``` Batches let you fan out and react when the group finishes; `allowFailures()` keeps the batch going when individual jobs fail. ```php Bus::batch([new ProcessOrder($a), new ProcessOrder($b)]) ->allowFailures() ->then(fn (Batch $b) => Log::info("done: {$b->totalJobs}")) ->dispatch(); ``` **Driver choice:** | Need | Use | |---|---| | Simple, low volume, no extra infra | `database` driver (a `jobs` table) | | High throughput + a dashboard (retries, runtime, failures) | **Redis + Horizon** — Horizon manages the workers and gives you the monitor | Schedule recurring work in `routes/console.php` (the L11/12 home for the scheduler — there is no `app/Console/Kernel.php`): ```php // routes/console.php Schedule::command('orders:reconcile')->dailyAt('02:00'); Schedule::job(new PruneStaleCarts)->hourly(); ``` Worker deployment, Horizon config, the retry/backoff/timeout matrix, `failed_jobs` + `queue:retry`, and supervisor/`queue:work` flags are in [references/queues-and-scheduling.md](references/queues-and-scheduling.md). ## Security (framework-native) | Control | Use | Why | |---|---|---| | Validation | Form Request `rules()` | reject malformed input at the edge, before the controller | | Mass-assignment guard | `$fillable` allowlist (see Eloquent) | stops a request writing columns you never exposed | | Authorization | Policies + Gates, `$user->can(...)`, `authorize()` | keeps authz out of controllers and consistent | | Tamper-proof links | signed URLs (`URL::signedRoute`, `signed` middleware) | unsubscribe/verify links that can't be forged | | Abuse limiting | rate limiting (`throttle` middleware, `RateLimiter::for`) | caps brute-force / scraping per user or IP | | Secrets at rest | `Hash::make`/`Hash::check` for passwords, `Crypt` for reversible data | bcrypt/argon2 hashing; never store plaintext | CSRF protection is automatic on `web` routes; do not disable it. **Read config through `config()`, never `env()` outside `config/*.php`** — once configs are cached (`config:cache` in prod) `env()` returns `null` everywhere else. Cross-stack OWASP/threat-modeling is the **secure-coding** skill; the table above is the framework-native layer this skill owns. ## Testing Pest is the default runner in new Laravel apps (PHPUnit still fully supported). Use `RefreshDatabase` for a clean schema per test and model factories for data. ```php uses(RefreshDatabase::class); it('processes a paid order', function () { $user = User::factory()->create(); $this->actingAs($user) ->post('/orders', ['sku' => 'ABC', 'quantity' => 2]) ->assertRedirect(); $this->assertDatabaseHas('orders', ['sku' => 'ABC', 'quantity' => 2]); }); ``` **The sync-driver gotcha.** Tests use the `sync` queue driver by default, so a dispatched job runs **inline** during the test — it does not sit on a queue. If you assert "the email was sent" you are accidentally testing execution. To test that work was *dispatched*, fake the queue first: ```php it('dispatches order processing', function () { Queue::fake(); // jobs are now recorded, not run $order = Order::factory()->create(); ProcessOrder::dispatch($order); Queue::assertPushed(ProcessOrder::class); // assert dispatch, not side effects }); ``` `Http::fake()` and `Event::fake()` work the same way for outbound HTTP and events. Cross-stack browser/E2E (Playwright/Cypress) strategy is the **testing-web** skill; this skill covers in-framework Pest/PHPUnit feature and unit tests. ## Ecosystem (reach for these by name) Pennant (feature flags / A-B), Pulse (real-time perf: slow queries, job throughput), Folio (page-based routing), Horizon (Redis queue dashboard). First-party — prefer them over rolling your own. ## Anti-patterns | Pattern | Why it is bad | Do instead | |---|---|---| | `protected $guarded = []` on a user-facing model | any request column becomes mass-assignable (`is_admin`, `balance`) | explicit `$fillable` allowlist | | Hunting for / re-creating `app/Http/Kernel.php` on L11/12 | the file was deleted; your middleware never registers | configure in `bootstrap/app.php` `withMiddleware()` | | Lazy access inside a loop/Blade (`$post->author` per row) | N+1 — one query per row | eager load with `with()` | | Fat controllers / logic in `routes/web.php` | untestable, unreusable, no authz boundary | thin controller + Form Request + service/model | | Asserting a queued job's *side effects* in a test | sync driver runs it inline — you test execution, not dispatch | `Queue::fake()` + `Queue::assertPushed()` | | `env()` outside `config/*.php` | returns `null` once `config:cache` runs in prod | read via `config()`; `env()` only inside config files | | `$tries` unset on a flaky job | one transient failure = permanent failure | set `$tries` + `backoff()` | | Committing real secrets in `.env` | leaks credentials into history | `.env` is git-ignored; commit `.env.example` only | | Old `$casts = [...]` array (deprecated form) | misses the L11+ `casts()` method idioms | the `casts(): array` method | ## Verify Run [scripts/verify.sh](scripts/verify.sh) from a Laravel project root. It validates `composer.json`, checks the framework version, runs Pint (style, read-only `--test`) and the test runner (Pest/`artisan test`) if present, and greps for `protected $guarded = []` under `app/Models`. It degrades gracefully — prints a skip notice and exits 0 — when there is no Laravel project or no `vendor/` dir, so it never blocks a non-Laravel repo. ## References & siblings - [references/eloquent-patterns.md](references/eloquent-patterns.md) — relationship catalog
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る