laravel-best-practices

Enforces Laravel architecture, exception handling, migration, and routing rules for controllers and repositories.

Updated Aug 28, 2026
One-click install
npx skills add https://github.com/jhannka/php-skills --skill laravel-best-practices-jhannka
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: laravel-best-practices
Source: https://github.com/jhannka/php-skills/tree/main/skills/laravel-best-practices
Command: npx skills add https://github.com/jhannka/php-skills --skill laravel-best-practices-jhannka

SYSTEM DOCUMENTATION & REQUIREMENTS

What problem does it solve? Laravel codebases built on a BaseController/BaseRepo architecture accumulate subtle runtime failures — LSP-violating overrides, dead FormRequest authorization, mass-assignment gaps, and version-incompatible validation APIs — that static analysis and php -l never catch. This Skill codifies the rules and production incident lessons needed to write and review Laravel controllers, repositories, migrations, and routes correctly. ## Core Features & Use Cases - Architecture Enforcement: Rules for BaseController/BaseRepo CRUD patterns, including parameter contravariance pitfalls, private method visibility, and mass-assignment gating via model $fillable. - Exception Handling & Security: Patterns for separating user-facing messages from logged debug details, safe trace logging, and correct HTTP status codes per exception type. - Migration & Route Safety: BigInt key conventions, PostgreSQL CHECK constraint handling, chunked data migrations, and route parameter constraints. - Use Case: When refactoring a Laravel controller that overrides BaseController::store(), apply the Skill to avoid narrowing Request to a FormRequest subtype, resolve the FormRequest via app(), and verify the override by actually loading the class rather than trusting php -l. ## Quick Start Ask the agent to review or write a Laravel controller, repository, migration, or route file using these best practices before committing the change.

Frequently Asked Questions about laravel-best-practices

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I override BaseController methods in Laravel without breaking LSP?

Keep parameter types identical to or wider than the parent signature — never narrow `Request $request` to a FormRequest subtype or add type hints where the parent has none. Resolve specific FormRequests inside the method body via `app()` instead of type-hinting them in the signature.

How do I prevent mass assignment vulnerabilities in Laravel controllers?

Mass assignment is gated by the Eloquent model's `$fillable` array, not the controller's `$validation` rules. Remove sensitive fields from `$fillable`, and use `$request->only(array_keys($rules))` to build payloads from validated fields only.

Why does php -l pass but my Laravel class still fatals?

`php -l` only parses syntax; it does not load the class hierarchy, so LSP-incompatible overrides fatal at autoload time. Verify by actually loading the class with `php -r` plus the autoloader, or by hitting the real route.

Does Laravel 5.4 FormRequest have a validated() method?

No — `FormRequest::validated()` was added in Laravel 5.5, and `Controller::validate()` returns void in 5.4.x. Run validation for its side effect, then call `$request->only(array_keys($rules))` to get the validated field subset.

Why does my Laravel migration rollback fail on PostgreSQL?

Changing a column type does not drop an existing CHECK constraint, and Laravel's `enum()` creates `varchar` plus CHECK rather than a native enum type. Explicitly drop the constraint with `DROP CONSTRAINT IF EXISTS` before altering the column, and restore the exact original definition in `down()`.

When should FormRequest authorize() not be relied on for permissions?

When the FormRequest is only instantiated in a constructor to source validation rules, `authorize()` never runs because Laravel only triggers it during container resolution of a type-hinted request. Enforce permissions with route-level middleware instead.