跳转到内容

模型事件


概览

模型允许您在执行插入/更新/删除操作时实现事件,这些事件可用于定义业务规则。以下是支持的事件及其执行顺序:Phalcon\Mvc\Model和它们的执行顺序:

操作 名称 停止? 解释
插入 afterCreate 在创建记录后运行
删除 afterDelete 在删除记录后运行
获取 afterFetch 在获取记录后运行
插入/更新 afterSave 在保存记录后运行
更新 afterUpdate 在更新记录后运行
插入/更新 afterValidation 在字段验证为非空(非空字符串或外键)之后执行null/空字符串或外键
插入 afterValidationOnCreate 在字段验证为非空(非空字符串或外键)之后执行null/空字符串或外键在插入时
更新 afterValidationOnUpdate 在字段验证为非空(非空字符串或外键)之后执行null/空字符串或外键在更新时
插入 beforeCreate 在创建记录前运行
删除 beforeDelete 在删除记录前运行
插入/更新 beforeSave 在保存记录前运行
更新 beforeUpdate 在更新记录前运行
插入/更新 beforeValidation 在字段验证为非空(非空字符串或外键)之前执行null/空字符串或外键
插入 beforeValidationOnCreate 在字段验证为非空(非空字符串或外键)之前执行null/空字符串或外键在插入时
更新 beforeValidationOnUpdate 在字段验证为非空(非空字符串或外键)之前执行null/空字符串或外键在更新时
删除 notDeleted 在记录未被删除(失败)时运行
保存 notSaved 在记录未被保存(失败)时运行
插入/更新 onValidationFails 在完整性验证失败后执行
插入/更新 prepareSave 在保存前执行并允许数据操作
插入/更新 validation 在字段验证为非空(非空字符串或外键)之前执行

事件

模型作为事件管理器的监听者。因此,我们只需要在模型中直接实现上述事件作为公共方法:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;

/**
 * Class Invoices
 *
 * @property string $inv_created_at
 * @property int    $inv_cst_id
 * @property int    $inv_id
 * @property string $inv_number
 * @property string $inv_title
 * @property float  $inv_total
 */
class Invoices extends Model
{
    /**
     * @var int
     */
    public $inv_cst_id;

    /**
     * @var string
     */
    public $inv_created_at;

    /**
     * @var int
     */
    public $inv_id;

    /**
     * @var string
     */
    public $inv_number;

    /**
     * @var string
     */
    public $inv_title;

    /**
     * @var float
     */
    public $inv_total;

    public function beforeValidationOnCreate()
    {
        if ($this->inv_total < 1) {
            $this->inv_total = 0;
        }
    }
}

事件可以用于在执行操作之前分配值,例如:

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;
use function str_pad;

/**
 * Class Invoices
 *
 * @property string $inv_created_at
 * @property int    $inv_cst_id
 * @property int    $inv_id
 * @property string $inv_number
 * @property string $inv_title
 * @property float  $inv_total
 */
class Invoices extends Model
{
    /**
     * @var int
     */
    public $inv_cst_id;

    /**
     * @var string
     */
    public $inv_created_at;

    /**
     * @var int
     */
    public $inv_id;

    /**
     * @var string
     */
    public $inv_number;

    /**
     * @var string
     */
    public $inv_title;

    /**
     * @var float
     */
    public $inv_total;

    public function beforeCreate()
    {
        $date     = date('YmdHis');
        $customer = substr(
            str_pad(
                $this->inv_cst_id, 6, '0', STR_PAD_LEFT
            ),
            -6
        );

        $this->inv_number = 'INV-' . $customer . '-' . $date;
    }
}

自定义事件管理器

此外,该组件与Phalcon\Events\Manager集成,这意味着我们可以创建在触发事件时运行的监听者。

<?php

namespace MyApp\Models;

use Phalcon\Mvc\Model;
use Phalcon\Events\Manager;

/**
 * Class Invoices
 *
 * @property string $inv_created_at
 * @property int    $inv_cst_id
 * @property int    $inv_id
 * @property string $inv_number
 * @property string $inv_title
 * @property float  $inv_total
 */
class Invoices extends Model
{
    /**
     * @var int
     */
    public $inv_cst_id;

    /**
     * @var string
     */
    public $inv_created_at;

    /**
     * @var int
     */
    public $inv_id;

    /**
     * @var string
     */
    public $inv_number;

    /**
     * @var string
     */
    public $inv_title;

    /**
     * @var float
     */
    public $inv_total;

    public function initialize()
    {
        $eventsManager = new Manager();

        $eventsManager->attach(
            'model:beforeSave',
            function (Event $event, $invoice) {
                if ($invoice->inv_total < 1) {
                    return false;
                }

                return true;
            }
        );

        $this->setEventsManager($eventsManager);
    }
}

在上面给出的示例中,事件管理器仅充当对象和监听者(匿名函数)之间的桥梁。当Invoices保存时会向监听者触发事件:

<?php

use MyApp\Models\Invoices;

$invoice = new Invoices();
$invoice->inv_cst_id = 10;
$invoice->inv_title = 'Invoice for ACME Inc.';

$invoice->save();

