Laravel 13 PHP Attributes 完全指南

摘要

Laravel 13 大幅扩展了 PHP 8 Attributes 的支持,新增 36 个属性,让配置更加声明式和直观。本文将全面解析 Laravel 13 中的 PHP Attributes 应用,包括:

  • PHP Attributes 核心概念与语法
  • Laravel 13 新增属性一览
  • 控制器属性详解
  • 路由属性应用
  • 验证属性使用
  • 自定义属性创建

本文适合希望采用声明式配置风格的 Laravel 开发者。

1. PHP Attributes 概述

1.1 什么是 PHP Attributes

PHP Attributes 是 PHP 8.0 引入的特性,允许在类、方法、属性等声明上添加结构化元数据。Laravel 13 充分利用这一特性,将传统配置方式转变为声明式风格。

1.2 传统方式 vs Attributes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 传统方式:路由定义在 routes/web.php
Route::get('/users', [UserController::class, 'index'])
->middleware('auth')
->name('users.index');

// Laravel 13 Attributes 方式
class UserController extends Controller
{
#[Get('/users', name: 'users.index')]
#[Middleware('auth')]
public function index()
{
// ...
}
}

1.3 核心优势

优势描述
配置集中配置与代码放在一起
可读性强一目了然的声明式风格
IDE 友好更好的代码提示和导航
类型安全编译时检查
减少样板代码减少配置文件内容

2. Laravel 13 新增属性一览

2.1 控制器属性

属性用途
#[Middleware]定义中间件
#[Authorize]定义授权策略
#[Route]定义路由
#[Get]GET 路由快捷方式
#[Post]POST 路由快捷方式
#[Put]PUT 路由快捷方式
#[Delete]DELETE 路由快捷方式
#[Patch]PATCH 路由快捷方式

2.2 队列属性

属性用途
#[Tries]最大尝试次数
#[Backoff]重试间隔
#[Timeout]超时时间
#[FailOnTimeout]超时失败
#[MaxExceptions]最大异常数
#[WithoutOverlapping]防止重叠
#[ShouldBeUnique]唯一任务

2.3 验证属性

属性用途
#[Rule]验证规则
#[Required]必填验证
#[Email]邮箱验证
#[Url]URL 验证
#[Date]日期验证

2.4 Eloquent 属性

属性用途
#[ObservedBy]观察者
#[Scope]查询作用域
#[Accessor]访问器
#[Mutator]修改器

3. 控制器属性详解

3.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
<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class UserController extends Controller
{
#[Middleware('verified')]
public function dashboard()
{
// 需要认证和邮箱验证
}

#[Middleware(['auth', 'admin'])]
public function adminPanel()
{
// 需要认证和管理员权限
}

#[Middleware('throttle:60,1')]
public function api()
{
// 限流:每分钟 60 次
}
}

3.2 中间件排除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#[Middleware('auth')]
class UserController extends Controller
{
public function index()
{
// 需要 auth 中间件
}

#[Middleware(except: ['auth'])]
public function public()
{
// 不需要 auth 中间件
}
}

3.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
<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Routing\Attributes\Controllers\Authorize;

class PostController extends Controller
{
#[Authorize('create', Post::class)]
public function create()
{
// 只有有创建权限的用户可以访问
}

#[Authorize('update', 'post')]
public function update(Post $post)
{
// 自动解析 'post' 路由参数
}

#[Authorize('delete', [Post::class, 'post'])]
public function destroy(Post $post)
{
// 使用路由参数解析模型
}
}

4. 路由属性详解

4.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
32
33
34
35
36
37
38
39
40
41
<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Attributes\Get;
use Illuminate\Routing\Attributes\Post;
use Illuminate\Routing\Attributes\Put;
use Illuminate\Routing\Attributes\Delete;

class PostController extends Controller
{
#[Get('/posts')]
public function index()
{
// GET /posts
}

#[Get('/posts/{post}')]
public function show(Post $post)
{
// GET /posts/{post}
}

#[Post('/posts')]
public function store()
{
// POST /posts
}

#[Put('/posts/{post}')]
public function update(Post $post)
{
// PUT /posts/{post}
}

#[Delete('/posts/{post}')]
public function destroy(Post $post)
{
// DELETE /posts/{post}
}
}

4.2 路由参数

1
2
3
4
5
6
7
8
9
10
11
#[Get('/users/{user}/posts/{post}')]
public function showUserPost(User $user, Post $post)
{
// GET /users/{user}/posts/{post}
}

#[Get('/posts/{post:slug}')]
public function showBySlug(Post $post)
{
// 使用 slug 字段解析
}

4.3 路由名称

1
2
3
4
5
6
7
8
9
10
11
#[Get('/posts', name: 'posts.index')]
public function index()
{
// route('posts.index')
}

#[Get('/posts/{post}', name: 'posts.show')]
public function show(Post $post)
{
// route('posts.show', $post)
}

4.4 路由组

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\Http\Controllers;

use Illuminate\Routing\Attributes\Middleware;
use Illuminate\Routing\Attributes\Prefix;
use Illuminate\Routing\Attributes\Get;

#[Prefix('api/v1')]
#[Middleware(['auth:sanctum', 'throttle:api'])]
class ApiPostController extends Controller
{
#[Get('/posts')]
public function index()
{
// GET /api/v1/posts
}

#[Get('/posts/{post}')]
public function show(Post $post)
{
// GET /api/v1/posts/{post}
}
}

4.5 资源路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Attributes\Resource;

#[Resource('posts')]
class PostController extends Controller
{
public function index() {}
public function create() {}
public function store() {}
public function show($id) {}
public function edit($id) {}
public function update($id) {}
public function destroy($id) {}
}

