Laravel Best Practices
Best practices for Laravel, prioritized by impact. Each rule teaches what to do and why. For exact API syntax, verify with search-docs.
Consistency First
Before applying any rule, check what the application already does. Laravel offers multiple valid approaches — the best choice is the one the codebase already uses, even if another pattern would be theoretically better. Inconsistency is worse than a suboptimal pattern.
Check sibling files, related controllers, models, or tests for established patterns. If one exists, follow it — don't introduce a second way. These rules are defaults for when no pattern exists yet, not overrides.
Quick Reference
1. Database Performance → rules/db-performance.md
- Eager load with
with() to prevent N+1 queries
- Enable
Model::preventLazyLoading() in development
- Select only needed columns, avoid
SELECT *
chunk() / chunkById() for large datasets
- Index columns used in
WHERE, ORDER BY, JOIN
withCount() instead of loading relations to count
cursor() for memory-efficient read-only iteration
- Never query in Blade templates
2. Advanced Query Patterns → rules/advanced-queries.md
addSelect() subqueries over eager-loading entire has-many for a single value
- Dynamic relationships via subquery FK +
belongsTo
- Conditional aggregates (
CASE WHEN in selectRaw) over multiple count queries
setRelation() to prevent circular N+1 queries
whereIn + pluck() over whereHas for better index usage
- Two simple queries can beat one complex query
- Compound indexes matching
orderBy column order
- Correlated subqueries in
orderBy for has-many sorting (avoid joins)
3. Security → rules/security.md
- Define
$fillable or $guarded on every model, authorize every action via policies or gates
- No raw SQL with user input — use Eloquent or query builder
{{ }} for output escaping, @csrf on all POST/PUT/DELETE forms, throttle on auth and API routes
- Validate MIME type, extension, and size for file uploads
- Never commit
.env, use config() for secrets, encrypted cast for sensitive DB fields
4. Caching → rules/caching.md
Cache::remember() over manual get/put
Cache::flexible() for stale-while-revalidate on high-traffic data
Cache::memo() to avoid redundant cache hits within a request
- Cache tags to invalidate related groups
Cache::add() for atomic conditional writes
once() to memoize per-request or per-object lifetime
Cache::lock() / lockForUpdate() for race conditions
- Failover cache stores in production
5. Eloquent Patterns → rules/eloquent.md
- Correct relationship types with return type hints
- Local scopes for reusable query constraints
- Global scopes sparingly — document their existence
- Attribute casts in the
casts() method
- Cast date columns, use Carbon instances in templates
whereBelongsTo($model) for cleaner queries
- Never hardcode table names — use
(new Model)->getTable() or Eloquent queries
6. Validation & Forms → rules/validation.md
- Form Request classes, not inline validation
- Array notation
['required', 'email'] for new code; follow existing convention
$request->validated() only — never $request->all()
Rule::when() for conditional validation
after() instead of withValidator()
7. Configuration → rules/config.md
env() only inside config files
App::environment() or app()->isProduction()
- Config, lang files, and constants over hardcoded text
8. Testing Patterns → rules/testing.md
LazilyRefreshDatabase over RefreshDatabase for speed
assertModelExists() over raw assertDatabaseHas()
- Factory states and sequences over manual overrides
- Use fakes (
Event::fake(), Exceptions::fake(), etc.) — but always after factory setup, not before
recycle() to share relationship instances across factories
9. Queue & Job Patterns → rules/queue-jobs.md
retry_after must exceed job timeout; use exponential backoff [1, 5, 10]
ShouldBeUnique to prevent duplicates; WithoutOverlapping::untilProcessing() for concurrency
- Always implement
failed(); with retryUntil(), set $tries = 0
RateLimited middleware for external API calls; Bus::batch() for related jobs
- Horizon for complex multi-queue scenarios
10. Routing & Controllers → rules/routing.md
- Implicit route model binding
- Scoped bindings for nested resources
Route::resource() or apiResource()
- Methods under 10 lines — extract to actions/services
- Type-hint Form Requests for auto-validation
11. HTTP Client → rules/http-client.md
- Explicit
timeout and connectTimeout on every request
retry() with exponential backoff for external APIs
- Check response status or use
throw()
Http::pool() for concurrent independent requests
Http::fake() and preventStrayRequests() in tests
12. Events, Notifications & Mail → rules/events-notifications.md, rules/mail.md
- Event discovery over manual registration;
event:cache in production
ShouldDispatchAfterCommit / afterCommit() inside transactions
- Queue notifications and mailables with
ShouldQueue
- On-demand notifications for non-user recipients
HasLocalePreference on notifiable models
assertQueued() not assertSent() for queued mailables
- Markdown mailables for transactional emails
13. Error Handling → rules/error-handling.md
report()/render() on exception classes or in bootstrap/app.php — follow existing pattern
ShouldntReport for exceptions that should never log
- Throttle high-volume exceptions to protect log sinks
dontReportDuplicates() for multi-catch scenarios
- Force JSON rendering for API routes
- Structured context via
context() on exception classes
14. Task Scheduling → rules/scheduling.md
withoutOverlapping() on variable-duration tasks
onOneServer() on multi-server deployments
runInBackground() for concurrent long tasks
environments() to restrict to appropriate environments
takeUntilTimeout() for time-bounded processing
- Schedule groups for shared configuration
15. Architecture → rules/architecture.md
- Single-purpose Action classes; dependency injection over
app() helper
- Prefer official Laravel packages and follow conventions, don't override defaults
- Default to
ORDER BY id DESC or created_at DESC; mb_* for UTF-8 safety
defer() for post-response work; Context for request-scoped data; Concurrency::run() for parallel execution
16. Migrations → rules/migrations.md
- Generate migrations with
php artisan make:migration
constrained() for foreign keys
- Never modify migrations that have run in production
- Add indexes in the migration, not as an afterthought
- Mirror column defaults in model
$attributes
- Reversible
down() by default; forward-fix migrations for intentionally irreversible changes
- One concern per migration — never mix DDL and DML
17. Collections → rules/collections.md
- Higher-order messages for simple collection operations
cursor() vs. lazy() — choose based on relationship needs
lazyById() when updating records while iterating
toQuery() for bulk operations on collections
18. Blade & Views → rules/blade-views.md
$attributes->merge() in component templates
- Blade components over
@include; @pushOnce for per-component scripts
- View Composers for shared view data
@aware for deeply nested component props
19. Conventions & Style → rules/style.md
- Follow Laravel naming conventions for all entities
- Prefer Laravel helpers (
Str, Arr, Number, Uri, Str::of(), $request->string()) over raw PHP functions
- No JS/CSS in Blade, no HTML in PHP classes
- Code should be readable; comments only for config files
How to Apply
Always use a sub-agent to read rule files and explore this skill's content.
- Identify the file type and select relevant sections (e.g., migration → §16, controller → §1, §3, §5, §6, §10)
- Check sibling files for existing patterns — follow those first per Consistency First
- Verify API syntax with
search-docs for the installed Laravel version
Source: lartisan/filament-architect — distributed by TomeVault.
1---2name: laravel-best-practices3description: Apply this skill whenever writing, reviewing, or refactoring Laravel PHP code. This includes creating or modifying controllers, models, migrations, form requests, policies, jobs, scheduled commands, service classes, and Eloquent queries. Triggers for N+1 and query performance issues, caching strategies, authorization and security patterns, validation, error handling, queue and job configuration, route definitions, and architectural decisions. Also use for Laravel code reviews and refactoring existing Laravel code to follow best practices. Covers any task involving Laravel backend PHP code patterns. Use when this capability is needed.4---56# Laravel Best Practices78Best practices for Laravel, prioritized by impact. Each rule teaches what to do and why. For exact API syntax, verify with `search-docs`.910## Consistency First1112Before applying any rule, check what the application already does. Laravel offers multiple valid approaches — the best choice is the one the codebase already uses, even if another pattern would be theoretically better. Inconsistency is worse than a suboptimal pattern.1314Check sibling files, related controllers, models, or tests for established patterns. If one exists, follow it — don't introduce a second way. These rules are defaults for when no pattern exists yet, not overrides.1516## Quick Reference1718### 1. Database Performance → `rules/db-performance.md`1920- Eager load with `with()` to prevent N+1 queries21- Enable `Model::preventLazyLoading()` in development22- Select only needed columns, avoid `SELECT *`23- `chunk()` / `chunkById()` for large datasets24- Index columns used in `WHERE`, `ORDER BY`, `JOIN`25- `withCount()` instead of loading relations to count26- `cursor()` for memory-efficient read-only iteration27- Never query in Blade templates2829### 2. Advanced Query Patterns → `rules/advanced-queries.md`3031- `addSelect()` subqueries over eager-loading entire has-many for a single value32- Dynamic relationships via subquery FK + `belongsTo`33- Conditional aggregates (`CASE WHEN` in `selectRaw`) over multiple count queries34- `setRelation()` to prevent circular N+1 queries35- `whereIn` + `pluck()` over `whereHas` for better index usage36- Two simple queries can beat one complex query37- Compound indexes matching `orderBy` column order38- Correlated subqueries in `orderBy` for has-many sorting (avoid joins)3940### 3. Security → `rules/security.md`4142- Define `$fillable` or `$guarded` on every model, authorize every action via policies or gates43- No raw SQL with user input — use Eloquent or query builder44- `{{ }}` for output escaping, `@csrf` on all POST/PUT/DELETE forms, `throttle` on auth and API routes45- Validate MIME type, extension, and size for file uploads46- Never commit `.env`, use `config()` for secrets, `encrypted` cast for sensitive DB fields4748### 4. Caching → `rules/caching.md`4950- `Cache::remember()` over manual get/put51- `Cache::flexible()` for stale-while-revalidate on high-traffic data52- `Cache::memo()` to avoid redundant cache hits within a request53- Cache tags to invalidate related groups54- `Cache::add()` for atomic conditional writes55- `once()` to memoize per-request or per-object lifetime56- `Cache::lock()` / `lockForUpdate()` for race conditions57- Failover cache stores in production5859### 5. Eloquent Patterns → `rules/eloquent.md`6061- Correct relationship types with return type hints62- Local scopes for reusable query constraints63- Global scopes sparingly — document their existence64- Attribute casts in the `casts()` method65- Cast date columns, use Carbon instances in templates66- `whereBelongsTo($model)` for cleaner queries67- Never hardcode table names — use `(new Model)->getTable()` or Eloquent queries6869### 6. Validation & Forms → `rules/validation.md`7071- Form Request classes, not inline validation72- Array notation `['required', 'email']` for new code; follow existing convention73- `$request->validated()` only — never `$request->all()`74- `Rule::when()` for conditional validation75- `after()` instead of `withValidator()`7677### 7. Configuration → `rules/config.md`7879- `env()` only inside config files80- `App::environment()` or `app()->isProduction()`81- Config, lang files, and constants over hardcoded text8283### 8. Testing Patterns → `rules/testing.md`8485- `LazilyRefreshDatabase` over `RefreshDatabase` for speed86- `assertModelExists()` over raw `assertDatabaseHas()`87- Factory states and sequences over manual overrides88- Use fakes (`Event::fake()`, `Exceptions::fake()`, etc.) — but always after factory setup, not before89- `recycle()` to share relationship instances across factories9091### 9. Queue & Job Patterns → `rules/queue-jobs.md`9293- `retry_after` must exceed job `timeout`; use exponential backoff `[1, 5, 10]`94- `ShouldBeUnique` to prevent duplicates; `WithoutOverlapping::untilProcessing()` for concurrency95- Always implement `failed()`; with `retryUntil()`, set `$tries = 0`96- `RateLimited` middleware for external API calls; `Bus::batch()` for related jobs97- Horizon for complex multi-queue scenarios9899### 10. Routing & Controllers → `rules/routing.md`100101- Implicit route model binding102- Scoped bindings for nested resources103- `Route::resource()` or `apiResource()`104- Methods under 10 lines — extract to actions/services105- Type-hint Form Requests for auto-validation106107### 11. HTTP Client → `rules/http-client.md`108109- Explicit `timeout` and `connectTimeout` on every request110- `retry()` with exponential backoff for external APIs111- Check response status or use `throw()`112- `Http::pool()` for concurrent independent requests113- `Http::fake()` and `preventStrayRequests()` in tests114115### 12. Events, Notifications & Mail → `rules/events-notifications.md`, `rules/mail.md`116117- Event discovery over manual registration; `event:cache` in production118- `ShouldDispatchAfterCommit` / `afterCommit()` inside transactions119- Queue notifications and mailables with `ShouldQueue`120- On-demand notifications for non-user recipients121- `HasLocalePreference` on notifiable models122- `assertQueued()` not `assertSent()` for queued mailables123- Markdown mailables for transactional emails124125### 13. Error Handling → `rules/error-handling.md`126127- `report()`/`render()` on exception classes or in `bootstrap/app.php` — follow existing pattern128- `ShouldntReport` for exceptions that should never log129- Throttle high-volume exceptions to protect log sinks130- `dontReportDuplicates()` for multi-catch scenarios131- Force JSON rendering for API routes132- Structured context via `context()` on exception classes133134### 14. Task Scheduling → `rules/scheduling.md`135136- `withoutOverlapping()` on variable-duration tasks137- `onOneServer()` on multi-server deployments138- `runInBackground()` for concurrent long tasks139- `environments()` to restrict to appropriate environments140- `takeUntilTimeout()` for time-bounded processing141- Schedule groups for shared configuration142143### 15. Architecture → `rules/architecture.md`144145- Single-purpose Action classes; dependency injection over `app()` helper146- Prefer official Laravel packages and follow conventions, don't override defaults147- Default to `ORDER BY id DESC` or `created_at DESC`; `mb_*` for UTF-8 safety148- `defer()` for post-response work; `Context` for request-scoped data; `Concurrency::run()` for parallel execution149150### 16. Migrations → `rules/migrations.md`151152- Generate migrations with `php artisan make:migration`153- `constrained()` for foreign keys154- Never modify migrations that have run in production155- Add indexes in the migration, not as an afterthought156- Mirror column defaults in model `$attributes`157- Reversible `down()` by default; forward-fix migrations for intentionally irreversible changes158- One concern per migration — never mix DDL and DML159160### 17. Collections → `rules/collections.md`161162- Higher-order messages for simple collection operations163- `cursor()` vs. `lazy()` — choose based on relationship needs164- `lazyById()` when updating records while iterating165- `toQuery()` for bulk operations on collections166167### 18. Blade & Views → `rules/blade-views.md`168169- `$attributes->merge()` in component templates170- Blade components over `@include`; `@pushOnce` for per-component scripts171- View Composers for shared view data172- `@aware` for deeply nested component props173174### 19. Conventions & Style → `rules/style.md`175176- Follow Laravel naming conventions for all entities177- Prefer Laravel helpers (`Str`, `Arr`, `Number`, `Uri`, `Str::of()`, `$request->string()`) over raw PHP functions178- No JS/CSS in Blade, no HTML in PHP classes179- Code should be readable; comments only for config files180181## How to Apply182183Always use a sub-agent to read rule files and explore this skill's content.1841851. Identify the file type and select relevant sections (e.g., migration → §16, controller → §1, §3, §5, §6, §10)1862. Check sibling files for existing patterns — follow those first per Consistency First1873. Verify API syntax with `search-docs` for the installed Laravel version188189---190> Source: [lartisan/filament-architect](https://github.com/lartisan/filament-architect) — distributed by [TomeVault](https://tomevault.io).191<!-- tomevault:4.0:skill_md:2026-06-21 -->