Skip to content

Commit a46ff16

Browse files
authored
Merge pull request #12414 from ahmed-bhs/docs/enum-type-mapping
Add documentation for enumType mapping with PHP backed enums
2 parents 94e60e4 + de4ec20 commit a46ff16

4 files changed

Lines changed: 211 additions & 1 deletion

File tree

docs/en/reference/basic-mapping.rst

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ Here is a complete list of ``Column``s attributes (all optional):
240240
- ``insertable`` (default: ``true``): Whether the column should be inserted.
241241
- ``updatable`` (default: ``true``): Whether the column should be updated.
242242
- ``generated`` (default: ``null``): Whether the generated strategy should be ``'NEVER'``, ``'INSERT'`` and ``ALWAYS``.
243-
- ``enumType`` (requires PHP 8.1 and ``doctrine/orm`` 2.11): The PHP enum class name to convert the database value into.
243+
- ``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`.
244244
- ``precision`` (default: 0): The precision for a decimal (exact numeric) column
245245
(applies only for decimal column),
246246
which is the maximum number of digits that are stored for the values.
@@ -311,6 +311,160 @@ and a custom ``Doctrine\ORM\Mapping\TypedFieldMapper`` implementation.
311311

312312
:doc:`Read more about TypedFieldMapper <typedfieldmapper>`.
313313

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

316470
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+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Entity;
6+
7+
enum Suit: string
8+
{
9+
case Hearts = 'hearts';
10+
case Diamonds = 'diamonds';
11+
case Clubs = 'clubs';
12+
case Spades = 'spades';
13+
}

0 commit comments

Comments
 (0)