draw/doctrine-extra
Adds extra helpers and integrations for using Doctrine within Laravel/PHP apps, including convenience utilities to extend Doctrine’s capabilities and streamline configuration and common tasks.
Installation Require the package via Composer:
composer require draw/doctrine-extra
Ensure your project meets the PHP 8.5+ requirement and has Doctrine ORM (v3.6+) or Doctrine DBAL (v4.4+) installed.
First Use Case: Basic Query Logging
Register the DoctrineExtraLogger in your Doctrine ORM configuration (e.g., config/packages/doctrine.yaml):
doctrine:
orm:
entity_listeners:
Draw\DoctrineExtra\Logger\DoctrineExtraLogger: ~
Configure Monolog to capture logs (if not already set up):
monolog:
handlers:
doctrine_extra:
type: stream
path: "%kernel.logs_dir%/doctrine.log"
level: debug
channels: ["doctrine"]
Enable Logging in Code Use the logger directly in repositories or services:
use Draw\DoctrineExtra\Logger\DoctrineExtraLogger;
$logger = new DoctrineExtraLogger($entityManager, $monologLogger);
$logger->logQuery('SELECT * FROM users'); // Log a custom query
Leverage Doctrine's event system to log entity operations (e.g., prePersist, preUpdate):
use Draw\DoctrineExtra\Logger\DoctrineExtraLogger;
use Doctrine\ORM\Event\LifecycleEventArgs;
class UserRepository
{
public function save(User $user, bool $flush = false): void
{
$logger = new DoctrineExtraLogger($this->entityManager, $this->monologLogger);
$logger->logEntityEvent('prePersist', $user, $args);
parent::save($user, $flush);
}
}
Log SQL queries with execution time for performance analysis:
use Draw\DoctrineExtra\Logger\QueryLogger;
$logger = new QueryLogger($entityManager, $monologLogger);
$logger->startQueryTimer(); // Start timing a query
$users = $this->entityManager->createQuery('SELECT u FROM User u')->getResult();
$logger->endQueryTimer('User query'); // Log execution time
Extend Monolog to process Doctrine logs:
use Draw\DoctrineExtra\Logger\DoctrineExtraHandler;
$handler = new DoctrineExtraHandler($entityManager, 'doctrine.log');
$monolog->pushHandler($handler);
DoctrineExtraLogger to log entity associations:
$logger->logEntityRelationship($entity, 'manyToOne', 'User');
draw/graphviz):
$graphvizLogger = new GraphvizLogger($entityManager);
$dot = $graphvizLogger->generateDot($entityManager->getClassMetadata(User::class));
file_put_contents('user_relationships.dot', $dot);
dot -Tpng user_relationships.dot -o user_relationships.png.public function removeAllInactiveUsers(): void
{
$logger = new DoctrineExtraLogger($this->entityManager, $this->monologLogger);
$logger->logMassOperation('DELETE', User::class, ['active' => false]);
$this->createQueryBuilder('u')
->delete()
->where('u.active = :active')
->setParameter('active', false)
->getQuery()
->execute();
}
monolog:
channels: ['audit']
handlers:
audit:
type: fingers_crossed
action_level: error
handler: stream
formatter: monolog.formatter.json
PHP Version Strictness
The package enforces PHP 8.5+, which may conflict with Laravel's default PHP version (e.g., 8.2 in Laravel 10). Use a .php-version file or adjust your Laravel setup:
echo "8.5" > .php-version
Doctrine Event Conflicts
Multiple listeners on the same event (e.g., prePersist) may cause race conditions. Prioritize listeners in config/packages/doctrine.yaml:
doctrine:
orm:
entity_listeners:
Draw\DoctrineExtra\Logger\DoctrineExtraLogger: 100 # Higher priority
App\CustomListener: 200
Graphviz Dependencies
The GraphvizLogger requires:
graphviz PHP extension (pecl install graphviz).dot, neato). Test locally:dot -V # Should return version info
Log Bloat Logging every query or entity operation can generate GBs of logs. Use environment-based filtering:
$logger = new DoctrineExtraLogger($entityManager, $monologLogger);
if (!app()->environment('local')) {
$logger->setEnabled(false);
}
Circular References in Graphviz
Entities with circular relationships (e.g., User ↔ Profile ↔ User) may cause infinite loops. Mitigate with:
$graphvizLogger = new GraphvizLogger(['ignoreCircularReferences' => true]);
Verify Doctrine Events Ensure events are dispatched correctly by adding a debug listener:
use Doctrine\ORM\Event\OnFlushEventArgs;
$eventManager->addEventListener(Events::onFlush, function (OnFlushEventArgs $args) {
\Log::debug('Doctrine flush event triggered', ['entities' => $args->getEntityChanges()]);
});
Check Monolog Configuration Validate Monolog channels and handlers:
$monolog = app('monolog');
\Log::debug('Monolog channels:', ['channels' => $monolog->getChannels()]);
Inspect Entity Metadata Debug metadata issues:
$metadata = $entityManager->getClassMetadata(User::class);
\Log::debug('Entity metadata:', [
'name' => $metadata->name,
'mappedSuperclass' => $metadata->isMappedSuperclass(),
]);
Log Query Timings
Use QueryLogger to identify slow queries:
$logger = new QueryLogger($entityManager, $monologLogger);
$logger->logQueryTime('SELECT * FROM users', 150); // Log a query taking 150ms
Custom Log Formatters
Extend DoctrineExtraLogger to format logs for specific needs:
class CustomDoctrineLogger extends DoctrineExtraLogger
{
protected function formatEntityLog(array $data): string
{
return json_encode([
'entity' => $data['entity'],
'event' => $data['event'],
'timestamp' => now()->toIso8601String(),
]);
}
}
Add Custom Event Listeners Extend the logger to handle custom Doctrine events:
use Doctrine\ORM\Event\PostGenerateEntityIdEventArgs;
$eventManager->addEventListener(Events::postGenerateEntityId, function (PostGenerateEntityIdEventArgs $args) {
$logger = new DoctrineExtraLogger($args->getEntityManager(), $monologLogger);
$logger->logEvent('postGenerateEntityId', $args->getEntity());
});
Integrate with Laravel Logging
Bridge Doctrine logs to Laravel's Log facade:
use Draw\DoctrineExtra\Logger\DoctrineExtraLogger;
$logger = new DoctrineExtraLogger($entityManager, new MonologLoggerAdapter());
$logger->logQuery('SELECT * FROM users');
// MonologLoggerAdapter.php
class MonologLoggerAdapter
{
public function log($level, string $message, array $context = []): void
{
\Log::channel('doctrine')->$level($message, $context);
}
}
Visualization Enhancements Customize Graphviz output for specific use cases:
$graphvizLogger = new GraphvizLogger([
'attributes' => [
'graph' => ['rankdir' => 'LR'], // Left-to-right
How can I help you explore Laravel packages today?