如果我们希望应用程序中创建的所有对象都使用相同的 EventsManager,则需要在 DI 容器中设置 Models Manager 时分配它:

<?php

use MyApp\Models\Invoices;
use Phalcon\Di\FactoryDefault;
use Phalcon\Events\Event;
use Phalcon\Events\Manager;
use Phalcon\Mvc\Model\Manager as ModelsManager;

$container = new FactoryDefault();
$container->setShared(
    'modelsManager',
    function () {
        $eventsManager = new Manager();

        $eventsManager->attach(
            'model:beforeSave',
            function (Event $event, $model) {
                if (get_class($model) === Invoices::class) {
                    if ($model->inv_total < 1) {
                        return false;
                    }
                }

                return true;
            }
        );

        $modelsManager = new ModelsManager();
        $modelsManager->setEventsManager($eventsManager);

        return $modelsManager;
    }
);

如果监听者返回 false,这将停止当前正在执行的操作。

记录 SQL 语句

当使用高级抽象组件如Phalcon\Mvc\Model来访问数据库时,很难理解最终发送到数据库系统的语句是什么。Phalcon\Mvc\Model内部由Phalcon\Db. Phalcon\Logger\LoggerPhalcon\Db交互,提供数据库抽象层上的日志记录功能,从而允许我们记录发生的 SQL 语句。

<?php

use Phalcon\Db\Adapter\Pdo\Mysql;
use Phalcon\Di\FactoryDefault;
use Phalcon\Events\Manager;
use Phalcon\Logger\Logger;
use Phalcon\Logger\Adapter\Stream;

$container = new FactoryDefault();
$container->set(
    'db',
    function () {
        $eventsManager = new Manager();
        $adapter = new Stream('/storage/logs/db.log');
        $logger  = new Logger(
            'messages',
            [
                'main' => $adapter,
            ]
        );

        $eventsManager->attach(
            'db:beforeQuery',
            function ($event, $connection) use ($logger) {
                $logger->info(
                    $connection->getSQLStatement()
                );
            }
        );

        $connection = new Mysql\(
            [
                'host'     => 'localhost',
                'username' => 'root',
                'password' => 'secret',
                'dbname'   => 'phalcon',
            ]
        );

        $connection->setEventsManager($eventsManager);

        return $connection;
    }
);

由于模型访问默认数据库连接,所有发送到数据库系统的 SQL 语句都将记录在文件中:

<?php

use MyApp\Models\Invoices;

$invoice = new Invoices();
$invoice->inv_cst_id = 10;
$invoice->inv_title  = 'Invoice for ACME Inc.';
$invoice->inv_total  = 10000;

if ($invoice->save() === false) {
    echo 'Cannot save robot';
}

如上所述,文件/storage/logs/db.log将包含类似以下内容:

日志

[Mon, 30 Apr 12 13:47:18 -0500][DEBUG][Resource Id #77] INSERT INTO co_invoices (inv_cst_id, inv_title, inv_total) VALUES (10, 'Invoice for ACME Inc.', 10000)

分析 SQL 语句

使用Phalcon\Db,这是Phalcon\Mvc\Model的底层组件,可以对 ORM 生成的 SQL 语句进行分析以评估数据库操作的性能。通过分析日志可以帮助识别 SQL 代码中的瓶颈:

<?php

use Phalcon\Db\Profiler;
use Phalcon\Di\FactoryDefault;
use Phalcon\Events\Manager;
use Phalcon\Db\Adapter\Pdo;

$container = new FactoryDefault();
$container->set(
    'profiler',
    function () {
        return new Profiler();
    },
    true
);

$container->set(
    'db',
    function () use ($container) {
        $manager  = new Manager();
        $profiler = $container->getProfiler();

        $manager->attach(
            'db',
            function ($event, $connection) use ($profiler) {
                if ($event->getType() === 'beforeQuery') {
                    $profiler->startProfile(
                        $connection->getSQLStatement()
                    );
                }

                if ($event->getType() === 'afterQuery') {
                    $profiler->stopProfile();
                }
            }
        );

        $connection = new Mysql(
            [
                'host'     => 'localhost',
                'username' => 'root',
                'password' => 'secret',
                'dbname'   => 'phalcon',
            ]
        );

        $connection->setEventsManager($manager);

        return $connection;
    }
);

分析某些查询:

<?php

use MyApp\Models\Invoices;

Invoices::find();
Invoices::find(
    [
        'order' => 'inv_cst_id, inv_title',
    ]
);
Invoices::find(
    [
        'limit' => 30,
    ]
);

$profiles = $container->get('profiler')->getProfiles();

foreach ($profiles as $profile) {
    echo 'SQL: ', 
        $profile->getSQLStatement(), 
        PHP_EOL,
        'Start: ',
        $profile->getInitialTime(),
        PHP_EOL,
        'Final: ',
        $profile->getFinalTime(),
        PHP_EOL,
        'Elapsed: ',
        $profile->getTotalElapsedSeconds(),
        PHP_EOL
    );
}

每个生成的分析记录包含每条指令完成所需的时间(以毫秒为单位),以及生成的 SQL 语句。

无噪 Logo
无噪文档
25 年 6 月翻译
文档源↗