模型事件(Model Events)

最后更新于:2018-09-10 23:00:39

事件与事件管理器(Events and Events Manager)

模型事件允许你在通过模型执行 插入/更新/删除(insert/update/delete)时执行额外的逻辑。目前这是Phalcon\Mvc\Model支持的事件:

操作 事务方法名 是否可以终止操作 描述
插入时/更新时 beforeValidation 在保存时检查字段是否为空或者是外键约束是否满足之前运行。(此处可简单理解未保存前执行)
插入时 beforeValidationOnCreate 在新建记录时,检查字段是否为空或者是外键约束是否满足之前运行。(此处可简单理解为新建记录前执行)
更新时 beforeValidationOnUpdate 在更新记录时,检查字段是否为空或者是外键约束是否满足之前运行。(此处可简单理解为更新记录前执行)
插入时/更新时 onValidationFails 是 (already stopped) 在模型验证失败时执行
插入时 afterValidationOnCreate 在新建记录时,检查字段是否为空或者是外键约束是否满足之后运行。(此处可简单理解为新建记录后执行)
更新时 afterValidationOnUpdate 在更新记录时,检查字段是否为空或者是外键约束是否满足之后运行。(此处可简单理解为更新记录后执行)
插入时/更新时 afterValidation 在保存时检查字段是否为空或者是外键约束是否满足之后运行。(此处可简单理解未保存后执行)
插入时/更新时 beforeSave 在数据库真正执行(新建/更新)sql前运行
更新时 beforeUpdate 在数据库真正执行(更新)sql前运行
插入时 beforeCreate 在数据库真正执行(新建)sql前运行
更新时 afterUpdate 在数据库真正执行(更新)sql后运行
插入时 afterCreate 在数据库真正执行(新建)sql后运行
插入时/更新时 afterSave 在数据库真正执行(新建/更新)sql后运行

模型中自定义事件(Implementing Events in the Model’s class)

The easier way to make a model react to events is implement a method with the same name of the event in the model’s class:

<?php

namespace Store\Toys;

use Phalcon\Mvc\Model;

class Robots extends Model
{
    public function beforeValidationOnCreate()
    {
        echo "This is executed before creating a Robot!";
    }
}

Events can be useful to assign values before performing an operation, for example:

<?php

use Phalcon\Mvc\Model;

class Products extends Model
{
    public function beforeCreate()
    {
        // Set the creation date
        $this->created_at = date("Y-m-d H:i:s");
    }

    public function beforeUpdate()
    {
        // Set the modification date
        $this->modified_in = date("Y-m-d H:i:s");
    }
}

使用自定义事件管理器(Using a custom Events Manager)

Additionally, this component is integrated with Phalcon\Events\Manager,
this means we can create listeners that run when an event is triggered.

<?php

namespace Store\Toys;

use Phalcon\Mvc\Model;
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;

class Robots extends Model
{
    public function initialize()
    {
        $eventsManager = new EventsManager();

        // Attach an anonymous function as a listener for "model" events
        $eventsManager->attach(
            "model:beforeSave",
            function (Event $event, $robot) {
                if ($robot->name === "Scooby Doo") {
                    echo "Scooby Doo isn't a robot!";

                    return false;
                }

                return true;
            }
        );

        // Attach the events manager to the event
        $this->setEventsManager($eventsManager);
    }
}

In the example given above, the Events Manager only acts as a bridge between an object and a listener (the anonymous function).
Events will be fired to the listener when ‘robots’ are saved:

<?php

use Store\Toys\Robots;

$robot = new Robots();

$robot->name = "Scooby Doo";
$robot->year = 1969;

$robot->save();

If we want all objects created in our application use the same EventsManager, then we need to assign it to the Models Manager:

<?php

use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;

// Registering the modelsManager service
$di->setShared(
    "modelsManager",
    function () {
        $eventsManager = new EventsManager();

        // Attach an anonymous function as a listener for "model" events
        $eventsManager->attach(
            "model:beforeSave",
            function (Event $event, $model) {
                // Catch events produced by the Robots model
                if (get_class($model) === "Store\\Toys\\Robots") {
                    if ($model->name === "Scooby Doo") {
                        echo "Scooby Doo isn't a robot!";

                        return false;
                    }
                }

                return true;
            }
        );

        // Setting a default EventsManager
        $modelsManager = new ModelsManager();

        $modelsManager->setEventsManager($eventsManager);

        return $modelsManager;
    }
);

