04-常用组件

4 阅读5分钟

Hyperf 常用组件

1. 数据库组件

1.1 Model 模型

定义模型
<?php
namespace App\Model;

use Hyperf\DbConnection\Model\Model;

class User extends Model
{
    protected ?string $table = 'users';
    
    protected string $primaryKey = 'id';
    
    protected array $fillable = ['name', 'email', 'password'];
    
    protected array $hidden = ['password'];
    
    protected array $casts = [
        'created_at' => 'datetime',
        'is_active' => 'boolean',
    ];
}
基本操作
<?php
// 查询
$user = User::find(1);
$user = User::where('email', 'test@example.com')->first();
$users = User::where('status', 1)->get();

// 创建
$user = User::create([
    'name' => 'John',
    'email' => 'john@example.com',
    'password' => password_hash('123456', PASSWORD_DEFAULT),
]);

// 更新
$user = User::find(1);
$user->name = 'Jane';
$user->save();

// 或者
User::where('id', 1)->update(['name' => 'Jane']);

// 删除
$user = User::find(1);
$user->delete();

// 或者
User::destroy(1);
User::where('status', 0)->delete();
关联关系
<?php
namespace App\Model;

use Hyperf\Database\Model\Relations\HasMany;
use Hyperf\Database\Model\Relations\BelongsTo;

class User extends Model
{
    // 一对多:一个用户有多个订单
    public function orders(): HasMany
    {
        return $this->hasMany(Order::class, 'user_id', 'id');
    }
}

class Order extends Model
{
    // 多对一:订单属于用户
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id', 'id');
    }
}

// 使用
$user = User::find(1);
$orders = $user->orders;  // 获取用户的所有订单

$order = Order::find(1);
$user = $order->user;  // 获取订单的用户

1.2 查询构造器

<?php
use Hyperf\DbConnection\Db;

// 基本查询
$users = Db::table('users')->get();
$user = Db::table('users')->where('id', 1)->first();

// 条件查询
$users = Db::table('users')
    ->where('status', 1)
    ->where('age', '>', 18)
    ->orWhere('vip', true)
    ->get();

// 聚合查询
$count = Db::table('users')->count();
$max = Db::table('users')->max('age');
$avg = Db::table('users')->avg('score');

// 分组
$result = Db::table('orders')
    ->select('user_id', Db::raw('SUM(amount) as total'))
    ->groupBy('user_id')
    ->having('total', '>', 1000)
    ->get();

// 联表查询
$orders = Db::table('orders')
    ->join('users', 'orders.user_id', '=', 'users.id')
    ->select('orders.*', 'users.name')
    ->get();

// 分页
$users = Db::table('users')->paginate(15);

// 事务
Db::transaction(function () {
    Db::table('users')->update(['balance' => Db::raw('balance - 100')]);
    Db::table('orders')->insert(['amount' => 100]);
});

1.3 数据库迁移

创建迁移
php bin/hyperf.php gen:migration create_users_table
编写迁移
<?php
use Hyperf\Database\Schema\Schema;
use Hyperf\Database\Schema\Blueprint;
use Hyperf\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
    public function up(): void
    {
        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('name', 100);
            $table->string('email', 100)->unique();
            $table->string('password');
            $table->tinyInteger('status')->default(1);
            $table->timestamps();
            
            $table->index('email');
        });
    }
    
    public function down(): void
    {
        Schema::dropIfExists('users');
    }
}
运行迁移
php bin/hyperf.php migrate
php bin/hyperf.php migrate:rollback
php bin/hyperf.php migrate:refresh

2. 缓存组件

2.1 注解方式(推荐)

<?php
namespace App\Service;

use Hyperf\Cache\Annotation\Cacheable;
use Hyperf\Cache\Annotation\CachePut;
use Hyperf\Cache\Annotation\CacheEvict;

class UserService
{
    // 生成缓存
    #[Cacheable(prefix: 'user', ttl: 3600, value: '#{id}')]
    public function getUserById(int $id)
    {
        return Db::table('users')->find($id);
    }
    
