Basic Mapping
=============

This guide explains the basic mapping of entities and properties.
After working through this guide you should know:

- How to create PHP objects that can be saved to the database with Doctrine;
- How to configure the mapping between columns on tables and properties on
  entities;
- What Doctrine mapping types are;
- Defining primary keys and how identifiers are generated by Doctrine;
- How quoting of reserved symbols works in Doctrine.

Mapping of associations will be covered in the next chapter on
:doc:`Association Mapping <association-mapping>`.

Creating Classes for the Database
---------------------------------

Every PHP object that you want to save in the database using Doctrine
is called an *Entity*. The term "Entity" describes objects
that have an identity over many independent requests. This identity is
usually achieved by assigning a unique identifier to an entity.
In this tutorial the following ``Message`` PHP class will serve as the
example Entity:

.. code-block:: php

    <?php
    class Message
    {
        private $id;
        private $text;
        private $postedAt;
    }

Because Doctrine is a generic library, it only knows about your
entities because you will describe their existence and structure using
mapping metadata, which is configuration that tells Doctrine how your
entity should be stored in the database. The documentation will often
speak of "mapping something", which means writing the mapping metadata
that describes your entity.

Doctrine provides several different ways to specify object-relational
mapping metadata:

-  :doc:`Attributes <attributes-reference>`
-  :doc:`XML <xml-mapping>`
-  :doc:`PHP code <php-mapping>`
-  :doc:`Docblock Annotations <annotations-reference>` (deprecated and will be removed in ``doctrine/orm`` 3.0)
-  :doc:`YAML <yaml-mapping>` (deprecated and will be removed in ``doctrine/orm`` 3.0.)

This manual will usually show mapping metadata via attributes, though
many examples also show the equivalent configuration in annotations,
YAML and XML.

.. note::

    All metadata drivers perform equally. Once the metadata of a class has been
    read from the source (attributes, annotations, XML, etc.) it is stored in an instance
    of the ``Doctrine\ORM\Mapping\ClassMetadata`` class which are
    stored in the metadata cache.  If you're not using a metadata cache (not
    recommended!) then the XML driver is the fastest.

Marking our ``Message`` class as an entity for Doctrine is straightforward:

.. configuration-block::

    .. code-block:: attribute

        <?php
        use Doctrine\ORM\Mapping\Entity;

        #[Entity]
        class Message
        {
            // ...
        }

    .. code-block:: annotation

        <?php
        use Doctrine\ORM\Mapping\Entity;

        /** @Entity */
        class Message
        {
            // ...
        }

    .. code-block:: xml

        <doctrine-mapping>
          <entity name="Message">
              <!-- ... -->
          </entity>
        </doctrine-mapping>

    .. code-block:: yaml

        Message:
          type: entity
          # ...

With no additional information, Doctrine expects the entity to be saved
into a table with the same name as the class in our case ``Message``.
You can change this by configuring information about the table:

.. configuration-block::

    .. code-block:: attribute

        <?php
        use Doctrine\ORM\Mapping\Entity;
        use Doctrine\ORM\Mapping\Table;

        #[Entity]
        #[Table(name: 'message')]
        class Message
        {
            // ...
        }

    .. code-block:: annotation

        <?php
        use Doctrine\ORM\Mapping\Entity;
        use Doctrine\ORM\Mapping\Table;

        /**
         * @Entity
         * @Table(name="message")
         */
        class Message
        {
            // ...
        }

    .. code-block:: xml

        <doctrine-mapping>
          <entity name="Message" table="message">
              <!-- ... -->
          </entity>
        </doctrine-mapping>

    .. code-block:: yaml

        Message:
          type: entity
          table: message
          # ...

Now the class ``Message`` will be saved and fetched from the table ``message``.

Property Mapping
----------------

The next step is mapping its properties to columns in the table.

To configure a property use the ``Column`` attribute. The ``type``
argument specifies the :ref:`Doctrine Mapping Type
<reference-mapping-types>` to use for the field. If the type is not
specified, ``string`` is used as the default.

