laravel-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLaravel 12 Best Practices
Laravel 12 最佳实践
Comprehensive best practices guide for Laravel 12 applications. Contains 45+ rules across 8 categories for building scalable, maintainable Laravel applications.
这是一份针对Laravel 12应用的全面最佳实践指南,包含8个类别下的45+条规则,助力构建可扩展、可维护的Laravel应用。
When to Apply
适用场景
Reference these guidelines when:
- Creating controllers, models, and services
- Writing migrations and database queries
- Implementing validation and form requests
- Building APIs with Laravel
- Structuring Laravel applications
在以下场景中可参考本指南:
- 创建控制器、模型和服务时
- 编写迁移和数据库查询时
- 实现验证和表单请求时
- 使用Laravel构建API时
- 构建Laravel应用架构时
Rule Categories by Priority
按优先级划分的规则类别
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Architecture & Structure | CRITICAL | |
| 2 | Eloquent & Database | CRITICAL | |
| 3 | Controllers & Routing | HIGH | |
| 4 | Validation & Requests | HIGH | |
| 5 | Security | HIGH | |
| 6 | Performance | MEDIUM | |
| 7 | API Design | MEDIUM | |
| 优先级 | 类别 | 影响程度 | 前缀 |
|---|---|---|---|
| 1 | 架构与结构 | 关键 | |
| 2 | Eloquent与数据库 | 关键 | |
| 3 | 控制器与路由 | 高 | |
| 4 | 验证与请求 | 高 | |
| 5 | 安全 | 高 | |
| 6 | 性能 | 中 | |
| 7 | API设计 | 中 | |
Quick Reference
快速参考
1. Architecture & Structure (CRITICAL)
1. 架构与结构(关键)
- - Extract business logic to services
arch-service-classes - - Single-purpose action classes
arch-action-classes - - When to use repositories
arch-repository-pattern - - Data transfer objects
arch-dto-pattern - - Encapsulate domain concepts
arch-value-objects - - Decouple with events and listeners
arch-event-driven - - Organize by domain/feature
arch-feature-folders
- - 将业务逻辑提取到服务层
arch-service-classes - - 单一职责的动作类
arch-action-classes - - 仓库模式的适用场景
arch-repository-pattern - - 数据传输对象
arch-dto-pattern - - 封装领域概念
arch-value-objects - - 利用事件和监听器实现解耦
arch-event-driven - - 按领域/功能组织代码
arch-feature-folders
2. Eloquent & Database (CRITICAL)
2. Eloquent与数据库(关键)
- - Prevent N+1 queries
eloquent-eager-loading - - Process large datasets
eloquent-chunking - - Reusable query logic
eloquent-query-scopes - - Use observers for side effects
eloquent-model-events - - Define relationships properly
eloquent-relationships - - Automatic attribute casting
eloquent-casts - - Transform attributes
eloquent-accessors-mutators - - Safe deletion with recovery
eloquent-soft-deletes - - Automatic cleanup of old records
eloquent-pruning
- - 避免N+1查询问题
eloquent-eager-loading - - 处理大型数据集
eloquent-chunking - - 可复用的查询逻辑
eloquent-query-scopes - - 使用观察者处理副作用
eloquent-model-events - - 正确定义关联关系
eloquent-relationships - - 自动属性类型转换
eloquent-casts - - 属性转换处理
eloquent-accessors-mutators - - 安全删除与恢复
eloquent-soft-deletes - - 自动清理旧记录
eloquent-pruning
3. Controllers & Routing (HIGH)
3. 控制器与路由(高)
- - Use resource controllers
ctrl-resource-controllers - - Single action invokable controllers
controller-single-action - - RESTful resource methods
controller-resource-methods - - Use form requests
controller-form-requests - - Transform API responses
controller-api-resources - - Apply middleware properly
controller-middleware - - Inject dependencies
controller-dependency-injection
- - 使用资源控制器
ctrl-resource-controllers - - 单一动作的可调用控制器
controller-single-action - - RESTful资源方法
controller-resource-methods - - 使用表单请求
controller-form-requests - - 转换API响应
controller-api-resources - - 正确应用中间件
controller-middleware - - 依赖注入
controller-dependency-injection
4. Validation & Requests (HIGH)
4. 验证与请求(高)
- - Use form request classes
validation-form-requests - - Create custom rules
validation-custom-rules - - Conditional validation
validation-conditional-rules - - Validate nested arrays
validation-array-validation - - Complex validation logic
validation-after-hooks
- - 使用表单请求类
validation-form-requests - - 创建自定义验证规则
validation-custom-rules - - 条件验证
validation-conditional-rules - - 嵌套数组验证
validation-array-validation - - 复杂验证逻辑
validation-after-hooks
5. Security (HIGH)
5. 安全(高)
- - Protect against mass assignment
sec-mass-assignment - Additional security rules can be added as needed
- - 防范批量赋值漏洞
sec-mass-assignment - 可根据需要添加更多安全规则
6. Performance (MEDIUM)
6. 性能(中)
- Performance rules can be added for caching, queues, and optimization
- 可添加缓存、队列和优化相关的性能规则
7. API Design (MEDIUM)
7. API设计(中)
- API design rules can be added for versioning and response formatting
- 可添加版本控制和响应格式相关的API设计规则
Essential Patterns
核心模式
Controller with Form Request
结合表单请求的控制器
php
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StorePostRequest;
use App\Http\Requests\UpdatePostRequest;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
class PostController extends Controller
{
public function store(StorePostRequest $request): RedirectResponse
{
// Validation happens automatically
$validated = $request->validated();
$post = Post::create($validated);
return redirect()
->route('posts.show', $post)
->with('success', 'Post created successfully.');
}
public function update(UpdatePostRequest $request, Post $post): RedirectResponse
{
$post->update($request->validated());
return redirect()
->route('posts.show', $post)
->with('success', 'Post updated successfully.');
}
}php
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StorePostRequest;
use App\Http\Requests\UpdatePostRequest;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
class PostController extends Controller
{
public function store(StorePostRequest $request): RedirectResponse
{
// 验证自动执行
$validated = $request->validated();
$post = Post::create($validated);
return redirect()
->route('posts.show', $post)
->with('success', 'Post created successfully.');
}
public function update(UpdatePostRequest $request, Post $post): RedirectResponse
{
$post->update($request->validated());
return redirect()
->route('posts.show', $post)
->with('success', 'Post updated successfully.');
}
}Form Request Class
表单请求类
php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('create', Post::class);
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'body' => ['required', 'string', 'min:100'],
'category_id' => ['required', 'exists:categories,id'],
'tags' => ['nullable', 'array'],
'tags.*' => ['exists:tags,id'],
'published_at' => ['nullable', 'date', 'after:now'],
];
}
public function messages(): array
{
return [
'body.min' => 'The post body must be at least 100 characters.',
];
}
}php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('create', Post::class);
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'body' => ['required', 'string', 'min:100'],
'category_id' => ['required', 'exists:categories,id'],
'tags' => ['nullable', 'array'],
'tags.*' => ['exists:tags,id'],
'published_at' => ['nullable', 'date', 'after:now'],
];
}
public function messages(): array
{
return [
'body.min' => 'The post body must be at least 100 characters.',
];
}
}Service Class Pattern
服务类模式
php
<?php
namespace App\Services;
use App\Models\User;
use App\Models\Post;
use App\Events\PostPublished;
use Illuminate\Support\Facades\DB;
class PostService
{
public function __construct(
private readonly NotificationService $notifications,
) {}
public function publish(Post $post): Post
{
return DB::transaction(function () use ($post) {
$post->update([
'published_at' => now(),
'status' => 'published',
]);
event(new PostPublished($post));
$this->notifications->notifyFollowers($post->author, $post);
return $post->fresh();
});
}
}php
<?php
namespace App\Services;
use App\Models\User;
use App\Models\Post;
use App\Events\PostPublished;
use Illuminate\Support\Facades\DB;
class PostService
{
public function __construct(
private readonly NotificationService $notifications,
) {}
public function publish(Post $post): Post
{
return DB::transaction(function () use ($post) {
$post->update([
'published_at' => now(),
'status' => 'published',
]);
event(new PostPublished($post));
$this->notifications->notifyFollowers($post->author, $post);
return $post->fresh();
});
}
}Eloquent Model
Eloquent模型
php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Builder;
class Post extends Model
{
use HasFactory;
protected $fillable = [
'title',
'slug',
'body',
'category_id',
'published_at',
];
protected $casts = [
'published_at' => 'datetime',
];
// Relationships
public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class)->withTimestamps();
}
// Scopes
public function scopePublished(Builder $query): Builder
{
return $query->whereNotNull('published_at')
->where('published_at', '<=', now());
}
public function scopeByCategory(Builder $query, int $categoryId): Builder
{
return $query->where('category_id', $categoryId);
}
// Accessors & Mutators
protected function title(): Attribute
{
return Attribute::make(
set: fn (string $value) => ucfirst($value),
);
}
}php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Builder;
class Post extends Model
{
use HasFactory;
protected $fillable = [
'title',
'slug',
'body',
'category_id',
'published_at',
];
protected $casts = [
'published_at' => 'datetime',
];
// 关联关系
public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class)->withTimestamps();
}
// 查询作用域
public function scopePublished(Builder $query): Builder
{
return $query->whereNotNull('published_at')
->where('published_at', '<=', now());
}
public function scopeByCategory(Builder $query, int $categoryId): Builder
{
return $query->where('category_id', $categoryId);
}
// 访问器与修改器
protected function title(): Attribute
{
return Attribute::make(
set: fn (string $value) => ucfirst($value),
);
}
}Migration Best Practices
迁移最佳实践
php
<?php
use 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('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('category_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->string('slug')->unique();
$table->text('body');
$table->timestamp('published_at')->nullable();
$table->timestamps();
// Indexes for common queries
$table->index(['user_id', 'published_at']);
$table->index('category_id');
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};php
<?php
use 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('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('category_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->string('slug')->unique();
$table->text('body');
$table->timestamp('published_at')->nullable();
$table->timestamps();
// 为常用查询添加索引
$table->index(['user_id', 'published_at']);
$table->index('category_id');
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};Eager Loading
预加载
php
// ❌ N+1 Problem
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // Query per post
}
// ✅ Eager loading
$posts = Post::with(['author', 'category', 'tags'])->get();
foreach ($posts as $post) {
echo $post->author->name; // No additional queries
}
// ✅ Nested eager loading
$posts = Post::with([
'author.profile',
'comments.user',
'tags',
])->get();
// ✅ Constrained eager loading
$posts = Post::with([
'comments' => fn ($query) => $query->latest()->limit(5),
])->get();php
// ❌ N+1 查询问题
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // 每个帖子触发一次查询
}
// ✅ 预加载
$posts = Post::with(['author', 'category', 'tags'])->get();
foreach ($posts as $post) {
echo $post->author->name; // 无额外查询
}
// ✅ 嵌套预加载
$posts = Post::with([
'author.profile',
'comments.user',
'tags',
])->get();
// ✅ 条件预加载
$posts = Post::with([
'comments' => fn ($query) => $query->latest()->limit(5),
])->get();How to Use
使用方法
Read individual rule files for detailed explanations and code examples:
rules/arch-service-classes.md
rules/eloquent-eager-loading.md
rules/validation-form-requests.md
rules/_sections.mdEach rule file contains:
- YAML frontmatter with metadata (title, impact, tags)
- Brief explanation of why it matters
- Incorrect code example with explanation
- Correct code example with explanation
- Laravel 12 and PHP 8.5 specific context and references
阅读单个规则文件获取详细说明和代码示例:
rules/arch-service-classes.md
rules/eloquent-eager-loading.md
rules/validation-form-requests.md
rules/_sections.md每个规则文件包含:
- 带元数据的YAML前置内容(标题、影响程度、标签)
- 规则重要性的简要说明
- 错误代码示例及解释
- 正确代码示例及解释
- Laravel 12和PHP 8.5的特定上下文与参考
Full Compiled Document
完整编译文档
For the complete guide with all rules expanded:
AGENTS.md包含所有扩展规则的完整指南:
AGENTS.md