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

6.1 KiB
Raw Blame History

事件

模型事件實現 psr/event-dispatcher 介面,預設由 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

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
{
    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 | 資料刪除後 | 否 | | | forceDeleted | 資料強制刪除後 | 否 | |

針對某個模型的事件使用十分簡單,只需要在模型中增加對應的方法即可。例如下方儲存資料時,觸發 saving 事件,主動覆寫 created_at 欄位。

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

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();
            }
        }
    }
}