5. 验证属性详解

5.1 表单请求验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Attributes\Rule;
use Illuminate\Validation\Attributes\Required;
use Illuminate\Validation\Attributes\Email;
use Illuminate\Validation\Attributes\Password;

class StoreUserRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:users'],
'password' => ['required', Password::defaults()],
];
}
}

5.2 模型属性验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Attributes\Required;
use Illuminate\Validation\Attributes\Email;
use Illuminate\Validation\Attributes\Rule;

class User extends Model
{
#[Required]
#[Rule('string', 'max:255')]
protected string $name;

#[Required]
#[Email]
#[Rule('unique:users')]
protected string $email;
}

6. 队列属性详解

6.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
<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Attributes\Tries;
use Illuminate\Queue\Attributes\Backoff;
use Illuminate\Queue\Attributes\Timeout;
use Illuminate\Queue\Attributes\FailOnTimeout;

#[Tries(3)]
#[Backoff([10, 30, 60])]
#[Timeout(120)]
#[FailOnTimeout]
class ProcessPodcast implements ShouldQueue
{
use Queueable;

public function handle()
{
// 任务逻辑
}
}

6.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
<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Attributes\ShouldBeUnique;
use Illuminate\Queue\Attributes\WithoutOverlapping;

#[ShouldBeUnique(3600)] // 1 小时内唯一
class SendWelcomeEmail implements ShouldQueue
{
use Queueable;

public function uniqueId(): string
{
return $this->user->id;
}
}

#[WithoutOverlapping(10)] // 10 分钟内不重叠
class GenerateReport implements ShouldQueue
{
use Queueable;
}

6.3 队列连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Attributes\OnConnection;
use Illuminate\Queue\Attributes\OnQueue;

#[OnConnection('redis')]
#[OnQueue('podcasts')]
class ProcessPodcast implements ShouldQueue
{
use Queueable;
}

7. Eloquent 属性详解

7.1 观察者

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use App\Observers\UserObserver;

#[ObservedBy(UserObserver::class)]
class User extends Model
{
// 观察者自动注册
}

7.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
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Attributes\Scope;

class User extends Model
{
#[Scope]
public function active($query)
{
return $query->where('active', true);
}

#[Scope]
public function ofType($query, string $type)
{
return $query->where('type', $type);
}
}

// 使用
User::active()->ofType('admin')->get();

7.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
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Attributes\Accessor;
use Illuminate\Database\Eloquent\Attributes\Mutator;

class User extends Model
{
#[Accessor]
public function fullName(): Attribute
{
return Attribute::make(
get: fn($value, $attributes) => $attributes['first_name'] . ' ' . $attributes['last_name'],
);
}

#[Mutator]
public function password(): Attribute
{
return Attribute::make(
set: fn($value) => bcrypt($value),
);
}
}

8. 自定义属性

8.1 创建自定义属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace App\Attributes;

use Attribute;

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
class LogActivity
{
public function __construct(
public string $action,
public string $description = '',
) {}
}

8.2 使用自定义属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

namespace App\Http\Controllers;

use App\Attributes\LogActivity;

class PostController extends Controller
{
#[LogActivity('post.created', 'User created a new post')]
public function store()
{
// 创建文章
}

#[LogActivity('post.deleted', 'User deleted a post')]
public function destroy($id)
{
// 删除文章
}
}

8.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
<?php

namespace App\Services;

use Illuminate\Support\Facades\Log;
use ReflectionClass;

class ActivityLogService
{
public function log(object $controller, string $method): void
{
$reflection = new ReflectionClass($controller);
$methodReflection = $reflection->getMethod($method);

$attributes = $methodReflection->getAttributes(LogActivity::class);

foreach ($attributes as $attribute) {
$log = $attribute->newInstance();

Log::info("Activity: {$log->action}", [
'description' => $log->description,
'user_id' => auth()->id(),
]);
}
}
}

9. 最佳实践

9.1 属性组织

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Attributes\Get;
use Illuminate\Routing\Attributes\Middleware;
use Illuminate\Routing\Attributes\Authorize;
use Illuminate\Routing\Attributes\Prefix;

#[Prefix('api/v1')]
#[Middleware(['auth:sanctum', 'throttle:api'])]
class PostController extends Controller
{
#[Get('/posts')]
#[Authorize('viewAny', Post::class)]
public function index()
{
// ...
}
}

9.2 避免过度使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 不推荐:过多属性
#[Get('/posts')]
#[Middleware('auth')]
#[Middleware('verified')]
#[Middleware('admin')]
#[Authorize('viewAny')]
#[Throttle(60)]
#[Cache(3600)]
public function index() {}

// 推荐:合理组合
#[Get('/posts')]
#[Middleware(['auth', 'verified', 'admin'])]
#[Authorize('viewAny')]
public function index() {}

9.3 文档注释

1
2
3
4
5
6
7
8
9
10
11
/**
* 获取文章列表
*
* @return \Illuminate\Http\JsonResponse
*/
#[Get('/posts', name: 'posts.index')]
#[Authorize('viewAny', Post::class)]
public function index()
{
// ...
}

10. 总结

Laravel 13 的 PHP Attributes 支持为开发者提供了声明式配置的强大能力:

  1. 配置集中:配置与代码放在一起,更易维护
  2. 类型安全:编译时检查,减少错误
  3. IDE 友好:更好的代码提示和导航
  4. 可扩展:支持自定义属性

通过本指南,您已经掌握了 Laravel 13 PHP Attributes 的核心用法,可以开始采用声明式风格编写代码了。

参考资料