.. configuration-block::

    .. code-block:: attribute

        <?php
        use Doctrine\ORM\Mapping\Column;
        use Doctrine\DBAL\Types\Types;

        #[Entity]
        class Message
        {
            #[Column(type: Types::INTEGER)]
            private $id;
            #[Column(length: 140)]
            private $text;
            #[Column(name: 'posted_at', type: Types::DATETIME)]
            private $postedAt;
        }

    .. code-block:: annotation

        <?php
        use Doctrine\ORM\Mapping\Entity;
        use Doctrine\ORM\Mapping\Column;

        /** @Entity */
        class Message
        {
            /** @Column(type="integer") */
            private $id;
            /** @Column(length=140) */
            private $text;
            /** @Column(type="datetime", name="posted_at") */
            private $postedAt;
        }

    .. code-block:: xml

        <doctrine-mapping>
          <entity name="Message">
            <field name="id" type="integer" />
            <field name="text" length="140" />
            <field name="postedAt" column="posted_at" type="datetime" />
          </entity>
        </doctrine-mapping>

    .. code-block:: yaml

        Message:
          type: entity
          fields:
            id:
              type: integer
            text:
              length: 140
            postedAt:
              type: datetime
              column: posted_at

When we don't explicitly specify a column name via the ``name`` option, Doctrine
assumes the field name is also the column name. So in this example:

* the ``id`` property will map to the column ``id`` using the type ``integer``;
* the ``text`` property will map to the column ``text`` with the default mapping type ``string``;
* the ``postedAt`` property will map to the ``posted_at`` column with the ``datetime`` type.

Here is a complete list of ``Column``s attributes (all optional):

- ``type`` (default: 'string'): The mapping type to use for the column.
- ``name`` (default: name of property): The name of the column in the database.
- ``length`` (default: 255): The length of the column in the database.
  Applies only if a string-valued column is used.
- ``unique`` (default: ``false``): Whether the column is a unique key.
- ``nullable`` (default: ``false``): Whether the column is nullable.
- ``insertable`` (default: ``true``): Whether the column should be inserted.
- ``updatable`` (default: ``true``): Whether the column should be updated.
- ``enumType`` (requires PHP 8.1 and ``doctrine/orm`` 2.11): The PHP enum class name to convert the database value into.
- ``precision`` (default: 0): The precision for a decimal (exact numeric) column
  (applies only for decimal column),
  which is the maximum number of digits that are stored for the values.
- ``scale`` (default: 0): The scale for a decimal (exact
  numeric) column (applies only for decimal column), which represents
  the number of digits to the right of the decimal point and must
  not be greater than ``precision``.
- ``columnDefinition``: Allows to define a custom
  DDL snippet that is used to create the column. Warning: This normally
  confuses the :doc:`SchemaTool <tools>` to always detect the column as changed.
- ``options``: Key-value pairs of options that get passed
  to the underlying database platform when generating DDL statements.

.. _reference-php-mapping-types:

PHP Types Mapping
_________________

.. versionadded:: 2.9

The column types can be inferred automatically from PHP's property types.
However, when the property type is nullable this has no effect on the ``nullable`` Column attribute.

These are the "automatic" mapping rules:

+-----------------------+-------------------------------+
| PHP property type     | Doctrine column type          |
+=======================+===============================+
| ``DateInterval``      | ``Types::DATEINTERVAL``       |
+-----------------------+-------------------------------+
| ``DateTime``          | ``Types::DATETIME_MUTABLE``   |
+-----------------------+-------------------------------+
| ``DateTimeImmutable`` | ``Types::DATETIME_IMMUTABLE`` |
+-----------------------+-------------------------------+
| ``array``             | ``Types::JSON``               |
+-----------------------+-------------------------------+
| ``bool``              | ``Types::BOOLEAN``            |
+-----------------------+-------------------------------+
| ``float``             | ``Types::FLOAT``              |
+-----------------------+-------------------------------+
| ``int``               | ``Types::INTEGER``            |
+-----------------------+-------------------------------+
| Any other type        | ``Types::STRING``             |
+-----------------------+-------------------------------+

As of version 2.11 Doctrine can also automatically map typed properties using a
PHP 8.1 enum to set the right ``type`` and ``enumType``.

.. versionadded:: 2.14

Since version 2.14 you can specify custom typed field mapping between PHP type and DBAL type using ``Configuration``
and a custom ``Doctrine\ORM\Mapping\TypedFieldMapper`` implementation.

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

