5. 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 Association Mapping.

5.1. Guide Assumptions

You should have already installed and configure Doctrine.

5.2. 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:

<?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:

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

Note

All metadata drivers perform equally. Once the metadata of a class has been read from the source (annotations, xml or yaml) it is stored in an instance of the Doctrine\ORM\Mapping\ClassMetadata class and these instances 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:

  • PHP
    <?php
    /** @Entity */
    class Message
    {
        //...
    }
    
  • XML
    <doctrine-mapping>
      <entity name="Message">
          <!-- ... -->
      </entity>
    </doctrine-mapping>
    
  • 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:

  • PHP
    <?php
    /**
     * @Entity
     * @Table(name="message")
     */
    class Message
    {
        //...
    }
    
  • XML
    <doctrine-mapping>
      <entity name="Message" table="message">
          <!-- ... -->
      </entity>
    </doctrine-mapping>
    
  • YAML
    Message:
      type: entity
      table: message
      # ...
    

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

5.3. Property Mapping

The next step after marking a PHP class as an entity is mapping its properties to columns in a table.

To configure a property use the @Column docblock annotation. The type attribute specifies the Doctrine Mapping Type to use for the field. If the type is not specified, string is used as the default.

  • PHP
    <?php
    /** @Entity */
    class Message
    {
        /** @Column(type="integer") */
        private $id;
        /** @Column(length=140) */
        private $text;
        /** @Column(type="datetime", name="posted_at") */
        private $postedAt;
    }
    
  • 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>
    
  • 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. This means that:

  • 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.

The Column annotation has some more attributes. Here is a complete list:

  • type: (optional, defaults to ‘string’) The mapping type to use for the column.
  • name: (optional, defaults to field name) The name of the column in the database.
  • length: (optional, default 255) The length of the column in the database. (Applies only if a string-valued column is used).
  • unique: (optional, default FALSE) Whether the column is a unique key.
  • nullable: (optional, default FALSE) Whether the database column is nullable.
  • precision: (optional, 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: (optional, 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: (optional) Allows to define a custom DDL snippet that is used to create the column. Warning: This normally confuses the SchemaTool to always detect the column as changed.
  • options: (optional) Key-value pairs of options that get passed to the underlying database platform when generating DDL statements.

5.4. Doctrine Mapping Types

The type option used in the @Column accepts any of the existing Doctrine types or even your own custom types. A Doctrine type defines the conversion between PHP and SQL types, independent from the database vendor you are using. All Mapping Types that ship with Doctrine are fully portable between the supported database systems.

As an example, the Doctrine Mapping Type string defines the mapping from a PHP string to a SQL VARCHAR (or VARCHAR2 etc. depending on the RDBMS brand). Here is a quick overview of the built-in mapping types:

  • string: Type that maps a SQL VARCHAR to a PHP string.
  • integer: Type that maps a SQL INT to a PHP integer.
  • smallint: Type that maps a database SMALLINT to a PHP integer.
  • bigint: Type that maps a database BIGINT to a PHP string.
  • boolean: Type that maps a SQL boolean or equivalent (TINYINT) to a PHP boolean.
  • decimal: Type that maps a SQL DECIMAL to a PHP string.
  • date: Type that maps a SQL DATETIME to a PHP DateTime object.
  • time: Type that maps a SQL TIME to a PHP DateTime object.
  • datetime: Type that maps a SQL DATETIME/TIMESTAMP to a PHP DateTime object.
  • datetimetz: Type that maps a SQL DATETIME/TIMESTAMP to a PHP DateTime object with timezone.
  • text: Type that maps a SQL CLOB to a PHP string.
  • object: Type that maps a SQL CLOB to a PHP object using serialize() and unserialize()
  • array: Type that maps a SQL CLOB to a PHP array using serialize() and unserialize()
  • simple_array: Type that maps a SQL CLOB to a PHP array using implode() and explode(), with a comma as delimiter. IMPORTANT Only use this type if you are sure that your values cannot contain a ”,”.
  • json_array: Type that maps a SQL CLOB to a PHP array using json_encode() and json_decode()
  • float: Type that maps a SQL Float (Double Precision) to a PHP double. IMPORTANT: Works only with locale settings that use decimal points as separator.
  • guid: Type that maps a database GUID/UUID to a PHP string. Defaults to varchar but uses a specific type if the platform supports it.
  • blob: Type that maps a SQL BLOB to a PHP resource stream

A cookbook article shows how to define your own custom mapping types.

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() 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 cookbook entry on working with datetimes that gives hints for implementing multi timezone applications.

5.5. 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 annotation.

  • PHP
    <?php
    class Message
    {
        /**
         * @Id @Column(type="integer")
         * @GeneratedValue
         */
        private $id;
        //...
    }
    
  • XML
    <doctrine-mapping>
      <entity name="Message">
        <id name="id" type="integer">
            <generator strategy="AUTO" />
        </id>
        <!-- -->
      </entity>
    </doctrine-mapping>
    
  • 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. It defaults to the identifier generation mechanism your current database vendor prefers: AUTO_INCREMENT with MySQL, SERIAL with PostgreSQL, Sequences with Oracle and so on.

5.5.1. 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 SEQUENCE for Oracle and PostgreSQL. This strategy provides full portability.
  • 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.
  • 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).
  • TABLE: Tells Doctrine to use a separate table for ID generation. This strategy provides full portability. *This strategy is not yet implemented!*
  • 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.

5.5.1.1. 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:

  • PHP
    <?php
    class Message
    {
        /**
         * @Id
         * @GeneratedValue(strategy="SEQUENCE")
         * @SequenceGenerator(sequenceName="message_seq", initialValue=1, allocationSize=100)
         */
        protected $id = null;
        //...
    }
    
  • 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>
    
  • 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 2 would only need to access the sequence once to generate the identifiers for 100 new entities.

The default allocationSize for a @SequenceGenerator is currently 10.

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.

5.5.2. Composite Keys

with Doctrine 2 you can use composite primary keys, using @Id on more then one column. Some restrictions exist opposed to using a single identifier in this case: The use of the @GeneratedValue annotation 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 dedicated tutorial.

5.6. 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 then it would solve. Quoting tables and column names needs to be done explicitly using ticks in the definition.

<?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.

For more control over column quoting the Doctrine\ORM\Mapping\QuoteStrategy interface was introduced in 2.3. 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:

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

$configuration->setQuoteStrategy(new AnsiQuoteStrategy());
Fork me on GitHub