Laravel 13 最佳实践完全指南
摘要
本文汇总 Laravel 13 开发的最佳实践,涵盖代码规范、架构设计、性能优化等多个方面。内容包括:
- 项目结构与组织
- 代码规范与风格
- 架构设计模式
- 安全最佳实践
- 测试策略
- 部署与运维
本文适合希望提升代码质量的 Laravel 开发者。
1. 项目结构
1.1 推荐目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| app/ ├── Actions/ # 单一职责动作类 ├── Concerns/ # 可复用 Traits ├── Contracts/ # 接口定义 ├── Domain/ # 领域逻辑 │ ├── Users/ │ │ ├── Actions/ │ │ ├── Models/ │ │ └── Services/ │ └── Orders/ ├── Http/ │ ├── Controllers/ │ ├── Middleware/ │ └── Requests/ ├── Models/ ├── Policies/ ├── Providers/ └── Services/
|
1.2 模块化设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php
namespace App\Domain\Users\Actions;
use App\Models\User; use Illuminate\Support\Facades\Hash;
class CreateUserAction { public function execute(array $data): User { return User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); } }
|
2. 代码规范
2.1 控制器规范
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <?php
namespace App\Http\Controllers;
use App\Http\Requests\StorePostRequest; use App\Http\Resources\PostResource; use App\Models\Post; use Illuminate\Http\JsonResponse;
class PostController extends Controller { public function index(): JsonResponse { $posts = Post::query() ->published() ->with(['author', 'tags']) ->paginate(); return PostResource::collection($posts) ->response(); } public function store(StorePostRequest $request): JsonResponse { $post = Post::create($request->validated()); return (new PostResource($post)) ->response() ->setStatusCode(201); } }
|
2.2 模型规范
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model { use SoftDeletes; protected $fillable = [ 'title', 'content', 'status', 'author_id', ]; protected $casts = [ 'published_at' => 'datetime', 'status' => PostStatus::class, ]; public function author(): BelongsTo { return $this->belongsTo(User::class, 'author_id'); } public function comments(): HasMany { return $this->hasMany(Comment::class); } public function scopePublished($query) { return $query->where('status', PostStatus::Published); } }
|
2.3 请求验证规范
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule;
class StorePostRequest extends FormRequest { public function authorize(): bool { return true; } public function rules(): array { return [ 'title' => ['required', 'string', 'max:255'], 'content' => ['required', 'string'], 'status' => ['required', Rule::in(['draft', 'published'])], 'tags' => ['array'], 'tags.*' => ['exists:tags,id'], ]; } public function messages(): array { return [ 'title.required' => '标题不能为空', 'content.required' => '内容不能为空', ]; } }
|
3. 架构模式
3.1 Action 模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?php
namespace App\Actions;
use App\Models\User; use Illuminate\Support\Facades\DB;
class TransferCreditsAction { public function execute(User $from, User $to, int $amount): void { DB::transaction(function () use ($from, $to, $amount) { $from->decrement('credits', $amount); $to->increment('credits', $amount); Transaction::create([ 'from_user_id' => $from->id, 'to_user_id' => $to->id, 'amount' => $amount, ]); }); } }
|
3.2 服务类模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?php
namespace App\Services;
use App\Models\User; use Illuminate\Support\Facades\Hash;
class UserService { public function register(array $data): User { $user = User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); $this->sendWelcomeEmail($user); return $user; } private function sendWelcomeEmail(User $user): void { } }
|
3.3 仓储模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php
namespace App\Repositories;
use App\Models\User; use Illuminate\Contracts\Pagination\LengthAwarePaginator;
interface UserRepositoryInterface { public function find(int $id): ?User; public function findByEmail(string $email): ?User; public function paginate(int $perPage = 15): LengthAwarePaginator; public function create(array $data): User; public function update(User $user, array $data): bool; public function delete(User $user): bool; }
|
4. 安全最佳实践
4.1 输入验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| $validated = $request->validate([ 'email' => 'required|email|max:255', 'password' => 'required|string|min:8|confirmed', ]);
class RegisterRequest extends FormRequest { public function rules(): array { return [ 'email' => ['required', 'email', 'unique:users'], 'password' => ['required', 'confirmed', Password::defaults()], ]; } }
|
4.2 授权检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class PostPolicy { public function update(User $user, Post $post): bool { return $user->id === $post->author_id; } }
public function update(Post $post) { $this->authorize('update', $post); }
|
4.3 SQL 注入防护
1 2 3 4 5 6 7
| User::where('email', $email)->first();
DB::select('SELECT * FROM users WHERE email = ?', [$email]);
DB::select("SELECT * FROM users WHERE email = '{$email}'");
|
5. 测试策略
5.1 测试金字塔
1 2 3 4 5 6 7
| /\ / \ E2E 测试 (10%) /----\ / \ 集成测试 (20%) /--------\ / \ 单元测试 (70%) /------------\
|
5.2 单元测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?php
namespace Tests\Unit;
use App\Services\Calculator; use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase { private Calculator $calculator; protected function setUp(): void { parent::setUp(); $this->calculator = new Calculator(); } public function test_addition(): void { $result = $this->calculator->add(2, 3); $this->assertEquals(5, $result); } }
|
5.3 功能测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <?php
namespace Tests\Feature;
use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase;
class PostTest extends TestCase { use RefreshDatabase; public function test_user_can_create_post(): void { $user = User::factory()->create(); $response = $this->actingAs($user) ->postJson('/api/posts', [ 'title' => 'Test Post', 'content' => 'Test Content', ]); $response->assertCreated() ->assertJsonPath('data.title', 'Test Post'); $this->assertDatabaseHas('posts', [ 'title' => 'Test Post', 'author_id' => $user->id, ]); } }
|
6. 性能优化
6.1 查询优化
1 2 3 4 5 6 7 8 9 10 11 12
| $posts = Post::with(['author', 'comments.user'])->get();
$users = User::select(['id', 'name', 'email'])->get();
User::chunk(1000, function ($users) { foreach ($users as $user) { } });
|
6.2 缓存策略
1 2 3 4 5 6 7
| $users = Cache::remember('active_users', 3600, function () { return User::where('active', true)->get(); });
Cache::tags(['users'])->flush();
|
6.3 队列使用
1 2 3 4
| ProcessPodcast::dispatch($podcast) ->onQueue('podcasts') ->delay(now()->addMinutes(5));
|
7. 错误处理
7.1 异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class InsufficientCreditsException extends Exception { public function render() { return response()->json([ 'message' => 'Insufficient credits', ], 402); } }
public function render($request, Throwable $e) { if ($e instanceof InsufficientCreditsException) { return $e->render(); } return parent::render($request, $e); }
|
7.2 日志记录
1 2 3 4 5
| use Illuminate\Support\Facades\Log;
Log::info('User registered', ['user_id' => $user->id]); Log::error('Payment failed', ['error' => $e->getMessage()]); Log::channel('slack')->critical('System down');
|
8. 文档与注释
8.1 API 文档
1 2 3 4 5 6 7 8 9 10 11
|
public function index() { }
|
8.2 代码注释
1 2 3 4 5 6 7 8 9 10 11
|
public function addCredits(User $user, int $amount): bool { }
|
9. 总结
Laravel 13 最佳实践的核心原则:
- 单一职责:每个类只做一件事
- 依赖注入:使用容器管理依赖
- 接口编程:面向接口而非实现
- 测试驱动:编写可测试的代码
- 安全优先:始终验证和授权
遵循这些最佳实践,可以构建高质量、可维护的 Laravel 应用。
参考资料