Generated Columns

Generated columns, sometimes also called virtual columns, are populated by the database engine itself. They are a tool for performance optimization, to avoid calculating a value on each query.

You can define generated columns on entities and have Doctrine map the values to your entity.

Declaring a generated column

There is no explicit mapping instruction for generated columns. Instead, you specify that the column should not be written to, and define a custom column definition.

1<?php declare(strict_types=1); use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] class Person { #[ORM\Column(type: 'string')] private string $firstName; #[ORM\Column(type: 'string', name: 'name')] private string $lastName; #[ORM\Column( type: 'string', insertable: false, updatable: false, columnDefinition: "VARCHAR(255) GENERATED ALWAYS AS (concat(firstName, ' ', name) stored NOT NULL", generated: 'ALWAYS', )] private string $fullName; }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  • insertable, updatable: Setting these to false tells Doctrine to never write this column - writing to a generated column would result in an error from the database.
  • columnDefinition: We specify the full DDL to create the column. To allow to use database specific features, this attribute does not use Doctrine Query Language but native SQL. Note that you need to reference columns by their database name (either explicitly set in the mapping or per the current naming strategy). Be aware that specifying a column definition makes the SchemaTool completely ignore all other configuration for this column. See also #[Column]
  • generated: Specifying that this column is always generated tells Doctrine to update the field on the entity with the value from the database after every write operation.

Advanced example: Extracting a value from a JSON structure

Lets assume we have an entity that stores a blogpost as structured JSON. To avoid extracting all titles on the fly when listing the posts, we create a generated column with the field.

1<?php declare(strict_types=1); use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] class Article { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private int $id; /** * When working with Postgres, it is recommended to use the jsonb * format for better performance. */ #[ORM\Column(options: ['jsonb' => true])] private array $content; /** * Because we specify NOT NULL, inserting will fail if the content does * not have a string in the title field. */ #[ORM\Column( insertable: false, updatable: false, columnDefinition: "VARCHAR(255) generated always as (content->>'title') stored NOT NULL", generated: 'ALWAYS', )] private string $title; }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34