hyperf/docs/zh-tw/db/event.md
沈唁 bb3ed4815c
Add translate docs action (#3276)
* Add translate docs action

* Remove push

Co-authored-by: sy-records <sy-records@users.noreply.github.com>
2021-02-19 12:53:50 +08:00

192 lines
6.1 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\Utils\Arr;
use Hyperf\Utils\Str;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
/**
* @Listener
*/
class DbQueryExecutedListener implements ListenerInterface
{
/**
* @var LoggerInterface
*/
private $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 | 資料刪除後 | 否 | |
| 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();
}
}
}
}
```