When Doctrine Doctor recommends DBAL

Doctrine Doctor recommends DBAL only where the ORM has no equivalent, or where the code does not need the unit of work. Each suggestion says what switching bypasses.

Analyzer Recommendation Why DBAL
BulkInsertAnalyzer Chunked multi-row INSERT through executeStatement() The ORM writes one INSERT per entity and has no multi-row insert
BulkOperationAnalyzer One set-based UPDATE/DELETE, in DQL or DBAL One statement replaces one per entity; DQL is offered first
DTOHydrationAnalyzer fetchAllAssociative() as an alternative to SELECT NEW Reports and exports that only read values need no hydration
NPlusOneSqlAnalyzer IN (?) with an ArrayParameterType parameter The loop is already DBAL code; ORM lazy loading goes to NPlusOneAnalyzer, fixed with a fetch join
SQLInjectionInRawQueriesAnalyzer executeQuery() with bound, typed parameters The vulnerable code is raw SQL; the fix stays in the same layer

Where the ORM stays the recommendation

  • ORM QueryBuilder or DQL concatenation: setParameter(), never a rewrite in raw SQL.
  • N+1 from lazy loading: a fetch join or batch fetching, not hand-written SQL.
  • Imports that need the persisted objects afterwards: flush() and clear() in batches.

What DBAL bypasses

Suggestions that recommend DBAL for writes mention the parts of the ORM it skips:

  • lifecycle callbacks (prePersist, preUpdate, …) and entity listeners, read from the entity metadata;
  • Doctrine event subscribers registered on the entity manager or connection, which metadata cannot show;
  • cascades, and identifiers generated by IDENTITY or sequences, which are not set back on objects;
  • entities already loaded, which keep their old values until refreshed.

DBAL 4 API

Code shown in suggestions uses the DBAL 4 parameter API: ParameterType and ArrayParameterType in the types argument of executeQuery() and executeStatement(). PDO::PARAM_* constants are rejected by DBAL 4.