Model Events ============ イベントとイベント・マネージャ ------------------------------ Models allow you to implement events that will be thrown while performing an insert/update/delete which can be used to define business rules. The following are the events supported by :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` and their order of execution: +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Operation | Name | Can stop operation? | Explanation | +====================+==========================+=======================+===================================================================================================================================+ | Inserting/Updating | beforeValidation | YES | Is executed before the fields are validated for not nulls/empty strings or foreign keys | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Inserting | beforeValidationOnCreate | YES | Is executed before the fields are validated for not nulls/empty strings or foreign keys when an insertion operation is being made | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Updating | beforeValidationOnUpdate | YES | Is executed before the fields are validated for not nulls/empty strings or foreign keys when an updating operation is being made | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Inserting/Updating | onValidationFails | YES (already stopped) | Is executed after an integrity validator fails | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Inserting | afterValidationOnCreate | YES | Is executed after the fields are validated for not nulls/empty strings or foreign keys when an insertion operation is being made | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Updating | afterValidationOnUpdate | YES | Is executed after the fields are validated for not nulls/empty strings or foreign keys when an updating operation is being made | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Inserting/Updating | afterValidation | YES | Is executed after the fields are validated for not nulls/empty strings or foreign keys | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Inserting/Updating | beforeSave | YES | Runs before the required operation over the database system | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Updating | beforeUpdate | YES | Runs before the required operation over the database system only when an updating operation is being made | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Inserting | beforeCreate | YES | Runs before the required operation over the database system only when an inserting operation is being made | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Updating | afterUpdate | NO | Runs after the required operation over the database system only when an updating operation is being made | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Inserting | afterCreate | NO | Runs after the required operation over the database system only when an inserting operation is being made | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ | Inserting/Updating | afterSave | NO | Runs after the required operation over the database system | +--------------------+--------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------------------------------+ モデルクラス内でのイベントの実装 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The easier way to make a model react to events is to implement a method with the same name of the event in the model's class: .. code-block:: php 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"); } } カスタムイベントマネージャの使用 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Additionally, this component is integrated with :doc:`Phalcon\\Events\\Manager <../api/Phalcon_Events_Manager>`, this means we can create listeners that run when an event is triggered. .. code-block:: php 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: .. code-block:: php 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: .. code-block:: php 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文のロギング -------------------------------- When using high-level abstraction components such as :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` to access a database, it is difficult to understand which statements are finally sent to the database system. :doc:`Phalcon\\Mvc\\Model <../api/Phalcon_Mvc_Model>` is supported internally by :doc:`Phalcon\\Db <../api/Phalcon_Db>`. :doc:`Phalcon\\Logger <../api/Phalcon_Logger>` interacts with :doc:`Phalcon\\Db <../api/Phalcon_Db>`, providing logging capabilities on the database abstraction layer, thus allowing us to log SQL statements as they happen. .. code-block:: php 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: .. code-block:: php 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: .. code-block:: irc [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文のプロファイリング ------------------------ Thanks to :doc:`Phalcon\\Db <../api/Phalcon_Db>`, the underlying component of :doc:`Phalcon\\Mvc\\Model <../api/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. .. code-block:: php 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: .. code-block:: php "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.