diff --git a/docs/en/cookbook/generated-columns.rst b/docs/en/cookbook/generated-columns.rst new file mode 100644 index 00000000000..40b25a447d4 --- /dev/null +++ b/docs/en/cookbook/generated-columns.rst @@ -0,0 +1,44 @@ +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. + +.. code-block:: php + .. literalinclude:: generated-columns/Person.php + +* ``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 + :doc:`naming strategy <../reference/namingstrategy>`). + Be aware that specifying a column definition makes the ``SchemaTool`` + completely ignore all other configuration for this column. See also + :ref:`#[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. + +.. code-block:: php + .. literalinclude:: generated-columns/Article.php \ No newline at end of file diff --git a/docs/en/cookbook/generated-columns/Article.php b/docs/en/cookbook/generated-columns/Article.php new file mode 100644 index 00000000000..fdcde391a9d --- /dev/null +++ b/docs/en/cookbook/generated-columns/Article.php @@ -0,0 +1,33 @@ + 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; +} diff --git a/docs/en/cookbook/generated-columns/Person.php b/docs/en/cookbook/generated-columns/Person.php new file mode 100644 index 00000000000..6e4f25f4ede --- /dev/null +++ b/docs/en/cookbook/generated-columns/Person.php @@ -0,0 +1,24 @@ +` \| + :doc:`Generated/Virtual Columns ` \| :doc:`Decorator Pattern ` \| :doc:`Strategy Pattern ` @@ -123,4 +124,5 @@ Cookbook * **Custom Datatypes** :doc:`MySQL Enums ` + :doc:`Custom Mapping Types ` :doc:`Advanced Field Value Conversion ` diff --git a/docs/en/reference/attributes-reference.rst b/docs/en/reference/attributes-reference.rst index 8709cda6279..449d369f70a 100644 --- a/docs/en/reference/attributes-reference.rst +++ b/docs/en/reference/attributes-reference.rst @@ -213,12 +213,15 @@ Optional parameters: - ``check``: Adds a check constraint type to the column (might not be supported by all vendors). -- **columnDefinition**: DDL SQL snippet that starts after the column +- **columnDefinition**: Specify the DDL SQL snippet that starts after the column name and specifies the complete (non-portable!) column definition. This attribute allows to make use of advanced RMDBS features. - However you should make careful use of this feature and the - consequences. ``SchemaTool`` will not detect changes on the column correctly - anymore if you use ``columnDefinition``. + However, as this needs to be specified in the DDL native to the database, + the resulting schema changes are no longer portable. If you specify a + ``columnDefinition``, the ``SchemaTool`` ignores all other attributes + that are normally used to build the definition DDL. Changes to the + ``columnDefinition`` are not detected, you will need to manually create a + migration to apply changes. Additionally you should remember that the ``type`` attribute still handles the conversion between PHP and Database @@ -261,10 +264,11 @@ Examples: )] protected $loginCount; - // MySQL example: full_name char(41) GENERATED ALWAYS AS (concat(firstname,' ',lastname)), + // columnDefinition is raw SQL, not DQL. This example works for MySQL: #[Column( type: "string", name: "user_fullname", + columnDefinition: "VARCHAR(255) GENERATED ALWAYS AS (concat(firstname,' ',lastname))", insertable: false, updatable: false )] @@ -366,7 +370,7 @@ Optional parameters: - **type**: By default this is string. - **length**: By default this is 255. -- **columnDefinition**: By default this is null the definition according to the type will be used. This option allows to override it. +- **columnDefinition**: Allows to override how the column is generated. See the "columnDefinition" attribute on :ref:`#[Column] ` - **enumType**: By default this is `null`. Allows to map discriminatorColumn value to PHP enum - **options**: See "options" attribute on :ref:`#[Column] `. @@ -678,8 +682,10 @@ Optional parameters: - **onDelete**: Cascade Action (Database-level) - **columnDefinition**: DDL SQL snippet that starts after the column name and specifies the complete (non-portable!) column definition. - This attribute enables the use of advanced RMDBS features. Using - this attribute on ``#[JoinColumn]`` is necessary if you need slightly + This attribute enables the use of advanced RMDBS features. Note that you + need to reference columns by their database name (either explicitly set in + the mapping or per the current :doc:`naming strategy `). + Using this attribute on ``#[JoinColumn]`` is necessary if you need different column definitions for joining columns, for example regarding NULL/NOT NULL defaults. However by default a "columnDefinition" attribute on :ref:`#[Column] ` also sets diff --git a/docs/en/sidebar.rst b/docs/en/sidebar.rst index b94c81fdd48..f8e8c4683c3 100644 --- a/docs/en/sidebar.rst +++ b/docs/en/sidebar.rst @@ -65,6 +65,7 @@ cookbook/decorator-pattern cookbook/dql-custom-walkers cookbook/dql-user-defined-functions + cookbook/generated-columns cookbook/implementing-arrayaccess-for-domain-objects cookbook/implementing-the-notify-changetracking-policy cookbook/resolve-target-entity-listener