    // 更新缓存
    #[CachePut(prefix: 'user', ttl: 3600, value: '#{user.id}')]
    public function updateUser(array $user)
    {
        Db::table('users')->where('id', $user['id'])->update($user);
        return $user;
    }
    
    // 删除缓存
    #[CacheEvict(prefix: 'user', value: '#{id}')]
    public function deleteUser(int $id)
    {
        Db::table('users')->where('id', $id)->delete();
    }
    
    // 删除所有缓存
    #[CacheEvict(prefix: 'user', all: true)]
    public function deleteAllCache()
    {
    }
}

2.2 手动操作

<?php
use Hyperf\Redis\Redis;
use Hyperf\Di\Annotation\Inject;

class UserService
{
    #[Inject]
    private Redis $redis;
    
    public function getUserById(int $id)
    {
        $key = "user:{$id}";
        
        // 获取缓存
        $cached = $this->redis->get($key);
        if ($cached !== null) {
            return json_decode($cached, true);
        }
        
        // 查询数据库
        $user = Db::table('users')->find($id);
        
        // 保存到缓存
        $this->redis->setex($key, 3600, json_encode($user));
        
        return $user;
    }
}

2.3 缓存失效策略

<?php
// 设置过期时间
$redis->setex('key', 3600, 'value');  // 3600秒后过期
$redis->expire('key', 3600);

// 删除缓存
$redis->del('key');
$redis->del(['key1', 'key2', 'key3']);

// 模糊删除
$keys = $redis->keys('user:*');
if ($keys) {
    $redis->del(...$keys);
}

3. 日志组件

3.1 基本使用

<?php
namespace App\Service;

use Hyperf\Logger\LoggerFactory;
use Psr\Log\LoggerInterface;

class UserService
{
    private LoggerInterface $logger;
    
    public function __construct(LoggerFactory $loggerFactory)
    {
        // 获取日志实例(default 是配置名称)
        $this->logger = $loggerFactory->get('default');
    }
    
    public function createUser(array $data)
    {
        $this->logger->debug('开始创建用户', $data);
        
        try {
            $user = User::create($data);
            $this->logger->info('用户创建成功', ['id' => $user->id]);
            return $user;
        } catch (\Exception $e) {
            $this->logger->error('用户创建失败', [
                'exception' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);
            throw $e;
        }
    }
}

3.2 日志级别

<?php
$logger->emergency('系统不可用');
$logger->alert('必须立即采取行动');
$logger->critical('临界条件');
$logger->error('错误');
$logger->warning('警告');
$logger->notice('通知');
$logger->info('信息');
$logger->debug('调试');

3.3 配置多个日志

config/autoload/logger.php

<?php
return [
    'default' => [
        'handler' => [
            'class' => Monolog\Handler\StreamHandler::class,
            'constructor' => [
                'stream' => BASE_PATH . '/runtime/logs/hyperf.log',
                'level' => Monolog\Logger::DEBUG,
            ],
        ],
    ],
    'sql' => [
        'handler' => [
            'class' => Monolog\Handler\StreamHandler::class,
            'constructor' => [
                'stream' => BASE_PATH . '/runtime/logs/sql.log',
                'level' => Monolog\Logger::INFO,
            ],
        ],
    ],
];

使用:

<?php
$defaultLogger = $loggerFactory->get('default');
$sqlLogger = $loggerFactory->get('sql');

4. 验证器

4.1 创建验证器

<?php
namespace App\Request;

use Hyperf\Validation\Request\FormRequest;

class UserRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;  // 是否有权限执行此请求
    }
    
    public function rules(): array
    {
        return [
            'name' => 'required|string|max:100',
            'email' => 'required|email|unique:users,email',
            'password' => 'required|string|min:6|confirmed',
            'age' => 'required|integer|min:1|max:150',
            'avatar' => 'nullable|image|max:2048',  // 最大 2MB
        ];
    }
    
    public function messages(): array
    {
        return [
            'name.required' => '姓名不能为空',
            'email.required' => '邮箱不能为空',
            'email.email' => '邮箱格式不正确',
            'email.unique' => '邮箱已被注册',
            'password.required' => '密码不能为空',
            'password.min' => '密码长度不能少于 6 位',
            'password.confirmed' => '两次密码输入不一致',
        ];
    }
}

