Pular para o conteúdo principal

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:

EventFired
BeforeQueryBefore a query (getIterator/getScalar) is executed on the database
AfterQueryAfter a query was executed on the database
BeforeExecuteBefore a command (execute/executeAndGetId) is executed
AfterExecuteAfter a command was executed

Notes:

  • Queries answered from the cache do not fire query events (no database round-trip happens).
  • executeAndGetId() fires the BeforeExecute/AfterExecute events for the INSERT statement and the BeforeQuery/AfterQuery events 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(): the DatabaseEventTypeEnum fired;
  • getStatement(): the SqlStatement (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(): true for AfterExecute; null for the other events.

See Also​