If a listener returns false that will stop the operation that is executing currently.

记录底层 SQL 语句(Logging Low-Level SQL Statements)

When using high-level abstraction components such as Phalcon\Mvc\Model to access a database, it is
difficult to understand which statements are finally sent to the database system. Phalcon\Mvc\Model
is supported internally by Phalcon\Db. Phalcon\Logger interacts
with Phalcon\Db, providing logging capabilities on the database abstraction layer, thus allowing us to log SQL
statements as they happen.

<?php

use Phalcon\Logger;
use Phalcon\Events\Manager;
use Phalcon\Logger\Adapter\File as FileLogger;
use Phalcon\Db\Adapter\Pdo\Mysql as Connection;

$di->set(
    "db",
    function () {
        $eventsManager = new EventsManager();

        $logger = new FileLogger("app/logs/debug.log");

        // Listen all the database events
        $eventsManager->attach(
            "db:beforeQuery",
            function ($event, $connection) use ($logger) {
                $logger->log(
                    $connection->getSQLStatement(),
                    Logger::INFO
                );
            }
        );

        $connection = new Connection(
            [
                "host"     => "localhost",
                "username" => "root",
                "password" => "secret",
                "dbname"   => "invo",
            ]
        );

        // Assign the eventsManager to the db adapter instance
        $connection->setEventsManager($eventsManager);

        return $connection;
    }
);

As models access the default database connection, all SQL statements that are sent to the database system will be logged in the file:

<?php

use Store\Toys\Robots;

$robot = new Robots();

$robot->name       = "Robby the Robot";
$robot->created_at = "1956-07-21";

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

As above, the file app/logs/db.log will contain something like this:

[Mon, 30 Apr 12 13:47:18 -0500][DEBUG][Resource Id #77] INSERT INTO robots
(name, created_at) VALUES ('Robby the Robot', '1956-07-21')

分析 SQL 语句(Profiling SQL Statements)

Thanks to Phalcon\Db, the underlying component of Phalcon\Mvc\Model,
it’s possible to profile the SQL statements generated by the ORM in order to analyze the performance of database operations. With
this you can diagnose performance problems and to discover bottlenecks.

<?php

use Phalcon\Db\Profiler as ProfilerDb;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Db\Adapter\Pdo\Mysql as MysqlPdo;

$di->set(
    "profiler",
    function () {
        return new ProfilerDb();
    },
    true
);

$di->set(
    "db",
    function () use ($di) {
        $eventsManager = new EventsManager();

        // Get a shared instance of the DbProfiler
        $profiler = $di->getProfiler();

        // Listen all the database events
        $eventsManager->attach(
            "db",
            function ($event, $connection) use ($profiler) {
                if ($event->getType() === "beforeQuery") {
                    $profiler->startProfile(
                        $connection->getSQLStatement()
                    );
                }

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

        $connection = new MysqlPdo(
            [
                "host"     => "localhost",
                "username" => "root",
                "password" => "secret",
                "dbname"   => "invo",
            ]
        );

        // Assign the eventsManager to the db adapter instance
        $connection->setEventsManager($eventsManager);

        return $connection;
    }
);

Profiling some queries:

<?php

use Store\Toys\Robots;

// Send some SQL statements to the database
Robots::find();

Robots::find(
    [
        "order" => "name",
    ]
);

Robots::find(
    [
        "limit" => 30,
    ]
);

// Get the generated profiles from the profiler
$profiles = $di->get("profiler")->getProfiles();

foreach ($profiles as $profile) {
   echo "SQL Statement: ", $profile->getSQLStatement(), "\n";
   echo "Start Time: ", $profile->getInitialTime(), "\n";
   echo "Final Time: ", $profile->getFinalTime(), "\n";
   echo "Total Elapsed Time: ", $profile->getTotalElapsedSeconds(), "\n";
}

Each generated profile contains the duration in milliseconds that each instruction takes to complete as well as the generated SQL statement.