hyperf/docs/zh-hk/db/event.md

186 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 事件
模型事件實現 [psr/event-dispatcher](https://github.com/php-fig/event-dispatcher) 接口,默認由 [hyperf/event](https://github.com/hyperf/event) 組件提供功能支持。
## 運行事件
在 ORM 的運行期間,會對應觸發以下事件,您可以進行對這些事件進行監聽以滿足您的需求。
| 事件 | 描述 |
| :--------: | :----: |
| Hyperf\Database\Events\QueryExecuted| Query 語句執行後 |
| Hyperf\Database\Events\StatementPrepared| SQL 語句 prepared 後 |
| Hyperf\Database\Events\TransactionBeginning| 事務開啓後 |
| Hyperf\Database\Events\TransactionCommitted| 事務提交後 |
| Hyperf\Database\Events\TransactionRolledBack| 事務回滾後 |
### SQL 執行監聽器
根據上述的 ORM 運行事件,接下來我們來實現一個記錄 SQL 的監聽器,達到在每次執行 SQL 時記錄下來並輸出到日誌上。
首先我們定義好 `DbQueryExecutedListener` ,實現 `Hyperf\Event\Contract\ListenerInterface` 接口並對類定義 `Hyperf\Event\Annotation\Listener` 註解,這樣 Hyperf 就會在應用啓動時自動把該監聽器註冊到事件調度器中,並在事件觸發時執行監聽邏輯,示例代碼如下:
```php
<?php
declare(strict_types=1);
namespace App\Listener;
use Hyperf\Database\Events\QueryExecuted;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\Logger\LoggerFactory;
use Hyperf\Collection\Arr;
use Hyperf\Utils\Str;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
#[Listener]
class DbQueryExecutedListener implements ListenerInterface
{
private LoggerInterface $logger;
public function __construct(ContainerInterface $container)
{
// 輸出到對應名為 sql 的日誌 name如不存在則需自行添加配置
// 這裏的 sql 日誌 name 不是必須的,只是表達可以將 SQL 執行日誌與普通日誌區分開
$this->logger = $container->get(LoggerFactory::class)->get('sql');
}
public function listen(): array
{
return [
QueryExecuted::class,
];
}
/**
* @param QueryExecuted $event
*/
public function process(object $event)
{
if ($event instanceof QueryExecuted) {
$sql = $event->sql;
if (! Arr::isAssoc($event->bindings)) {
foreach ($event->bindings as $key => $value) {
$sql = Str::replaceFirst('?', "'{$value}'", $sql);
}
}
$this->logger->info(sprintf('[%s] %s', $event->time, $sql));
}
}
}
```
## 模型事件
模型事件與 `Eloquent ORM` 不太一致,`Eloquent ORM` 使用 `Observer` 監聽模型事件。`Hyperf` 提供 `鈎子函數``事件監聽` 兩種形式來處理對應的事件。
### 鈎子函數
| 事件名 | 觸發實際 | 是否阻斷 | 備註 |
|:------------:|:----------------:|:--------:|:-------------------------- --:|
| booting | 模型首次加載前 | 否 | 進程生命週期中只會觸發一次 |
| booted | 模型首次加載後 | 否 | 進程生命週期中只會觸發一次 |
| retrieved | 填充數據後 | 否 | 每當模型從 DB 或緩存查詢出來後觸發 |
| creating | 數據創建時 | 是 | |
| created | 數據創建後 | 否 | |
| updating | 數據更新時 | 是 | |
| updated | 數據更新後 | 否 | |
| saving | 數據創建或更新時 | 是 | |
| saved | 數據創建或更新後 | 否 | |
| restoring | 軟刪除數據恢復時 | 是 | |
| restored | 軟刪除數據恢復後 | 否 | |
| deleting | 數據刪除時 | 是 | |
| deleted | 數據刪除後 | 否 | |
| forceDeleting | 數據強制刪除時 | 是 | |
| forceDeleted | 數據強制刪除後 | 否 | |
針對某個模型的事件使用十分簡單,只需要在模型中增加對應的方法即可。例如下方保存數據時,觸發 `saving` 事件,主動覆寫 `created_at` 字段。
```php
<?php
declare(strict_types=1);
namespace App\Models;
use Hyperf\Database\Model\Events\Saving;
/**
* @property $id
* @property $name
* @property $gender
* @property $created_at
* @property $updated_at
*/
class User extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'user';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['id', 'name', 'gender', 'created_at', 'updated_at'];
protected $casts = ['id' => 'integer', 'gender' => 'integer'];
public function saving(Saving $event)
{
$this->setCreatedAt('2019-01-01');
}
}
```
### 事件監聽
當您需要監聽所有的模型事件時,可以很方便的自定義對應的 `Listener`,比如下方模型緩存的監聽器,當模型修改和刪除後,會刪除對應緩存。
```php
<?php
declare(strict_types=1);
namespace Hyperf\ModelCache\Listener;
use Hyperf\Database\Model\Events\Deleted;
use Hyperf\Database\Model\Events\Event;
use Hyperf\Database\Model\Events\Saved;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\ModelCache\CacheableInterface;
#[Listener]
class DeleteCacheListener implements ListenerInterface
{
public function listen(): array
{
return [
Deleted::class,
Saved::class,
];
}
public function process(object $event)
{
if ($event instanceof Event) {
$model = $event->getModel();
if ($model instanceof CacheableInterface) {
$model->deleteCache();
}
}
}
}
```