【基于 Swoole+Hyperf 的微服务实战】第一周·周五:Hyperf 依赖注入、注解路由与数据库连接
第一周 周五:Hyperf 依赖注入、注解路由与数据库连接

今日目标
- 理解 Hyperf 的依赖注入容器,学会通过构造函数、
#[Inject]注解注入依赖。 - 掌握 注解路由,使用
#[Controller]和#[RequestMapping]定义 RESTful 接口,告别手写路由配置。 - 安装并配置 hyperf/database,实现模型与数据表的映射,完成第一个 CRUD 接口。
- 结合验证器与异常处理(衔接下周一内容),编写一个完整的文章创建接口,体验框架的便捷。
- 测试所有接口,验证数据库读写、参数校验和依赖注入是否正常工作。
一、环境准备(约 20 分钟)
继续使用周四创建的 hyperf-app 项目,确保 Docker 容器已启动并进入:
cd swoole-course
docker-compose exec swoole bash
cd /var/www/hyperf-app
1. 确认 MySQL 服务可用
昨天我们已经在 Docker Compose 中加入了 MySQL(如果还没有,请参考周四内容添加)。测试连接:
php -r "new PDO('mysql:host=mysql;dbname=hyperf_blog', 'blog_user', 'blog_pass'); echo 'OK';"
若输出 OK,说明数据库连接正常。
2. 安装数据库组件(若尚未安装)
周四我们已经安装了 hyperf/database,如果没有安装,执行:
composer require hyperf/database hyperf/db-connection
发布配置文件(如果还没有):
php bin/hyperf.php vendor:publish hyperf/database
3. 创建数据表
我们需要一张 articles 表用于今天的 CRUD。在 MySQL 中执行以下 SQL(可以通过 mysql 客户端或 PHP 脚本):
CREATE TABLE `articles` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`title` VARCHAR(255) NOT NULL,
`content` TEXT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
在容器内使用 mysql 客户端:
mysql -h mysql -u blog_user -pblog_pass hyperf_blog
粘贴 SQL 执行。或者直接用 PHP 脚本创建。
二、知识核心:DI、注解路由与 Eloquent ORM(约 1.5 小时)
1. 依赖注入(DI)容器
Hyperf 内置了强大的 PSR-11 容器,它管理对象的创建和依赖解析。我们不需要手动 new 对象,而是通过容器获取,或者由框架自动注入。
三种注入方式:
- 构造函数注入:在类构造函数中声明依赖,容器会自动解析并传入。
#[Inject]注解注入:在属性上使用注解,适合控制器、服务等由容器管理的类。ApplicationContext手动获取:在非容器管理的场景(如闭包)中,通过ApplicationContext::getContainer()->get(SomeClass::class)获取。
例如,在控制器中注入 ArticleModel 或 ValidatorFactoryInterface 都是通过 #[Inject] 实现。
2. 注解路由
与周四的闭包路由不同,注解路由直接写在控制器类和方法上,更贴近代码,便于维护。
#[Controller(prefix: '/articles')]:标记控制器,prefix为所有方法路由的前缀。#[RequestMapping(path: '', methods: 'get')]:标记方法,path相对于控制器前缀,methods为 HTTP 方法(GET、POST、PUT、DELETE 等)。
框架启动时扫描所有注解,自动注册路由。这种方式的优点是路由与控制器紧密绑定,减少配置文件修改。
3. Eloquent ORM
Hyperf 的 hyperf/database 基于 Illuminate Database,提供 Eloquent ORM。我们可以创建模型类,继承 Hyperf\DbConnection\Model\Model,定义表名、可填充字段等,然后像操作对象一样操作数据库。
关键点:
- 模型默认表名为类名的蛇形复数(如
Article→articles),可通过$table属性指定。 - 使用
$fillable定义允许批量赋值的字段。 - 通过
Article::create()、Article::find()、Article::where()等方法进行数据库操作。
三、实战:创建文章 CRUD 接口(约 2.5 小时)
步骤 1:配置数据库连接
编辑 config/autoload/databases.php(如果已存在,检查配置):
<?php
return [
'default' => [
'driver' => env('DB_DRIVER', 'mysql'),
'host' => env('DB_HOST', 'mysql'),
'port' => env('DB_PORT', 3306),
'database' => env('DB_DATABASE', 'hyperf_blog'),
'username' => env('DB_USERNAME', 'blog_user'),
'password' => env('DB_PASSWORD', 'blog_pass'),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'pool' => [
'min_connections' => 1,
'max_connections' => 10,
'connect_timeout' => 10.0,
'wait_timeout' => 3.0,
'heartbeat' => -1,
'max_idle_time' => 60,
],
],
];
确认 .env 中有对应配置。
步骤 2:创建模型
新建 app/Model/Article.php:
<?php
namespace App\Model;
use Hyperf\DbConnection\Model\Model;
class Article extends Model
{
protected $table = 'articles';
protected $fillable = ['title', 'content'];
public $timestamps = true; // 使用 created_at 和 updated_at
}
步骤 3:创建控制器
新建 app/Controller/ArticleController.php:
<?php
namespace App\Controller;
use App\Model\Article;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\RequestMapping;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Validation\Contract\ValidatorFactoryInterface;
use Hyperf\Validation\ValidationException;
#[Controller(prefix: '/articles')]
class ArticleController extends AbstractController
{
#[Inject]
protected ValidatorFactoryInterface $validatorFactory;
// 列表
#[RequestMapping(path: '', methods: 'get')]
public function index()
{
$articles = Article::orderBy('id', 'desc')->paginate(10);
return $articles->toArray();
}
// 详情
#[RequestMapping(path: '{id:\d+}', methods: 'get')]
public function show(int $id)
{
$article = Article::find($id);
if (!$article) {
return $this->response->json(['code' => 404, 'message' => '文章不存在'])->withStatus(404);
}
return $article->toArray();
}
// 创建
#[RequestMapping(path: '', methods: 'post')]
public function store()
{
$data = $this->request->all();
$validator = $this->validatorFactory->make($data, [
'title' => 'required|string|max:255',
'content' => 'required|string',
], [
'title.required' => '文章标题必填',
'content.required' => '文章内容必填',
]);
if ($validator->fails()) {
throw new ValidationException($validator);
}
$article = Article::create($validator->validated());
return $this->response->json(['code' => 201, 'message' => '创建成功', 'data' => $article])->withStatus(201);
}
// 更新
#[RequestMapping(path: '{id:\d+}', methods: 'put')]
public function update(int $id)
{
$article = Article::find($id);
if (!$article) {
return $this->response->json(['code' => 404, 'message' => '文章不存在'])->withStatus(404);
}
$data = $this->request->all();
$validator = $this->validatorFactory->make($data, [
'title' => 'sometimes|required|string|max:255',
'content' => 'sometimes|required|string',
]);
if ($validator->fails()) {
throw new ValidationException($validator);
}
$article->update($validator->validated());
return ['code' => 200, 'message' => '更新成功', 'data' => $article];
}
// 删除
#[RequestMapping(path: '{id:\d+}', methods: 'delete')]
public function destroy(int $id)
{
$article = Article::find($id);
if (!$article) {
return $this->response->json(['code' => 404, 'message' => '文章不存在'])->withStatus(404);
}
$article->delete();
return $this->response->json(['code' => 204, 'message' => '删除成功'])->withStatus(204);
}
}
注意:
- 控制器继承
AbstractController,可以直接使用$this->request和$this->response。 - 验证器工厂通过
#[Inject]注入,无需手动实例化。 paginate()需要hyperf/paginator组件,如果未安装请执行composer require hyperf/paginator。- 如果验证失败,主动抛出
ValidationException。该异常目前还没有专门的处理器,会走全局异常处理,但会返回不太友好的调试信息。我们将在下周一学习异常处理器,今天可以先略过或简单捕获。
步骤 4:测试接口
重启 Hyperf 服务(确保注解路由被扫描):
php bin/hyperf.php start
使用 curl 测试:
# 创建文章
curl -X POST http://localhost:9501/articles \
-H "Content-Type: application/json" \
-d '{"title":"Hello Hyperf","content":"这是第一篇文章"}'
# 获取列表
curl http://localhost:9501/articles
# 获取详情
curl http://localhost:9501/articles/1
# 更新
curl -X PUT http://localhost:9501/articles/1 \
-H "Content-Type: application/json" \
-d '{"title":"Updated Title"}'
# 删除
curl -X DELETE http://localhost:9501/articles/1
验证数据库中的数据变化。
四、成果测试与检验(约 1 小时)
测试清单
| 检验项 | 方法 | 通过标准 |
|---|---|---|
| 依赖注入正常 | 接口能正常创建文章,验证器工厂被成功注入 | 验证失败时返回 422(如缺少字段) |
| 注解路由生效 | 访问 /articles 各 HTTP 方法 | 返回预期 JSON,无 404 |
| 模型映射正确 | 查询数据库 articles 表,数据与请求一致 | 字段正确填充 |
| 分页功能 | 列表接口 ?page=2 | 返回分页结构,包含 data, current_page, total |
| 参数校验 | 发送缺少 title 的 POST | 返回验证错误信息(目前可能是 500,需等下周一优化) |
| 数据库连接池 | 高并发请求列表接口 | 无连接错误,延迟稳定 |
常见问题
- 路由未生效:检查控制器类是否在
app/Controller目录下(默认扫描),注解是否正确,以及服务重启。 - 验证失败导致 500:暂时未配置验证异常处理器,可在控制器内
catch或直接返回,或者接受这个临时问题,因为下周一我们会专门解决异常处理。 - 数据库连接错误:检查
.env和databases.php配置,确保容器内可访问 MySQL 服务(主机名mysql)。
调试技巧
在控制器方法中临时添加 var_dump($article); exit; 查看对象属性,验证模型工作正常。
五、今日作业与学习产出
- 提交代码:将
Article模型、ArticleController、数据库配置等提交到 Git。 - 完善 CRUD:
- 为文章增加
author_id字段(可先不关联用户,仅存储 ID),并在创建时设置。 - 实现软删除(使用
SoftDeletestrait),并测试删除后数据仍可查询。
- 为文章增加
- 学习笔记:
- 绘制 Hyperf 请求生命周期图,标注 DI 容器、注解路由、控制器、模型的位置。
- 对比注解路由和闭包路由的优劣。
- 挑战任务:
- 使用模型事件(
created,updated)在文章创建后自动写入日志。 - 尝试使用查询构造器(
Db::table)替代 Eloquent 完成相同功能,体会两者差异。
- 使用模型事件(
通过今天的学习,你已经掌握了 Hyperf 中最核心的开发三件套:依赖注入、注解路由和数据库 ORM。下周我们将继续深入,学习验证器和异常处理,让你的 API 更加健壮和专业。
更多推荐
所有评论(0)