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
// app/Domain/Users/Actions/CreateUserAction.php
<?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
// 安全:使用 Eloquent 或参数化查询
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
// 使用 Eager Loading 避免 N+1
$posts = Post::with(['author', 'comments.user'])->get();

// 选择必要字段
$users = User::select(['id', 'name', 'email'])->get();

// 使用 chunk 处理大数据
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);
}
}

// 全局异常处理
// app/Exceptions/Handler.php
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
/**
* @OA\Get(
* path="/api/posts",
* summary="List posts",
* @OA\Response(response="200", description="Post list")
* )
*/
public function index()
{
// ...
}

8.2 代码注释

1
2
3
4
5
6
7
8
9
10
11
/**
* 计算用户积分
*
* @param User $user 用户实例
* @param int $amount 积分数量
* @return bool 是否成功
*/
public function addCredits(User $user, int $amount): bool
{
// ...
}

9. 总结

Laravel 13 最佳实践的核心原则:

  1. 单一职责:每个类只做一件事
  2. 依赖注入:使用容器管理依赖
  3. 接口编程:面向接口而非实现
  4. 测试驱动:编写可测试的代码
  5. 安全优先:始终验证和授权

遵循这些最佳实践,可以构建高质量、可维护的 Laravel 应用。

参考资料