События модели
События и менеджер событий
Модели позволяют вам реализовать события, которые будут срабатывать во время выполнения операций вставки/обновления/удаления, что может быть использовано для определения бизнес-правил. Ниже приведены поддерживаемые события Phalcon\Mvc\Model и их порядок выполнения:
| Операция | Имя | Можно остановить операцию? | Описание |
|---|---|---|---|
| Вставка/Обновление | beforeValidation | ДА | Выполняется перед валидацией полей на предмет отсутствия значений NULL/пустых строк или внешних ключей |
| Вставка | beforeValidationOnCreate | ДА | Выполняется перед валидацией полей на предмет отсутствия значений NULL/пустых строк или внешних ключей при выполнении операции вставки |
| Обновление | beforeValidationOnUpdate | ДА | Выполняется перед валидацией полей на предмет отсутствия значений NULL/пустых строк или внешних ключей при выполнении операции обновления |
| Вставка/Обновление | onValidationFails | ДА (уже остановлено) | Выполняется после того, как валидатор целостности завершил работу с ошибкой |
| Вставка | afterValidationOnCreate | ДА | Выполняется после валидации полей на предмет отсутствия значений NULL/пустых строк или внешних ключей при выполнении операции вставки |
| Обновление | afterValidationOnUpdate | ДА | Выполняется после валидации полей на предмет отсутствия значений NULL/пустых строк или внешних ключей при выполнении операции обновления |
| Вставка/Обновление | afterValidation | ДА | Выполняется после валидации полей на предмет отсутствия значений NULL/пустых строк или внешних ключей |
| Вставка/Обновление | beforeSave | ДА | Выполняется перед необходимой операцией над системой базы данных |
| Обновление | beforeUpdate | ДА | Выполняется перед необходимой операцией над системой базы данных только при выполнении операции обновления |
| Вставка | beforeCreate | ДА | Выполняется перед необходимой операцией над системой базы данных только при выполнении операции вставки |
| Обновление | afterUpdate | НЕТ | Выполняется после необходимой операции над системой базы данных только при выполнении операции обновления |
| Вставка | afterCreate | НЕТ | Выполняется после необходимой операции над системой базы данных только при выполнении операции вставки |
| Вставка/Обновление | afterSave | НЕТ | Выполняется после необходимой операции над системой базы данных |
Реализация событий в классе модели
Проще всего заставить модель реагировать на события, реализовав метод с тем же именем, что и событие, в классе модели:
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function beforeValidationOnCreate()
{
echo "This is executed before creating a Robot!";
}
}
События могут использоваться для присвоения значений перед выполнением операции, например:
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");
}
}
Использование пользовательского менеджера событий
Кроме того, этот компонент интегрирован с Phalcon\Events\Manager, что означает, что мы можем создавать слушателей, которые запускаются при срабатывании события.
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);
}
}
В приведенном выше примере менеджер событий только действует как посредник между объектом и слушателем (анонимной функцией). События будут отправляться слушателю при сохранении «роботов»:
use Store\Toys\Robots; $robot = new Robots(); $robot->name = "Scooby Doo"; $robot->year = 1969; $robot->save();
Если мы хотим, чтобы все созданные объекты в нашем приложении использовали один и тот же EventsManager, то нам нужно назначить его менеджеру моделей:
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;
}
);
Если слушатель возвращает false, это остановит выполняющуюся операцию.
Ведение журнала низкоуровневых SQL-запросов
При использовании высокоуровневых абстрактных компонентов, таких как Phalcon\Mvc\Model для доступа к базе данных, трудно понять, какие запросы в конечном итоге отправляются в систему базы данных. Phalcon\Mvc\Model поддерживается внутри Phalcon\Db. Phalcon\Logger взаимодействует с Phalcon\Db, обеспечивая возможности ведения журнала на уровне абстракции базы данных, что позволяет нам записывать SQL-запросы по мере их выполнения.
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;
}
);
Поскольку модели обращаются к подключению к базе данных по умолчанию, все SQL-запросы, которые отправляются в систему базы данных, будут записываться в файл:
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";
}
Как и выше, файл app/logs/db.log будет содержать что-то вроде этого:
[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-запросов
Благодаря Phalcon\Db, базовому компоненту Phalcon\Mvc\Model, можно профилировать SQL-запросы, генерируемые ORM, чтобы проанализировать производительность операций с базой данных. С помощью этого вы можете диагностировать проблемы производительности и найти узкие места.
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;
}
);
Профилирование некоторых запросов:
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";
}
Каждый сгенерированный профиль содержит время в миллисекундах, которое требуется для выполнения каждой инструкции, а также сгенерированный SQL-запрос.
© 2011–2017 Phalcon Framework Team
Licensed under the Creative Commons Attribution License 3.0.
https://docs.phalconphp.com/en/latest/reference/model-events.html