.. _reference-mapping-types:

Doctrine Mapping Types
----------------------

The ``type`` option used in the ``@Column`` accepts any of the
`existing Doctrine DBAL types <https://docs.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/types.html#reference>`_
or :doc:`your own custom mapping types
<../cookbook/custom-mapping-types>`. A Doctrine type defines
the conversion between PHP and SQL types, independent from the database vendor
you are using.

.. note::

    DateTime and Object types are compared by reference, not by value. Doctrine
    updates this values if the reference changes and therefore behaves as if
    these objects are immutable value objects.

.. warning::

    All Date types assume that you are exclusively using the default timezone
    set by `date_default_timezone_set() <https://php.net/manual/en/function.date-default-timezone-set.php>`_
    or by the php.ini configuration ``date.timezone``. Working with
    different timezones will cause troubles and unexpected behavior.

    If you need specific timezone handling you have to handle this
    in your domain, converting all the values back and forth from UTC.
    There is also a :doc:`cookbook entry <../cookbook/working-with-datetime>`
    on working with datetimes that gives hints for implementing
    multi timezone applications.

Identifiers / Primary Keys
--------------------------

Every entity class must have an identifier/primary key. You can select
the field that serves as the identifier with the ``#[Id]`` attribute.

.. configuration-block::

    .. code-block:: attribute

        <?php
        class Message
        {
            #[Id]
            #[Column(type: 'integer')]
            #[GeneratedValue]
            private int|null $id = null;
            // ...
        }

    .. code-block:: annotation

        <?php
        class Message
        {
            /**
             * @Id
             * @Column(type="integer")
             * @GeneratedValue
             */
            private int|null $id = null;
            // ...
        }

    .. code-block:: xml

        <doctrine-mapping>
          <entity name="Message">
            <id name="id" type="integer">
                <generator strategy="AUTO" />
            </id>
            <!-- -->
          </entity>
        </doctrine-mapping>

    .. code-block:: yaml

        Message:
          type: entity
          id:
            id:
              type: integer
              generator:
                strategy: AUTO
          fields:
            # fields here

