Executor Observers
Starting from version 7.0, you can attach observers to a DatabaseExecutor to be notified about
queries and commands executed against the database. This is the extension point used by features
such as the Journal, and can also be used for auditing, metrics or custom query logging.
Events
The events are defined by the DatabaseEventTypeEnum:
| Event | Fired |
|---|---|
BeforeQuery | Before a query (getIterator/getScalar) is executed on the database |
AfterQuery | After a query was executed on the database |
BeforeExecute | Before a command (execute/executeAndGetId) is executed |
AfterExecute | After a command was executed |
Notes:
- Queries answered from the cache do not fire query events (no database round-trip happens).
executeAndGetId()fires theBeforeExecute/AfterExecuteevents for the INSERT statement and theBeforeQuery/AfterQueryevents for the statement retrieving the generated ID (when the dialect runs one).- Observers are attached to a specific executor instance: two executors created from the same driver do not share observers. Attach the observer to the executor instance the code under observation actually uses.
Creating an Observer
Implement the DatabaseEventObserverInterface:
<?php
use ByJG\AnyDataset\Db\DatabaseEvent;
use ByJG\AnyDataset\Db\DatabaseEventTypeEnum;
use ByJG\AnyDataset\Db\Interfaces\DatabaseEventObserverInterface;
class QueryTimer implements DatabaseEventObserverInterface
{
private array $start = [];
public array $timings = [];
public function subscribedEvents(): array
{
return [DatabaseEventTypeEnum::BeforeQuery, DatabaseEventTypeEnum::AfterQuery];
}
public function handleEvent(DatabaseEvent $event): void
{
if ($event->getType() === DatabaseEventTypeEnum::BeforeQuery) {
$this->start[] = microtime(true);
return;
}
$this->timings[] = [
'sql' => $event->getStatement()->getSql(),
'elapsed' => microtime(true) - array_pop($this->start),
];
}
}
Attaching and Detaching
<?php
use ByJG\AnyDataset\Db\DatabaseExecutor;
use ByJG\AnyDataset\Db\Factory;
$executor = DatabaseExecutor::using(Factory::getDbInstance('mysql://user:password@host/database'));
$timer = new QueryTimer();
$executor->addObserver($timer);
$executor->getIterator('SELECT * FROM users');
$executor->removeObserver($timer);
The DatabaseEvent Object
Each notification receives a DatabaseEvent with:
getType(): theDatabaseEventTypeEnumfired;getStatement(): theSqlStatement(SQL text and parameters);getExecutor(): the executor that fired the event — observers can use it to run additional queries on the same connection (and same transaction);getResult():trueforAfterExecute;nullfor the other events.
See Also
- Journal - record INSERT/UPDATE/DELETE with old and new values
- DatabaseExecutor