Skip to content

Commit e88cd59

Browse files
authored
Merge pull request #12423 from greg0ire/3.6.x
Merge 2.20.x up into 3.6.x
2 parents 580a95c + 88a8f75 commit e88cd59

17 files changed

Lines changed: 405 additions & 13 deletions

.github/workflows/coding-standards.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ on:
2424

2525
jobs:
2626
coding-standards:
27-
uses: "doctrine/.github/.github/workflows/coding-standards.yml@13.1.0"
27+
uses: "doctrine/.github/.github/workflows/coding-standards.yml@14.0.0"

.github/workflows/composer-lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ on:
1717
jobs:
1818
composer-lint:
1919
name: "Composer Lint"
20-
uses: "doctrine/.github/.github/workflows/composer-lint.yml@13.1.0"
20+
uses: "doctrine/.github/.github/workflows/composer-lint.yml@14.0.0"

.github/workflows/continuous-integration.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ jobs:
116116
if: "${{ matrix.native_lazy == '0' }}"
117117

118118
- name: "Install dependencies with Composer"
119-
uses: "ramsey/composer-install@v3"
119+
uses: "ramsey/composer-install@v4"
120120
with:
121121
composer-options: "--ignore-platform-req=php+"
122122
dependency-versions: "${{ matrix.deps }}"
@@ -257,7 +257,7 @@ jobs:
257257
if: "${{ matrix.dbal-version != 'default' }}"
258258

259259
- name: "Install dependencies with Composer"
260-
uses: "ramsey/composer-install@v3"
260+
uses: "ramsey/composer-install@v4"
261261
with:
262262
composer-options: "--ignore-platform-req=php+"
263263

@@ -331,7 +331,7 @@ jobs:
331331
extensions: "${{ matrix.extension }}"
332332

333333
- name: "Install dependencies with Composer"
334-
uses: "ramsey/composer-install@v3"
334+
uses: "ramsey/composer-install@v4"
335335
with:
336336
composer-options: "--ignore-platform-req=php+"
337337

@@ -413,7 +413,7 @@ jobs:
413413
if: "${{ matrix.dbal-version != 'default' }}"
414414

415415
- name: "Install dependencies with Composer"
416-
uses: "ramsey/composer-install@v3"
416+
uses: "ramsey/composer-install@v4"
417417
with:
418418
composer-options: "--ignore-platform-req=php+"
419419

@@ -470,7 +470,7 @@ jobs:
470470
path: "reports"
471471

472472
- name: "Upload to Codecov"
473-
uses: "codecov/codecov-action@v5"
473+
uses: "codecov/codecov-action@v6"
474474
with:
475475
directory: reports
476476
env:

.github/workflows/documentation.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ on:
1717
jobs:
1818
documentation:
1919
name: "Documentation"
20-
uses: "doctrine/.github/.github/workflows/documentation.yml@13.1.0"
20+
uses: "doctrine/.github/.github/workflows/documentation.yml@14.0.0"

.github/workflows/phpbench.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
ini-values: "zend.assertions=1, apc.enable_cli=1"
4949

5050
- name: "Install dependencies with Composer"
51-
uses: "ramsey/composer-install@v3"
51+
uses: "ramsey/composer-install@v4"
5252

5353
- name: "Run PHPBench"
5454
run: "vendor/bin/phpbench run --report=default"

.github/workflows/release-on-milestone-closed.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77

88
jobs:
99
release:
10-
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@13.1.0"
10+
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@14.0.0"
1111
secrets:
1212
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
1313
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}

.github/workflows/static-analysis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050

5151

5252
- name: Install dependencies with Composer
53-
uses: ramsey/composer-install@v2
53+
uses: ramsey/composer-install@v4
5454

5555
- name: Run static analysis with phpstan/phpstan
5656
run: "vendor/bin/phpstan analyse -c ${{ matrix.config }} --error-format=checkstyle | cs2pr"