In most cases using the automatic generator strategy (``#[GeneratedValue]``) is
what you want, but for backwards-compatibility reasons it might not. It
defaults to the identifier generation mechanism your current database
vendor preferred at the time that strategy was introduced:
``AUTO_INCREMENT`` with MySQL, sequences with PostgreSQL and Oracle and
so on.

.. _identifier-generation-strategies:

Identifier Generation Strategies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The previous example showed how to use the default identifier
generation strategy without knowing the underlying database with
the AUTO-detection strategy. It is also possible to specify the
identifier generation strategy more explicitly, which allows you to
make use of some additional features.

Here is the list of possible generation strategies:

-  ``AUTO`` (default): Tells Doctrine to pick the strategy that is
   preferred by the used database platform. The preferred strategies
   are ``IDENTITY`` for MySQL, SQLite, MsSQL and SQL Anywhere and, for
   historical reasons, ``SEQUENCE`` for Oracle and PostgreSQL. This
   strategy provides full portability.
-  ``IDENTITY``: Tells Doctrine to use special identity columns in
   the database that generate a value on insertion of a row. This
   strategy does currently not provide full portability and is
   supported by the following platforms: MySQL/SQLite/SQL Anywhere
   (``AUTO_INCREMENT``), MSSQL (``IDENTITY``) and PostgreSQL (``SERIAL``).
-  ``SEQUENCE``: Tells Doctrine to use a database sequence for ID
   generation. This strategy does currently not provide full
   portability. Sequences are supported by Oracle, PostgreSql and
   SQL Anywhere.
-  ``UUID`` (deprecated): Tells Doctrine to use the built-in Universally
   Unique Identifier generator. This strategy provides full portability.
-  ``NONE``: Tells Doctrine that the identifiers are assigned (and
   thus generated) by your code. The assignment must take place before
   a new entity is passed to ``EntityManager#persist``. NONE is the
   same as leaving off the ``#[GeneratedValue]`` entirely.
-  ``CUSTOM``: With this option, you can use the ``#[CustomIdGenerator]`` attribute.
   It will allow you to pass a :ref:`class of your own to generate the identifiers. <annref_customidgenerator>`

Sequence Generator
^^^^^^^^^^^^^^^^^^

The Sequence Generator can currently be used in conjunction with
Oracle or Postgres and allows some additional configuration options
besides specifying the sequence's name:

.. configuration-block::

    .. code-block:: attribute

        <?php
        class Message
        {
            #[Id]
            #[GeneratedValue(strategy: 'SEQUENCE')]
            #[SequenceGenerator(sequenceName: 'message_seq', initialValue: 1, allocationSize: 100)]
            protected int|null $id = null;
            // ...
        }

    .. code-block:: annotation

        <?php
        class Message
        {
            /**
             * @Id
             * @GeneratedValue(strategy="SEQUENCE")
             * @SequenceGenerator(sequenceName="message_seq", initialValue=1, allocationSize=100)
             */
            protected int|null $id = null;
            // ...
        }

    .. code-block:: xml

        <doctrine-mapping>
          <entity name="Message">
            <id name="id" type="integer">
                <generator strategy="SEQUENCE" />
                <sequence-generator sequence-name="message_seq" allocation-size="100" initial-value="1" />
            </id>
          </entity>
        </doctrine-mapping>

    .. code-block:: yaml

        Message:
          type: entity
          id:
            id:
              type: integer
              generator:
                strategy: SEQUENCE
              sequenceGenerator:
                sequenceName: message_seq
                allocationSize: 100
                initialValue: 1

The initial value specifies at which value the sequence should
start.

The allocationSize is a powerful feature to optimize INSERT
performance of Doctrine. The allocationSize specifies by how much
values the sequence is incremented whenever the next value is
retrieved. If this is larger than 1 (one) Doctrine can generate
identifier values for the allocationSizes amount of entities. In
the above example with ``allocationSize=100`` Doctrine ORM would only
need to access the sequence once to generate the identifiers for
100 new entities.

.. caution::

    The allocationSize is detected by SchemaTool and
    transformed into an "INCREMENT BY " clause in the CREATE SEQUENCE
    statement. For a database schema created manually (and not
    SchemaTool) you have to make sure that the allocationSize
    configuration option is never larger than the actual sequences
    INCREMENT BY value, otherwise you may get duplicate keys.


.. note::

    It is possible to use strategy="AUTO" and at the same time
    specifying a @SequenceGenerator. In such a case, your custom
    sequence settings are used in the case where the preferred strategy
    of the underlying platform is SEQUENCE, such as for Oracle and
    PostgreSQL.


Composite Keys
~~~~~~~~~~~~~~

With Doctrine ORM you can use composite primary keys, using ``#[Id]`` on
more than one column. Some restrictions exist opposed to using a single
identifier in this case: The use of the ``#[GeneratedValue]`` attribute
is not supported, which means you can only use composite keys if you
generate the primary key values yourself before calling
``EntityManager#persist()`` on the entity.

More details on composite primary keys are discussed in a :doc:`dedicated tutorial
<../tutorials/composite-primary-keys>`.

Quoting Reserved Words
----------------------

Sometimes it is necessary to quote a column or table name because of reserved
word conflicts. Doctrine does not quote identifiers automatically, because it
leads to more problems than it would solve. Quoting tables and column names
needs to be done explicitly using ticks in the definition.

.. code-block:: php

    <?php

    #[Column(name: '`number`', type: 'integer')]
    private $number;

Doctrine will then quote this column name in all SQL statements
according to the used database platform.

.. warning::

    Identifier Quoting does not work for join column names or discriminator
    column names unless you are using a custom ``QuoteStrategy``.

.. _reference-basic-mapping-custom-mapping-types:

For more control over column quoting the ``Doctrine\ORM\Mapping\QuoteStrategy`` interface
was introduced in ORM. It is invoked for every column, table, alias and other
SQL names. You can implement the QuoteStrategy and set it by calling
``Doctrine\ORM\Configuration#setQuoteStrategy()``.

The ANSI Quote Strategy was added, which assumes quoting is not necessary for any SQL name.
You can use it with the following code:

.. code-block:: php

    <?php
    use Doctrine\ORM\Mapping\AnsiQuoteStrategy;

    $configuration->setQuoteStrategy(new AnsiQuoteStrategy());