4.2 使用验证器

<?php
namespace App\Controller;

use App\Request\UserRequest;

class UserController
{
    public function create(UserRequest $request)
    {
        // 如果验证失败,会自动返回 422 错误
        // 验证通过后,可以获取数据
        $data = $request->validated();
        
        $user = User::create($data);
        
        return ['id' => $user->id];
    }
}

4.3 常用验证规则

<?php
return [
    'name' => 'required',                     // 必填
    'email' => 'required|email|unique:users', // 必填、邮箱格式、唯一
    'age' => 'integer|between:1,150',         // 整数、范围
    'url' => 'url',                           // URL 格式
    'ip' => 'ip',                             // IP 地址
    'date' => 'date|after:2020-01-01',        // 日期、晚于某日期
    'amount' => 'numeric|min:0',              // 数字、最小值
    'status' => 'in:active,inactive',         // 枚举值
    'tags' => 'array',                        // 数组
    'tags.*' => 'string',                     // 数组元素类型
];

4.4 自定义验证规则

<?php
namespace App\Request;

use Hyperf\Validation\Request\FormRequest;
use Hyperf\Validation\Rule;

class UserRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'phone' => ['required', 'regex:/^1[3-9]\d{9}$/'],  // 手机号
            'id_card' => ['required', function ($attribute, $value, $fail) {
                // 自定义验证逻辑
                if (!$this->validateIdCard($value)) {
                    $fail('身份证号格式不正确');
                }
            }],
        ];
    }
    
    private function validateIdCard($value): bool
    {
        // 验证身份证号的逻辑
        return preg_match('/^\d{17}[\dXx]$/', $value);
    }
}

5. 定时任务

5.1 创建定时任务

<?php
namespace App\Crontab;

use Hyperf\Crontab\Annotation\Crontab;

#[Crontab(
    name: 'StatisticsTask',
    rule: '0 2 * * *',        // 每天凌晨 2 点执行
    callback: 'execute',
    memo: '统计每日数据'
)]
class StatisticsTask
{
    public function execute()
    {
        echo "执行统计任务\n";
        
        // 统计逻辑
        $count = Db::table('orders')
            ->whereDate('created_at', date('Y-m-d'))
            ->count();
        
        echo "今日订单数:{$count}\n";
    }
}

5.2 Cron 表达式

* * * * *
│ │ │ │ │
│ │ │ │ └─ 星期 (0-7, 07都表示周日)
│ │ │ └─── 月份 (1-12)
│ │ └───── 日期 (1-31)
│ └─────── 小时 (0-23)
└───────── 分钟 (0-59)

常用示例

'0 2 * * *'        // 每天凌晨 2 点
'*/5 * * * *'      // 每 5 分钟
'0 */2 * * *'      // 每 2 小时
'0 0 * * 0'        // 每周日凌晨
'0 0 1 * *'        // 每月 1 号凌晨
'0 9-18 * * 1-5'   // 工作日 9-18 点,每小时

5.3 动态定时任务

<?php
namespace App\Service;

use Hyperf\Crontab\Crontab;
use Hyperf\Crontab\CrontabManager;
use Hyperf\Di\Annotation\Inject;

class CrontabService
{
    #[Inject]
    private CrontabManager $crontabManager;
    
    public function addTask(string $name, string $rule, callable $callback)
    {
        $crontab = new Crontab();
        $crontab->setName($name);
        $crontab->setRule($rule);
        $crontab->setCallback($callback);
        $crontab->setMemo('动态添加的任务');
        
        $this->crontabManager->register($crontab);
    }
    
    public function removeTask(string $name)
    {
        $this->crontabManager->deregister($name);
    }
}

6. 异步队列

