Doctrine Doctor

Find Doctrine ORM problems while they are still easy to fix.

Doctrine Doctor watches real queries in the Symfony Web Profiler and checks source code, mappings, and optional database configuration in CI. Each finding includes context and a practical next step.

Get started now View on GitHub


PHP 8.4+ Symfony 6.x | 7.x | 8.x Doctrine ORM License MIT CI PHPStan Level 8


One tool, two useful moments

Doctrine Doctor complements PHPStan and Psalm with checks focused on persistence:

When you need an answer Where Doctrine Doctor helps
A page is slow or triggers unexpected SQL The Web Profiler shows request queries, patterns, timings, and backtraces.
A change may introduce a persistence problem CI checks source code, mappings, and configuration before merge.
You want to inspect the configured database Opt in with --with-database when a live database is available.

Symfony Web Profiler in focus with a real PGI Doctrine Doctor CLI result inset showing 50 analyzers and 129 findings


What it checks

Area Examples
Performance N+1 queries, slow queries, missing indexes, excessive hydration, unbounded reads, and inefficient joins
Security DQL/SQL injection risks, sensitive data exposure, and insecure randomness
Integrity Cascade and orphan-removal issues, mapping inconsistencies, type mismatches, and invalid entity boundaries
Configuration Charset, collation, timezone, strict mode, cache, and platform configuration

See Profiler and CI Checks to choose where each check runs. The analyzer catalog contains the complete list.


Quick start

Step 1: Install

composer require --dev ahmed-bhs/doctrine-doctor

Step 2: Load a page in your Symfony app.

The bundle is auto-configured through Symfony Flex. No YAML is required for the first run.

Step 3: Open the profiler panel.

Open the Web Profiler in the dev environment and select the Doctrine Doctor panel.

To add deterministic checks to CI:

php bin/console doctrine:doctor:analyze --fail-on=warning

Add --with-database only in a job that intentionally provides a live database.


Configuration (Optional)

Configure thresholds in config/packages/dev/doctrine_doctor.yaml:

doctrine_doctor:
    analyzers:
        n_plus_one:
            threshold: 5  # default, lower to 3 to be stricter
        slow_query:
            threshold: 100  # milliseconds (default)

Enable backtraces to see WHERE in your code issues originate:

# config/packages/dev/doctrine.yaml
doctrine:
    dbal:
        profiling_collect_backtrace: true

Full configuration reference →


Example: N+1 Query Detection

Problem: Template triggers lazy loading

// Controller
$users = $repository->findAll();

// Template
{% for user in users %}
    {{ user.profile.bio }}
{% endfor %}

Triggers 100 queries

Detection: Doctrine Doctor detects N+1

  • 100 queries instead of 1
  • Shows exact query count, execution time
  • Suggests eager loading

Real-time detection

Solution: Eager load with JOIN

$users = $repository
    ->createQueryBuilder('u')
    ->leftJoin('u.profile', 'p')
    ->addSelect('p')
    ->getQuery()
    ->getResult();

Single query


Documentation

Document Description
Configuration Reference Comprehensive guide to all configuration options - customize analyzers, thresholds, and outputs to match your workflow
Profiler and CI Checks Choose where to run Doctrine checks
Full Analyzers List Browse the built-in checks for performance, security, code quality, and configuration
Architecture Guide Deep dive into system design, architecture patterns, and technical internals
Template Security Essential security best practices for PHP templates - prevent XSS attacks and ensure safe template rendering

Contributing

We welcome contributions! See our Contributing Guide for details.


License

MIT License - see LICENSE for details.


Created by Ahmed EBEN HASSINE

Sponsor on GitHub Buy Me A Coffee