Glossary

Doctrine ORM: what it solves and when to use SQL directly

An ORM maps objects to relational data. But it does not replace constraints, transactions, indexes, or an understanding of the resulting SQL.

Short definition

Working with objects on top of a relational database.

A developer works, for example, with an order object, a customer, and order items; based on the mapping, Doctrine reads and writes tables, columns, and relationships. ORM stands for object-relational mapper and helps with common transactional parts of an application.

Entities are not automatically a domain model. In a simple application, the two may reasonably be the same, but in a more complex domain, persistence requirements such as IDs, nullable columns, and lazy collections may not belong in a clean business layer.

Use cases

Where an ORM makes work easier

Doctrine ORM works well where a system creates, changes, and retrieves connected data.

  • orders, customers, products, and order items
  • users, roles, and permissions
  • administration, workflows, and returns or claims
  • storing the results of imports and synchronisations
  • routine application logic working with objects and their relationships

Practical example

A marketplace order and its items

An import service looks up an order by its external ID. If it does not exist, it creates the order, a customer according to the system’s rules, and separate item entities; if it already exists, it updates only the data that the marketplace actually controls.

A unique database constraint on external_order_id protects against two imports running concurrently. Doctrine writes the order and its items within a single transactional unit. A revenue overview, however, can use one aggregate SQL query instead of loading the entire object graph.

How it works

From an object to a database change

Doctrine does not change the database every time a property is set. It tracks changes and writes them within a defined unit of work.

  1. Loading or creation The application retrieves or creates an Order entity and its related objects.
  2. Mapping Metadata defines the table, columns, value types, and relationship ownership.
  3. Entity Manager It manages entities, loading, write scheduling, and communication with the database.
  4. Unit of Work It tracks new, changed, and removed objects and determines the required SQL statements.
  5. Flush and transactions Changes are written within a clearly chosen transaction boundary, or none of them are applied if an error occurs.

Important characteristics

Concepts that affect correctness and performance

The convenience of an ORM must not obscure what the database actually does.

Entities and mapping

Mapping describes the primary key, types, and relationships. If a marketplace order has a unique external ID, that guarantee also belongs in a unique database constraint.

Repositories and relationships

A repository should contain meaningful queries. For order items with a price and quantity, a separate entity usually makes more sense than a simple many-to-many relationship.

Lazy loading and N+1

A relationship may be loaded only when it is used. This saves data, but it can create N+1 queries in a list. The solution should be based on measurements from the specific screen, not on blindly eager-loading everything.

Migrations, DBAL, and SQL

Versioned migrations are safer than automatically synchronising the schema in production. DBAL and parameterised SQL are natural choices for reports, aggregations, and specialised performance-sensitive queries.

Benefits and limitations

An ORM does not eliminate the cost of database operations

Benefits

  • a natural way to work with connected data in application logic
  • consistent mapping of types, relationships, and repositories
  • change tracking and coordinated persistence of objects
  • the option to combine it with DBAL and targeted SQL for specific needs

Common mistakes

  • serialising entities directly into a public API and unintentionally loading relationships
  • unnoticed N+1 queries and an unnecessarily large Unit of Work
  • calling flush after every small change without a transactional intent
  • assuming that an ORM automatically solves database integrity and performance

Scope of use

An entity is not automatically the application’s only model.

An ORM often provides a readable way to work with an order, its customer, and its items. But a revenue overview across a large volume of data usually does not require loading thousands of entities—one aggregate SQL or DBAL query returning only the required figures may be more suitable.

Separating the domain model from the persistence model should be justified by real complexity. Enforcing additional layers in a simple administration interface does not help, just as hiding an expensive database query behind a vague abstraction does not help.

What to consider

Correct data takes priority over a convenient API

Good use of an ORM connects application code with deliberate database design.

  • unique and referential constraints reflecting business rules
  • clear transaction boundaries for related changes
  • measuring SQL queries and addressing N+1 issues in a targeted way
  • versioned, verified, and backward-compatible migrations
  • DTOs or an explicit response model instead of direct entity serialisation

Common questions

What Doctrine ORM is and is not

Is Doctrine ORM a database?

No. It is a PHP library for working with a relational database. The data may be stored in PostgreSQL, MySQL, or SQLite, for example.

Do I have to write every query through Doctrine ORM?

No. You can use Doctrine DBAL or direct parameterised SQL alongside the ORM. The choice depends on the nature and readability of the query.

Should flush be called after every change?

Not necessarily. For a typical request, it may be natural to persist related changes together. For a large import, the work must be processed in batches and the size of the Unit of Work kept under control.

Does an ORM solve the N+1 problem automatically?

No. Lazy loading can actually conceal an N+1 issue. You need to observe the actual SQL and adjust loading for the specific use case.

How I use Doctrine ORM in practice

I address the data first and choose the tool second.

I use Doctrine for routine work with relational models in Symfony applications. For specialised and performance-sensitive queries, I choose targeted SQL.

Request a call

I will call you on the next working day between 9:00 and 17:00.

You can also call me directly.

+420 605 181 728

Leave your phone number and send a callback request.

By sending, you agree to processing your data in order to handle your request.