Laravel API 开发实战:从零到完整任务管理系统

记录 Laravel API 开发过程中的关键知识点和常见陷阱,帮助你避免踩坑。

项目背景

技术栈:

  • Laravel 11
  • Laravel Sanctum(API Token 认证)
  • MySQL 数据库
  • PHPUnit 测试

项目功能:

  • 用户注册、登录、登出
  • 任务 CRUD(创建、列表、删除)
  • API Token 认证
  • 分页查询

核心知识点

Laravel Sanctum 认证机制

1. 安装与配置

1
2
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

2. User Model 必须引入 HasApiTokens trait

❌ 错误示例:

1
2
3
4
5
class User extends Authenticatable
{
use HasFactory, Notifiable;
// 缺少 HasApiTokens
}

调用 createToken() 时会报错:Call to undefined method App\Models\User::createToken()

✅ 正确写法:

1
2
3
4
5
6
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}

原因: createToken() 方法定义在 HasApiTokens trait 中,必须显式引入。

3. Token 认证流程

注册时创建 Token:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function register(UserRequest $request)
{
$user = User::create($validated);
$token = $user->createToken('api-token')->plainTextToken;

return response()->json([
'code' => 0,
'msg' => '注册成功',
'data' => [
'user' => new UserResource($user),
'token' => $token,
],
]);
}

登出时删除 Token:

1
2
3
4
5
6
7
8
9
public function logout(Request $request)
{
$request->user()->currentAccessToken()->delete();

return response()->json([
'code' => 0,
'msg' => '登出成功',
]);
}

Form Request 验证

1. 创建 Form Request

1
2
3
php artisan make:request UserRequest
php artisan make:request LoginRequest
php artisan make:request TaskRequest

2. Controller 中使用

1
2
3
4
5
6
7
8
9
10
11
public function store(TaskRequest $request)
{
// 验证通过后,通过 $request->validated() 获取验证后的数据
$task = $request->user()->tasks()->create($request->validated());

return response()->json([
'code' => 0,
'msg' => '创建成功',
'data' => new TaskResource($task),
]);
}

好处:

  • 验证逻辑与 Controller 分离
  • 自动返回 422 错误响应
  • 代码更清晰、易维护

Resource 资源类

1. 定义数据格式

1
2
3
4
5
6
7
8
9
10
11
12
13
class TaskResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'description' => $this->description,
'completed' => (bool) $this->completed, // 强制转换布尔值
'created_at' => $this->created_at->toIso8601String(), // 统一时间格式
];
}
}

2. Controller 中使用

单个资源:

1
2
3
4
5
return response()->json([
'code' => 0,
'msg' => '创建成功',
'data' => new TaskResource($task),
]);

资源集合(列表):

1
2
3
4
5
6
7
$tasks = auth()->user()->tasks()->paginate(5);

return TaskResource::collection($tasks)
->additional([
'code' => 0,
'msg' => '获取任务列表成功',
]);

Eloquent 关系查询

1. Model 关系定义

1
2
3
4
5
6
7
8
9
10
11
// User.php
public function tasks()
{
return $this->hasMany(Task::class);
}

// Task.php
public function user()
{
return $this->belongsTo(User::class);
}

2. 使用关系创建任务

✅ 推荐写法:

1
2
3
4
5
6
7
8
9
10
public function store(TaskRequest $request)
{
$task = $request->user()->tasks()->create($request->validated());

return response()->json([
'code' => 0,
'msg' => '创建成功',
'data' => new TaskResource($task),
]);
}

好处:

  • 自动设置 user_id
  • 代码更简洁
  • 符合 Laravel 最佳实践

3. 查询当前用户的任务

✅ 正确写法:

1
2
3
4
5
6
7
8
9
10
public function index(Request $request)
{
$perPage = $request->query('per_page', 5);
$tasks = Task::where('user_id', $request->user()->id)
->latest()
->paginate($perPage);

return TaskResource::collection($tasks)
->additional(['code' => 0, 'msg' => '获取任务列表成功']);
}

或者使用关系查询:

1
$tasks = $request->user()->tasks()->latest()->paginate($perPage);

API 响应格式设计

1. 统一响应格式

场景 响应格式
成功(无数据) {"code": 0, "msg": "操作成功"}
成功(有数据) {"code": 0, "msg": "获取成功", "data": {...}}
成功(列表) {"data": [...], "meta": {...}, "code": 0, "msg": "获取成功"}
验证错误 {"code": 422, "msg": "错误信息", "errors": {...}}
认证失败 {"code": 401, "msg": "未认证"}

2. 关键问题:code 类型必须统一

前端判断:if (data.code === 0) 使用严格相等

❌ 错误: 返回字符串 "0" —— "0" === 0 返回 false

✅ 正确: 返回数字 0

3. 关键问题:key 名称必须统一

❌ 错误: 后端返回 message,前端期望 msg

✅ 正确: 统一使用 msg

常见错误与解决方案

错误 1:Call to undefined method User::createToken()

原因: User Model 缺少 HasApiTokens trait。

解决方案:

1
2
3
4
5
6
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}

错误 2:用户 A 能看到用户 B 的任务

原因: 查询时未过滤 user_id

解决方案:

1
$tasks = Task::where('user_id', $request->user()->id)->paginate(5);

错误 3:where 条件误用 like

❌ 错误:

1
Task::where('user_id', 'like', $request->user()->id)

原因: like 用于字符串模糊匹配,user_id 是整数。

✅ 正确:

1
Task::where('user_id', $request->user()->id)

错误 4:删除任务时访问 null 属性报错

✅ 正确顺序: 先检查是否存在,再检查权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public function destroy(Request $request, string $id)
{
$task = Task::find($id);

// 先检查是否存在
if (!$task) {
return response()->json(['code' => 404, 'msg' => '任务不存在'], 404);
}

// 再检查权限
if ($task->user_id != $request->user()->id) {
return response()->json(['code' => 403, 'msg' => '无权操作'], 403);
}

$task->delete();

return response()->json(['code' => 0, 'msg' => '删除成功']);
}

最佳实践总结

Laravel API 开发的关键在于:

  1. 认证机制:理解 Sanctum 的工作原理,正确配置 HasApiTokens trait
  2. 响应格式:统一 code 类型和 key 名称,避免前端判断失败
  3. 数据查询:正确使用 Eloquent 关系,确保只查询当前用户的数据
  4. 异常处理:配置 API 异常处理,返回规范的 JSON 响应
  5. 代码规范:方法命名统一、引入必要依赖、逻辑顺序正确

遵循这些最佳实践,可以避免 90% 的常见错误,写出高质量的 Laravel API。

参考资料