Laravel Best Practices
LaravelOfficialCreated by laravel
- Skill
- Guidelines
- Software development
- MIT
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Laravel best practices provides a structured set of guidelines for writing, reviewing, and refactoring Laravel applications. It emphasizes maintaining consistency with existing codebase patterns while offering standardized approaches for common concerns such as database performance, Eloquent usage, security, validation, and architectural design. The skill acts as a reference index, directing developers to specific rule files based on the task at hand.
What it does
- Provides a categorized index of rules for various Laravel components.
- Promotes consistency by prioritizing existing project patterns over new abstractions.
- Offers guidance on database performance, including eager loading and query optimization.
- Covers security, validation, routing, and error handling patterns.
- Advises on architectural decisions, service classes, and job management.
- Encourages the use of native framework features over custom helpers.
When to use it
- When writing new controllers, models, or service classes in Laravel.
- During code reviews to ensure alignment with established framework patterns.
- When refactoring existing code to improve maintainability or security.
- When addressing specific issues like N+1 queries or complex Eloquent relationships.
- When making architectural decisions regarding application structure.
Advantages
- Ensures architectural consistency across the codebase.
- Reduces technical debt by discouraging speculative abstractions.
- Provides clear, domain-specific guidance for common Laravel tasks.
- Helps prevent common pitfalls like hidden N+1 queries and security vulnerabilities.
Usage examples
- “How should I structure my Eloquent queries to avoid N+1 issues?”
- The agent will reference the database performance and Eloquent rule files to suggest eager loading strategies and proper relationship definitions.
- “Review this controller for security and validation best practices.”
- The agent will evaluate the code against the security and validation rule files, suggesting improvements for input handling and authorization.
- “Should I create a new helper function for this logic?”
- The agent will advise on whether to use existing framework features or if the logic warrants a new service class based on architectural guidelines.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/laravel/laravel-best-practices/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip laravel-best-practices.zip -d .agents/skills
Documentation by laravel
Laravel best practices
Best practices for Laravel, organized as an index of rule files. Each rule file 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 several valid approaches. The best choice is the one the codebase already uses, even if another pattern would be better in theory. Inconsistency is worse than a suboptimal pattern.
Check sibling files and 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.
How to apply
Check the changed files, nearby code, project configuration, and relevant tests for established patterns. Deviate only for a correctness or security defect, and call the deviation out.
Map every affected concern to the rule index below. Read each mapped rule file before editing. Skip unrelated rule files.
Make the smallest coherent change. Keep the application's architecture and naming instead of introducing a second pattern for the same job.
Verify version-sensitive Laravel APIs for the installed version with
search-docs, or inspect the installed framework whensearch-docsis unavailable.Run the narrowest relevant tests first, then the project's formatting and static-analysis checks when the change warrants them.
Re-read the diff against every mapped rule before finishing.
Rule index
Cross-cutting changes often need more than one rule file.
| Concern | Read |
|---|---|
| Query count, eager loading, indexes, large datasets | rules/db-performance.md |
| Subqueries, aggregates, complex ordering and query plans | rules/advanced-queries.md |
| Models, relationships, scopes, casts | rules/eloquent.md |
| Authentication, authorization, input safety, secrets, uploads | rules/security.md |
| Form Requests and validation rules | rules/validation.md |
| Controllers, route binding, resources, middleware | rules/routing.md |
| Schema changes, columns, foreign keys, indexes | rules/migrations.md |
| Jobs, retries, uniqueness, batches, Horizon | rules/queue-jobs.md |
| Cache lifetime, invalidation, locks, memoization | rules/caching.md |
| Outbound requests, retries, timeouts, fakes | rules/http-client.md |
| Exceptions, reporting, rendering, log context | rules/error-handling.md |
| Events and notifications | rules/events-notifications.md |
| Mailables and mail assertions | rules/mail.md |
| Scheduled tasks and overlap protection | rules/scheduling.md |
| Collections, lazy iteration, bulk operations | rules/collections.md |
| Blade components, attributes, composers | rules/blade-views.md |
| Environment values and application configuration | rules/config.md |
| Tests: coverage, factories, fakes, and assertions | the testing-best-practices skill |
| Naming, helpers, file boundaries, PHP style | rules/style.md |
| Actions, services, dependencies, application structure | rules/architecture.md |
Decision rules
Prefer framework features and existing application abstractions over new helpers or dependencies.
Avoid speculative abstractions. Extract code when it creates a clear domain boundary, removes meaningful duplication, or makes behavior independently testable.
Keep database access out of Blade views and prevent hidden N+1 queries across controllers, resources, jobs, and serialization.
Files
- ATTRIBUTION.mdAdded by AutoGineer924 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md4.5 KiB
- rules/advanced-queries.md4.2 KiB
- rules/architecture.md6.3 KiB
- rules/blade-views.md1.8 KiB
- rules/caching.md3.3 KiB
- rules/collections.md2.4 KiB
- rules/config.blade.phpScript2.1 KiB
- rules/db-performance.md5.5 KiB
- rules/eloquent.md3.9 KiB
- rules/error-handling.md3.1 KiB
- rules/events-notifications.blade.phpScript2.6 KiB
- rules/http-client.md4.4 KiB
- rules/mail.blade.phpScript2.0 KiB
- rules/migrations.blade.phpScript3.5 KiB
- rules/queue-jobs.md4.3 KiB
- rules/routing.md3.7 KiB
- rules/scheduling.blade.phpScript2.7 KiB
- rules/security.blade.phpScript5.8 KiB
- rules/style.md4.2 KiB
- rules/validation.md2.9 KiB
rules/db-performance.md
Database performance best practices
Eager load relationships before iterating
When code accesses a relationship on many models, eager load the relationship with with(). Eager loading avoids the N+1 query pattern: one initial query plus one relationship query per model. Lazy loading is reasonable when the code may not need the relationship or when only one model is involved.
Lazy-loaded version:
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name;
}Eager-loaded version:
$posts = Post::with('author')->get();
foreach ($posts as $post) {
echo $post->author->name;
}Constrain an eager load when the code does not need the large columns. Include the related model's primary key and every column Eloquent needs to match the relationship. In this example, users.id and posts.user_id match posts to users, and selecting posts.id keeps each related model's primary key:
$users = User::with(['posts' => function ($query) {
$query->select('id', 'user_id', 'title')
->where('published', true)
->latest()
->limit(10);
}])->get();Prevent lazy loading in development
Enable lazy-loading prevention in AppServiceProvider::boot() to catch N+1 queries during development.
public function boot(): void
{
Model::preventLazyLoading(! app()->isProduction());
}With prevention enabled, accessing an unloaded relationship throws a LazyLoadingViolationException by default. To customize violation handling, use handleLazyLoadingViolationUsing().
Select only the columns you need
When omitting large text, binary, or JSON columns gives a meaningful benefit, select only the columns the operation needs.
All columns:
$posts = Post::with('author')->get();Selected columns:
$posts = Post::select('id', 'title', 'user_id', 'created_at')
->with(['author:id,name,avatar'])
->get();When you limit the selected columns, keep every key Eloquent needs for matching. A belongsTo relationship needs its foreign key on the parent query and the owner's key on the related query. A hasMany relationship needs the parent's local key and the related model's foreign key.
Process large data sets incrementally
When loading the entire result set would exceed the application's practical memory budget, use chunking or lazy iteration.
Loads the complete result set:
$users = User::all();
foreach ($users as $user) {
$user->notify(new WeeklyDigest);
}Processes bounded chunks:
User::where('subscribed', true)->chunk(200, function ($users) {
foreach ($users as $user) {
$user->notify(new WeeklyDigest);
}
});Use chunkById() when updates can change which rows match the query. Standard chunk() uses offset pagination, so result positions can shift as rows change:
User::where('active', false)->chunkById(200, function ($users) {
$users->each->delete();
});For read-only iteration that uses only model attributes, cursor() runs one query and hydrates one model at a time. Some database drivers still buffer the raw results. Use lazy() when you need to eager load relationships in chunks. Use lazyById() or chunkById() when updates can change which rows match the query. See the collection rules for detailed tradeoffs.
Add indexes for measured query patterns
Design indexes around frequent, performance-sensitive query patterns. A column's presence in WHERE, ORDER BY, JOIN, or GROUP BY does not by itself justify an index. Selectivity, write cost, existing indexes, and the database query plan all matter.
Schema without an application-specific query index:
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('status');
$table->timestamps();
});Schema optimized for WHERE status = ? ORDER BY created_at:
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->string('status');
$table->timestamps();
$table->index(['status', 'created_at']);
});Use production-like data and the database's query-plan tools to confirm the column order of a composite index and whether the index helps. Before you add an index for a foreign key, check whether the database already created one.
Count relationships without loading them
Use withCount() when you need only the relationship counts. Loading and hydrating every related model wastes memory.
Loads related models:
$posts = Post::all();
foreach ($posts as $post) {
echo $post->comments->count();
}Selects relationship counts:
$posts = Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}Conditional counting:
$posts = Post::withCount([
'comments',
'comments as approved_comments_count' => function ($query) {
$query->where('approved', true);
},
])->get();Keep queries out of Blade templates
Prepare data before rendering a Blade template, for example in a controller, a query service, or a view composer. Queries outside the template stay visible and testable.
Query in the template:
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeachData prepared before rendering:
// Controller
$users = User::with('profile')->get();
return view('users.index', compact('users'));@foreach ($users as $user)
{{ $user->profile->name }}
@endforeachRequirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
What our scanner noticed· Low risk
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- laravel
- Publisher
- Laravel (Official)
- Source
- laravel/boost · .ai/laravel/skill/laravel-best-practices · commit 5f8d05d
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by laravel or Laravel.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download laravel-best-practices.zip