6.1 创建任务

<?php
namespace App\Job;

use Hyperf\AsyncQueue\Job;

class EmailJob extends Job
{
    public $params;
    
    public function __construct(array $params)
    {
        $this->params = $params;
    }
    
    public function handle()
    {
        // 发送邮件的逻辑
        $to = $this->params['to'];
        $subject = $this->params['subject'];
        $content = $this->params['content'];
        
        echo "发送邮件到: {$to}\n";
        
        // 实际发送邮件...
    }
}

6.2 推送任务

<?php
namespace App\Controller;

use App\Job\EmailJob;
use Hyperf\AsyncQueue\Driver\DriverFactory;
use Hyperf\Di\Annotation\Inject;

class UserController
{
    #[Inject]
    private DriverFactory $driverFactory;
    
    public function sendEmail()
    {
        $driver = $this->driverFactory->get('default');
        
        // 推送任务到队列
        $driver->push(new EmailJob([
            'to' => 'user@example.com',
            'subject' => '欢迎注册',
            'content' => '感谢您的注册!',
        ]), 0);  // 延迟 0 秒
        
        return '邮件已加入发送队列';
    }
    
    public function sendDelayedEmail()
    {
        $driver = $this->driverFactory->get('default');
        
        // 延迟 60 秒后发送
        $driver->push(new EmailJob([
            'to' => 'user@example.com',
            'subject' => '延迟消息',
            'content' => '这是一封延迟发送的邮件',
        ]), 60);
        
        return '邮件将在 60 秒后发送';
    }
}

6.3 启动消费者

php bin/hyperf.php start

消费者会自动处理队列中的任务。

7. 事件系统

7.1 定义事件

<?php
namespace App\Event;

class UserRegistered
{
    public $user;
    
    public function __construct($user)
    {
        $this->user = $user;
    }
}

7.2 定义监听器

<?php
namespace App\Listener;

use App\Event\UserRegistered;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Psr\Container\ContainerInterface;

#[Listener]
class SendWelcomeEmailListener implements ListenerInterface
{
    public function __construct(private ContainerInterface $container)
    {
    }
    
    public function listen(): array
    {
        return [
            UserRegistered::class,
        ];
    }
    
    public function process(object $event): void
    {
        if ($event instanceof UserRegistered) {
            $user = $event->user;
            
            // 发送欢迎邮件
            echo "发送欢迎邮件到: {$user->email}\n";
        }
    }
}

7.3 触发事件

<?php
namespace App\Service;

use App\Event\UserRegistered;
use Psr\EventDispatcher\EventDispatcherInterface;
use Hyperf\Di\Annotation\Inject;

class UserService
{
    #[Inject]
    private EventDispatcherInterface $eventDispatcher;
    
    public function register(array $data)
    {
        $user = User::create($data);
        
        // 触发事件
        $this->eventDispatcher->dispatch(new UserRegistered($user));
        
        return $user;
    }
}

8. 要点

必须掌握

  • 数据库 Model 和查询构造器的使用
  • 缓存注解的使用
  • 日志的使用
  • 验证器的使用
  • 定时任务的配置

加分项

  • 异步队列的使用场景
  • 事件系统的应用
  • 数据库迁移和 Seeder

高频题

1. Hyperf 的缓存注解有哪几种?

答:主要有三种:

  • @Cacheable:生成缓存,方法首次调用时将结果缓存
  • @CachePut:更新缓存,每次都执行方法并更新缓存
  • @CacheEvict:删除缓存,可以删除单个或全部缓存

2. 异步队列有什么用?

答:异步队列用于将耗时任务异步执行,避免阻塞主流程。

应用场景:

  • 发送邮件、短信
  • 数据统计和分析
  • 图片处理
  • 导出大文件
  • 调用第三方 API

3. 定时任务如何配置?

答:使用 @Crontab 注解,配置任务名称、执行规则(Cron 表达式)、回调方法等。Hyperf 会自动调度执行。


下一步:阅读 08-题库.md 巩固知识点