docs/en/reference/basic-mapping.rst

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ Here is a complete list of ``Column``s attributes (all optional):
168168
- ``insertable`` (default: ``true``): Whether the column should be inserted.
169169
- ``updatable`` (default: ``true``): Whether the column should be updated.
170170
- ``generated`` (default: ``null``): Whether the generated strategy should be ``'NEVER'``, ``'INSERT'`` and ``ALWAYS``.
171-
- ``enumType`` (requires PHP 8.1 and ``doctrine/orm`` 2.11): The PHP enum class name to convert the database value into.
171+
- ``enumType`` (requires PHP 8.1 and ``doctrine/orm`` 2.11): The PHP enum class name to convert the database value into. See :ref:`reference-enum-mapping`.
172172
- ``precision`` (default: 0): The precision for a decimal (exact numeric) column
173173
(applies only for decimal column),
174174
which is the maximum number of digits that are stored for the values.
@@ -321,6 +321,160 @@ that value and raw value are different, you have to use the raw value representa
321321
$messageRepository = $entityManager->getRepository(Message::class);
322322
$deMessages = $messageRepository->findBy(['language' => 'de']); // Use lower case here for raw value representation
323323
324+
.. _reference-enum-mapping:
325+
326+
Mapping PHP Enums
327+
-----------------
328+
329+
.. versionadded:: 2.11
330+
331+
Doctrine natively supports mapping PHP backed enums to database columns.
332+
A backed enum is a PHP enum that the same scalar type (``string`` or ``int``)
333+
assigned to each case. Doctrine stores the scalar value in the database and
334+
converts it back to the enum instance when hydrating the entity.
335+
336+
Using ``enumType`` provides three main benefits:
337+
338+
- **Automatic conversion**: Doctrine handles the conversion in both directions
339+
transparently. When loading an entity, scalar values from the database are
340+
converted into enum instances. When persisting, enum instances are reduced
341+
to their scalar ``->value`` before being sent to the database.
342+
- **Type-safety**: Entity properties contain enum instances directly. Your
343+
getters return ``Suit`` instead of ``string``, removing the need to call
344+
``Suit::from()`` manually.
345+
- **Validation**: When a database value does not match any enum case, Doctrine
346+
throws a ``MappingException`` during hydration instead of silently returning
347+
an invalid value.
348+
349+
This feature works with all database platforms supported by Doctrine (MySQL,
350+
PostgreSQL, SQLite, etc.) as it relies on standard column types (``string``,
351+
``integer``, ``json``, ``simple_array``) rather than any vendor-specific enum
352+
type.
353+
354+
.. note::
355+
356+
This is unrelated to the MySQL-specific ``ENUM`` column type covered in
357+
:doc:`the MySQL Enums cookbook entry </cookbook/mysql-enums>`.
358+
359+
Defining an Enum
360+
~~~~~~~~~~~~~~~~
361+
362+
.. literalinclude:: basic-mapping/Suit.php
363+
:language: php
364+
365+
Only backed enums (``string`` or ``int``) are supported. Unit enums (without
366+
a scalar value) cannot be mapped.
367+
368+
Single-Value Columns
369+
~~~~~~~~~~~~~~~~~~~~
370+
371+
Use the ``enumType`` option on ``#[Column]`` to map a property to a backed enum.
372+
The underlying database column stores the enum's scalar value (``string`` or ``int``).
373+
374+
.. literalinclude:: basic-mapping/EnumMapping.php
375+
:language: php
376+
377+
When the PHP property is typed with the enum class, Doctrine automatically
378+
infers the appropriate column type (``string`` for string-backed enums,
379+
``integer`` for int-backed enums) and sets ``enumType``. You can also specify
380+
the column ``type`` explicitly.
381+
382+
Storing Collections of Enums
383+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
384+
385+
You can store multiple enum values in a single column by combining ``enumType``
386+
with a collection column type: ``json`` or ``simple_array``.
387+
388+
.. note::
389+
390+
Automatic type inference does not apply to collection columns. When the
391+
PHP property is typed as ``array``, Doctrine cannot detect the enum class.
392+
You must specify both ``type`` and ``enumType`` explicitly.
393+
394+
.. literalinclude:: basic-mapping/EnumCollectionMapping.php
395+
:language: php
396+
397+
With ``json``, the values are stored as a JSON array (e.g. ``["hearts","spades"]``).
398+
With ``simple_array``, the values are stored as a comma-separated string
399+
(e.g. ``hearts,spades``).
400+
401+
In both cases, Doctrine converts each element to and from the enum
402+
automatically during hydration and persistence.
403+
404+
.. tip::
405+
406+
Use ``json`` when enum values may contain commas, when you need to store
407+
int-backed enums (as it preserves value types), when the column also
408+
stores complex/nested data structures, or when you want to query individual
409+
values using database-native JSON operators (e.g. PostgreSQL ``jsonb``).
410+
Prefer ``simple_array`` for a compact, human-readable storage of
411+
string-backed enums whose values do not contain commas.
412+
413+
+-------------------+-----------------------------+-------------------------------+
414+
| Column type | Database storage | PHP type |
415+
+===================+=============================+===============================+
416+
| ``string`` | ``hearts`` | ``Suit`` |
417+
+-------------------+-----------------------------+-------------------------------+
418+
| ``integer`` | ``1`` | ``Priority`` |
419+
+-------------------+-----------------------------+-------------------------------+
420+
| ``json`` | ``["hearts","spades"]`` | ``array<Suit>`` |
421+
+-------------------+-----------------------------+-------------------------------+
422+
| ``simple_array`` | ``hearts,spades`` | ``array<Suit>`` |
423+
+-------------------+-----------------------------+-------------------------------+
424+
425+
Nullable Enums
426+
~~~~~~~~~~~~~~
427+
428+
Enum columns can be nullable. When the database value is ``NULL``, Doctrine
429+
preserves it as ``null`` without triggering any validation error.
430+
431+
.. code-block:: php
432+
433+
<?php
434+
#[ORM\Column(type: 'string', nullable: true, enumType: Suit::class)]
435+
private Suit|null $suit = null;
436+
437+
Default Values
438+
~~~~~~~~~~~~~~
439+
440+
You can specify a database-level default using an enum case directly in the
441+
column options:
442+
443+
.. code-block:: php
444+
445+
<?php
446+
#[ORM\Column(options: ['default' => Suit::Hearts])]
447+
public Suit $suit;
448+
449+
Using Enums in Queries
450+
~~~~~~~~~~~~~~~~~~~~~~
451+
452+
Enum instances can be used directly as parameters in DQL, QueryBuilder, and
453+
repository methods. Doctrine converts them to their scalar value automatically.
454+
455+
.. code-block:: php
456+
457+
<?php
458+
// QueryBuilder
459+
$qb = $em->createQueryBuilder();
460+
$qb->select('c')
461+
->from(Card::class, 'c')
462+
->where('c.suit = :suit')
463+
->setParameter('suit', Suit::Clubs);
464+
465+
// Repository
466+
$cards = $em->getRepository(Card::class)->findBy(['suit' => Suit::Clubs]);
467+
468+
XML Mapping
469+
~~~~~~~~~~~
470+
471+
When using XML mapping, the ``enum-type`` attribute is used on ``<field>``
472+
elements:
473+
474+
.. code-block:: xml
475+
476+
<field name="suit" type="string" enum-type="App\Entity\Suit" />
477+
324478
.. _reference-mapping-types:
325479

326480
Doctrine Mapping Types
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Entity;
6+
7+
use Doctrine\ORM\Mapping as ORM;
8+
9+
#[ORM\Entity]
10+
class Player
11+
{
12+
#[ORM\Id]
13+
#[ORM\GeneratedValue]
14+
#[ORM\Column]
15+
private int $id;
16+
17+
/** @var list<Suit> */
18+
#[ORM\Column(type: 'json', enumType: Suit::class)]
19+
private array $favouriteSuits = [];
20+
21+
/** @var list<Suit> */
22+
#[ORM\Column(type: 'simple_array', enumType: Suit::class)]
23+
private array $allowedSuits = [];
24+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Entity;
6+
7+
use Doctrine\ORM\Mapping as ORM;
8+
9+
#[ORM\Entity]
10+
class Card
11+
{
12+
#[ORM\Id]
13+
#[ORM\GeneratedValue]
14+
#[ORM\Column]
15+
private int $id;
16+
17+
#[ORM\Column(enumType: Suit::class)]
18+
private Suit $suit;
19+
}

0 commit comments

Comments
 (0)