Merge branch 'Docs'
This commit is contained in:
commit
340ae15ba7
@ -1,12 +1,11 @@
|
||||
Association Mapping
|
||||
===================
|
||||
|
||||
This chapter introduces association mappings which are used to explain
|
||||
references between objects and are mapped to a relational database using
|
||||
foreign keys.
|
||||
This chapter explains mapping associations between objects.
|
||||
|
||||
Instead of working with the foreign keys directly you will always work with
|
||||
references to objects:
|
||||
Instead of working with foreign keys in your code, you will always work with
|
||||
references to objects instead and Doctrine will convert those references
|
||||
to foreign keys internally.
|
||||
|
||||
- A reference to a single object is represented by a foreign key.
|
||||
- A collection of objects is represented by many foreign keys pointing to the object holding the collection
|
||||
@ -17,15 +16,89 @@ This chapter is split into three different sections.
|
||||
- :ref:`association_mapping_defaults` are explained that simplify the use-case examples.
|
||||
- :ref:`collections` are introduced that contain entities in associations.
|
||||
|
||||
To master associations you should also learn about :doc:`owning and inverse sides of associations <unitofwork-associations>`
|
||||
To gain a full understanding of associations you should also read about :doc:`owning and
|
||||
inverse sides of associations <unitofwork-associations>`
|
||||
|
||||
Many-To-One, Unidirectional
|
||||
---------------------------
|
||||
|
||||
A many-to-one association is the most common association between objects.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity **/
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Address")
|
||||
* @JoinColumn(name="address_id", referencedColumnName="id")
|
||||
**/
|
||||
private $address;
|
||||
}
|
||||
|
||||
/** @Entity **/
|
||||
class Address
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="User">
|
||||
<many-to-one field="address" target-entity="Address">
|
||||
<join-column name="address_id" referenced-column-name="id" />
|
||||
</many-to-one>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
manyToOne:
|
||||
address:
|
||||
targetEntity: Address
|
||||
joinColumn:
|
||||
name: address_id
|
||||
referencedColumnName: id
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
The above ``@JoinColumn`` is optional as it would default
|
||||
to ``address_id`` and ``id`` anyways. You can omit it and let it
|
||||
use the defaults.
|
||||
|
||||
Generated MySQL Schema:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
CREATE TABLE User (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
address_id INT DEFAULT NULL,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
CREATE TABLE Address (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
ALTER TABLE User ADD FOREIGN KEY (address_id) REFERENCES Address(id);
|
||||
|
||||
One-To-One, Unidirectional
|
||||
--------------------------
|
||||
|
||||
A unidirectional one-to-one association is very common. Here is an
|
||||
example of a ``Product`` that has one ``Shipping`` object
|
||||
associated to it. The ``Shipping`` side does not reference back to
|
||||
the ``Product`` so it is unidirectional.
|
||||
Here is an example of a one-to-one association with a ``Product`` entity that
|
||||
references one ``Shipping`` entity. The ``Shipping`` does not reference back to
|
||||
the ``Product`` so that the reference is said to be unidirectional, in one
|
||||
direction only.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@ -184,7 +257,7 @@ relation, the table ``Cart``.
|
||||
One-To-One, Self-referencing
|
||||
----------------------------
|
||||
|
||||
You can easily have self referencing one-to-one relationships like
|
||||
You can define a self-referencing one-to-one relationships like
|
||||
below.
|
||||
|
||||
.. code-block:: php
|
||||
@ -218,6 +291,102 @@ With the generated MySQL Schema:
|
||||
) ENGINE = InnoDB;
|
||||
ALTER TABLE Student ADD FOREIGN KEY (mentor_id) REFERENCES Student(id);
|
||||
|
||||
One-To-Many, Bidirectional
|
||||
--------------------------
|
||||
|
||||
A one-to-many association has to be bidirectional, unless you are using an
|
||||
additional join-table. This is necessary, because of the foreign key
|
||||
in a one-to-many association being defined on the "many" side. Doctrine
|
||||
needs a many-to-one association that defines the mapping of this
|
||||
foreign key.
|
||||
|
||||
This bidirectional mapping requires the ``mappedBy`` attribute on the
|
||||
``OneToMany`` association and the ``inversedBy`` attribute on the ``ManyToOne``
|
||||
association.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/** @Entity **/
|
||||
class Product
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* @OneToMany(targetEntity="Feature", mappedBy="product")
|
||||
**/
|
||||
private $features;
|
||||
// ...
|
||||
|
||||
public function __construct() {
|
||||
$this->features = new ArrayCollection();
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity **/
|
||||
class Feature
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Product", inversedBy="features")
|
||||
* @JoinColumn(name="product_id", referencedColumnName="id")
|
||||
**/
|
||||
private $product;
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="Product">
|
||||
<one-to-many field="features" target-entity="Feature" mapped-by="product" />
|
||||
</entity>
|
||||
<entity name="Feature">
|
||||
<many-to-one field="product" target-entity="Product" inversed-by="features">
|
||||
<join-column name="product_id" referenced-column-name="id" />
|
||||
</many-to-one>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Product:
|
||||
type: entity
|
||||
oneToMany:
|
||||
features:
|
||||
targetEntity: Feature
|
||||
mappedBy: product
|
||||
Feature:
|
||||
type: entity
|
||||
manyToOne:
|
||||
product:
|
||||
targetEntity: Product
|
||||
inversedBy: features
|
||||
joinColumn:
|
||||
name: product_id
|
||||
referencedColumnName: id
|
||||
|
||||
Note that the @JoinColumn is not really necessary in this example,
|
||||
as the defaults would be the same.
|
||||
|
||||
Generated MySQL Schema:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
CREATE TABLE Product (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
CREATE TABLE Feature (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
product_id INT DEFAULT NULL,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
ALTER TABLE Feature ADD FOREIGN KEY (product_id) REFERENCES Product(id);
|
||||
|
||||
One-To-Many, Unidirectional with Join Table
|
||||
-------------------------------------------
|
||||
|
||||
@ -226,12 +395,6 @@ join table. From Doctrine's point of view, it is simply mapped as a
|
||||
unidirectional many-to-many whereby a unique constraint on one of
|
||||
the join columns enforces the one-to-many cardinality.
|
||||
|
||||
.. note::
|
||||
|
||||
One-To-Many uni-directional relations with join-table only
|
||||
work using the @ManyToMany annotation and a unique-constraint.
|
||||
|
||||
|
||||
The following example sets up such a unidirectional one-to-many association:
|
||||
|
||||
.. configuration-block::
|
||||
@ -326,171 +489,6 @@ Generates the following MySQL Schema:
|
||||
ALTER TABLE users_phonenumbers ADD FOREIGN KEY (user_id) REFERENCES User(id);
|
||||
ALTER TABLE users_phonenumbers ADD FOREIGN KEY (phonenumber_id) REFERENCES Phonenumber(id);
|
||||
|
||||
|
||||
Many-To-One, Unidirectional
|
||||
---------------------------
|
||||
|
||||
You can easily implement a many-to-one unidirectional association
|
||||
with the following:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity **/
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Address")
|
||||
* @JoinColumn(name="address_id", referencedColumnName="id")
|
||||
**/
|
||||
private $address;
|
||||
}
|
||||
|
||||
/** @Entity **/
|
||||
class Address
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="User">
|
||||
<many-to-one field="address" target-entity="Address">
|
||||
<join-column name="address_id" referenced-column-name="id" />
|
||||
</many-to-one>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
manyToOne:
|
||||
address:
|
||||
targetEntity: Address
|
||||
joinColumn:
|
||||
name: address_id
|
||||
referencedColumnName: id
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
The above ``@JoinColumn`` is optional as it would default
|
||||
to ``address_id`` and ``id`` anyways. You can omit it and let it
|
||||
use the defaults.
|
||||
|
||||
|
||||
Generated MySQL Schema:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
CREATE TABLE User (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
address_id INT DEFAULT NULL,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
CREATE TABLE Address (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
ALTER TABLE User ADD FOREIGN KEY (address_id) REFERENCES Address(id);
|
||||
|
||||
One-To-Many, Bidirectional
|
||||
--------------------------
|
||||
|
||||
Bidirectional one-to-many associations are very common. The
|
||||
following code shows an example with a Product and a Feature
|
||||
class:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity **/
|
||||
class Product
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* @OneToMany(targetEntity="Feature", mappedBy="product")
|
||||
**/
|
||||
private $features;
|
||||
// ...
|
||||
|
||||
public function __construct() {
|
||||
$this->features = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity **/
|
||||
class Feature
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Product", inversedBy="features")
|
||||
* @JoinColumn(name="product_id", referencedColumnName="id")
|
||||
**/
|
||||
private $product;
|
||||
// ...
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="Product">
|
||||
<one-to-many field="features" target-entity="Feature" mapped-by="product" />
|
||||
</entity>
|
||||
<entity name="Feature">
|
||||
<many-to-one field="product" target-entity="Product" inversed-by="features">
|
||||
<join-column name="product_id" referenced-column-name="id" />
|
||||
</many-to-one>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Product:
|
||||
type: entity
|
||||
oneToMany:
|
||||
features:
|
||||
targetEntity: Feature
|
||||
mappedBy: product
|
||||
Feature:
|
||||
type: entity
|
||||
manyToOne:
|
||||
product:
|
||||
targetEntity: Product
|
||||
inversedBy: features
|
||||
joinColumn:
|
||||
name: product_id
|
||||
referencedColumnName: id
|
||||
|
||||
|
||||
Note that the @JoinColumn is not really necessary in this example,
|
||||
as the defaults would be the same.
|
||||
|
||||
Generated MySQL Schema:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
CREATE TABLE Product (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
CREATE TABLE Feature (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
product_id INT DEFAULT NULL,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
ALTER TABLE Feature ADD FOREIGN KEY (product_id) REFERENCES Product(id);
|
||||
|
||||
One-To-Many, Self-referencing
|
||||
-----------------------------
|
||||
|
||||
@ -756,8 +754,8 @@ one is bidirectional.
|
||||
The MySQL schema is exactly the same as for the Many-To-Many
|
||||
uni-directional case above.
|
||||
|
||||
Picking Owning and Inverse Side
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Owning and Inverse Side on a ManyToMany association
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For Many-To-Many associations you can chose which entity is the
|
||||
owning and which the inverse side. There is a very simple semantic
|
||||
@ -869,11 +867,9 @@ Generated MySQL Schema:
|
||||
Mapping Defaults
|
||||
----------------
|
||||
|
||||
Before we introduce all the association mappings in detail, you
|
||||
should note that the @JoinColumn and @JoinTable definitions are
|
||||
usually optional and have sensible default values. The defaults for
|
||||
a join column in a one-to-one/many-to-one association is as
|
||||
follows:
|
||||
The ``@JoinColumn`` and ``@JoinTable`` definitions are usually optional and have
|
||||
sensible default values. The defaults for a join column in a
|
||||
one-to-one/many-to-one association is as follows:
|
||||
|
||||
::
|
||||
|
||||
@ -973,8 +969,7 @@ similar defaults. As an example, consider this mapping:
|
||||
groups:
|
||||
targetEntity: Group
|
||||
|
||||
This is essentially the same as the following, more verbose,
|
||||
mapping:
|
||||
This is essentially the same as the following, more verbose, mapping:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@ -1043,73 +1038,28 @@ minimum.
|
||||
Collections
|
||||
-----------
|
||||
|
||||
In all the examples of many-valued associations in this manual we
|
||||
will make use of a ``Collection`` interface and a corresponding
|
||||
default implementation ``ArrayCollection`` that are defined in the
|
||||
``Doctrine\Common\Collections`` namespace. Why do we need that?
|
||||
Doesn't that couple my domain model to Doctrine? Unfortunately, PHP
|
||||
arrays, while being great for many things, do not make up for good
|
||||
collections of business objects, especially not in the context of
|
||||
an ORM. The reason is that plain PHP arrays can not be
|
||||
transparently extended / instrumented in PHP code, which is
|
||||
necessary for a lot of advanced ORM features. The classes /
|
||||
interfaces that come closest to an OO collection are ArrayAccess
|
||||
and ArrayObject but until instances of these types can be used in
|
||||
all places where a plain array can be used (something that may
|
||||
happen in PHP6) their usability is fairly limited. You "can"
|
||||
type-hint on ``ArrayAccess`` instead of ``Collection``, since the
|
||||
Collection interface extends ``ArrayAccess``, but this will
|
||||
severely limit you in the way you can work with the collection,
|
||||
because the ``ArrayAccess`` API is (intentionally) very primitive
|
||||
and more importantly because you can not pass this collection to
|
||||
all the useful PHP array functions, which makes it very hard to
|
||||
work with.
|
||||
Unfortunately, PHP arrays, while being great for many things, are missing
|
||||
features that make them suitable for lazy loading in the context of an ORM.
|
||||
This is why in all the examples of many-valued associations in this manual we
|
||||
will make use of a ``Collection`` interface and its
|
||||
default implementation ``ArrayCollection`` that are both defined in the
|
||||
``Doctrine\Common\Collections`` namespace. A collection implements
|
||||
the PHP interfaces ``ArrayAccess``, ``Traversable`` and ``Countable``.
|
||||
|
||||
.. warning::
|
||||
.. note::
|
||||
|
||||
The Collection interface and ArrayCollection class,
|
||||
like everything else in the Doctrine namespace, are neither part of
|
||||
the ORM, nor the DBAL, it is a plain PHP class that has no outside
|
||||
dependencies apart from dependencies on PHP itself (and the SPL).
|
||||
Therefore using this class in your domain classes and elsewhere
|
||||
does not introduce a coupling to the persistence layer. The
|
||||
Collection class, like everything else in the Common namespace, is
|
||||
not part of the persistence layer. You could even copy that class
|
||||
over to your project if you want to remove Doctrine from your
|
||||
project and all your domain classes will work the same as before.
|
||||
|
||||
|
||||
Therefore using this class in your model and elsewhere
|
||||
does not introduce a coupling to the ORM.
|
||||
|
||||
Initializing Collections
|
||||
------------------------
|
||||
|
||||
You have to be careful when using entity fields that contain a
|
||||
collection of related entities. Say we have a User entity that
|
||||
contains a collection of groups:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity **/
|
||||
class User
|
||||
{
|
||||
/** @ManyToMany(targetEntity="Group") **/
|
||||
private $groups;
|
||||
|
||||
public function getGroups()
|
||||
{
|
||||
return $this->groups;
|
||||
}
|
||||
}
|
||||
|
||||
With this code alone the ``$groups`` field only contains an
|
||||
instance of ``Doctrine\Common\Collections\Collection`` if the user
|
||||
is retrieved from Doctrine, however not after you instantiated a
|
||||
fresh instance of the User. When your user entity is still new
|
||||
``$groups`` will obviously be null.
|
||||
|
||||
This is why we recommend to initialize all collection fields to an
|
||||
empty ``ArrayCollection`` in your entities constructor:
|
||||
You should always initialize the collections of your ``@OneToMany``
|
||||
and ``@ManyToMany`` associations in the constructor of your entities:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -1133,13 +1083,12 @@ empty ``ArrayCollection`` in your entities constructor:
|
||||
}
|
||||
}
|
||||
|
||||
Now the following code will work even if the Entity hasn't
|
||||
The following code will then work even if the Entity hasn't
|
||||
been associated with an EntityManager yet:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$group = $entityManager->find('Group', $groupId);
|
||||
$group = new Group();
|
||||
$user = new User();
|
||||
$user->getGroups()->add($group);
|
||||
|
||||
|
@ -2,8 +2,8 @@ Basic Mapping
|
||||
=============
|
||||
|
||||
This chapter explains the basic mapping of objects and properties.
|
||||
Mapping of associations will be covered in the next chapter
|
||||
"Association Mapping".
|
||||
Mapping of associations will be covered in the next chapter on
|
||||
:doc:`Association Mapping <association-mapping>`.
|
||||
|
||||
Mapping Drivers
|
||||
---------------
|
||||
@ -11,30 +11,27 @@ Mapping Drivers
|
||||
Doctrine provides several different ways for specifying
|
||||
object-relational mapping metadata:
|
||||
|
||||
|
||||
- Docblock Annotations
|
||||
- XML
|
||||
- YAML
|
||||
- PHP code
|
||||
|
||||
This manual usually mentions docblock annotations in all the examples
|
||||
that are spread throughout all chapters, however for many examples
|
||||
alternative YAML and XML examples are given as well. There are dedicated
|
||||
reference chapters for XML and YAML mapping, respectively that explain them
|
||||
in more detail. There is also an Annotation reference chapter.
|
||||
This manual usually mentions docblock annotations in all the examples that are
|
||||
spread throughout all chapters, however for many examples alternative YAML and
|
||||
XML examples are given as well. There are dedicated reference chapters for
|
||||
:doc:`XML <xml-mapping>` and :doc:`YAML <yml-mapping>` mapping, respectively
|
||||
that explain them in more detail. There is also a reference chapter for
|
||||
:doc:`Annotations <annotations-reference>`.
|
||||
|
||||
.. note::
|
||||
|
||||
If you're wondering which mapping driver gives the best
|
||||
performance, the answer is: They all give exactly the same performance.
|
||||
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. Therefore at
|
||||
the end of the day all drivers perform equally well. If you're not
|
||||
using a metadata cache (not recommended!) then the XML driver might
|
||||
have a slight edge in performance due to the powerful native XML
|
||||
support in PHP.
|
||||
|
||||
All metadata drivers give exactly the same performance. 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. Therefore at the end
|
||||
of the day all drivers perform equally well. If you're not using a metadata
|
||||
cache (not recommended!) then the XML driver is the fastest by using PHP's
|
||||
native XML support.
|
||||
|
||||
Introduction to Docblock Annotations
|
||||
------------------------------------
|
||||
@ -69,9 +66,8 @@ annotations for supplying object-relational mapping metadata.
|
||||
Persistent classes
|
||||
------------------
|
||||
|
||||
In order to mark a class for object-relational persistence it needs
|
||||
to be designated as an entity. This can be done through the
|
||||
``@Entity`` marker annotation.
|
||||
Every PHP Classthat you want to save in the database using Doctrine
|
||||
need to be configured as "Entity".
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@ -98,9 +94,9 @@ to be designated as an entity. This can be done through the
|
||||
type: entity
|
||||
# ...
|
||||
|
||||
By default, the entity will be persisted to a table with the same
|
||||
name as the class name. In order to change that, you can use the
|
||||
``@Table`` annotation as follows:
|
||||
With no additional information given Doctrine expects the entity to be saved
|
||||
into a table with the same name as the class. You can change this assumption
|
||||
by adding more information about the used table:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@ -131,24 +127,22 @@ name as the class name. In order to change that, you can use the
|
||||
table: my_persistent_class
|
||||
# ...
|
||||
|
||||
Now instances of MyPersistentClass will be persisted into a table
|
||||
named ``my_persistent_class``.
|
||||
In this example the class ``MyPersistentClass`` will be saved and fetched from
|
||||
the table ``my_persistent_class``.
|
||||
|
||||
Doctrine Mapping Types
|
||||
----------------------
|
||||
|
||||
A Doctrine Mapping Type defines the mapping between a PHP type and
|
||||
a SQL type. All Doctrine Mapping Types that ship with Doctrine are
|
||||
fully portable between different RDBMS. You can even write your own
|
||||
custom mapping types that might or might not be portable, which is
|
||||
explained later in this chapter.
|
||||
A Doctrine Mapping Type defines the conversion the type of a PHP variable and
|
||||
an SQL type. All Mapping Types that ship with Doctrine are fully portable
|
||||
between the supported database systems. You can add your own custom mapping
|
||||
types to add more conversions.
|
||||
|
||||
For example, the Doctrine Mapping Type ``string`` defines the
|
||||
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
|
||||
@ -180,14 +174,6 @@ built-in mapping types:
|
||||
varchar but uses a specific type if the platform supports it.
|
||||
- ``blob``: Type that maps a SQL BLOB to a PHP resource stream
|
||||
|
||||
.. note::
|
||||
|
||||
Doctrine Mapping Types are NOT SQL types and NOT PHP
|
||||
types! They are mapping types between 2 types.
|
||||
Additionally Mapping types are *case-sensitive*. For example, using
|
||||
a DateTime column will NOT match the datetime type that ships with
|
||||
Doctrine 2.
|
||||
|
||||
.. note::
|
||||
|
||||
DateTime and Object types are compared by reference, not by value. Doctrine updates this values
|
||||
@ -206,21 +192,17 @@ built-in mapping types:
|
||||
on working with datetimes that gives hints for implementing
|
||||
multi timezone applications.
|
||||
|
||||
|
||||
Property Mapping
|
||||
----------------
|
||||
|
||||
After a class has been marked as an entity it can specify mappings
|
||||
for its instance fields. Here we will only look at simple fields
|
||||
that hold scalar values like strings, numbers, etc. Associations to
|
||||
other objects are covered in the chapter "Association Mapping".
|
||||
Properties of an entity class can be mapped to columns of the
|
||||
SQL table of that entity.
|
||||
|
||||
To configure a property use the ``@Column`` docblock annotation. This
|
||||
annotation usually requires at least 1 attribute to be set, the ``type``. 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 mapping type.
|
||||
|
||||
To mark a property for relational persistence the ``@Column``
|
||||
docblock annotation is used. This annotation usually requires at
|
||||
least 1 attribute to be set, the ``type``. 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 mapping type
|
||||
since it is the most flexible.
|
||||
|
||||
Example:
|
||||
|
||||
@ -258,13 +240,11 @@ Example:
|
||||
name:
|
||||
length: 50
|
||||
|
||||
In that example we mapped the field ``id`` to the column ``id``
|
||||
using the mapping type ``integer`` and the field ``name`` is mapped
|
||||
to the column ``name`` with the default mapping type ``string``. As
|
||||
you can see, by default the column names are assumed to be the same
|
||||
as the field names. To specify a different name for the column, you
|
||||
can use the ``name`` attribute of the Column annotation as
|
||||
follows:
|
||||
In that example we configured the property ``id`` to map to the column ``id``
|
||||
using the mapping type ``integer``. The field ``name`` is mapped to the column
|
||||
``name`` with the default mapping type ``string``. Column names are assumed to
|
||||
be the same as the field names unless you pecify a different name for the
|
||||
column using the ``name`` attribute of the Column annotation:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@ -294,7 +274,6 @@ follows:
|
||||
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
|
||||
@ -310,129 +289,12 @@ list:
|
||||
- ``scale``: (optional, default 0) The scale for a decimal (exact
|
||||
numeric) column. (Applies only if a decimal column is used.)
|
||||
|
||||
.. _reference-basic-mapping-custom-mapping-types:
|
||||
|
||||
Custom Mapping Types
|
||||
--------------------
|
||||
|
||||
Doctrine allows you to create new mapping types. This can come in
|
||||
handy when you're missing a specific mapping type or when you want
|
||||
to replace the existing implementation of a mapping type.
|
||||
|
||||
In order to create a new mapping type you need to subclass
|
||||
``Doctrine\DBAL\Types\Type`` and implement/override the methods as
|
||||
you wish. Here is an example skeleton of such a custom type class:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace My\Project\Types;
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
|
||||
/**
|
||||
* My custom datatype.
|
||||
*/
|
||||
class MyType extends Type
|
||||
{
|
||||
const MYTYPE = 'mytype'; // modify to match your type name
|
||||
|
||||
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
{
|
||||
// return the SQL used to create your column type. To create a portable column type, use the $platform.
|
||||
}
|
||||
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
// This is executed when the value is read from the database. Make your conversions here, optionally using the $platform.
|
||||
}
|
||||
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
// This is executed when the value is written to the database. Make your conversions here, optionally using the $platform.
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return self::MYTYPE; // modify to match your constant name
|
||||
}
|
||||
}
|
||||
|
||||
Restrictions to keep in mind:
|
||||
|
||||
|
||||
- If the value of the field is *NULL* the method
|
||||
``convertToDatabaseValue()`` is not called.
|
||||
- The ``UnitOfWork`` never passes values to the database convert
|
||||
method that did not change in the request.
|
||||
|
||||
When you have implemented the type you still need to let Doctrine
|
||||
know about it. This can be achieved through the
|
||||
``Doctrine\DBAL\Types\Type#addType($name, $className)``
|
||||
method. See the following example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// in bootstrapping code
|
||||
|
||||
// ...
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
|
||||
// ...
|
||||
|
||||
// Register my type
|
||||
Type::addType('mytype', 'My\Project\Types\MyType');
|
||||
|
||||
As can be seen above, when registering the custom types in the
|
||||
configuration you specify a unique name for the mapping type and
|
||||
map that to the corresponding fully qualified class name. Now you
|
||||
can use your new type in your mapping like this:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class MyPersistentClass
|
||||
{
|
||||
/** @Column(type="mytype") */
|
||||
private $field;
|
||||
}
|
||||
|
||||
To have Schema-Tool convert the underlying database type of your
|
||||
new "mytype" directly into an instance of ``MyType`` you have to
|
||||
additionally register this mapping with your database platform:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$conn = $em->getConnection();
|
||||
$conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype');
|
||||
|
||||
Now using Schema-Tool, whenever it detects a column having the
|
||||
``db_mytype`` it will convert it into a ``mytype`` Doctrine Type
|
||||
instance for Schema representation. Keep in mind that you can
|
||||
easily produce clashes this way, each database type can only map to
|
||||
exactly one Doctrine mapping type.
|
||||
|
||||
Custom ColumnDefinition
|
||||
-----------------------
|
||||
|
||||
You can define a custom definition for each column using the "columnDefinition"
|
||||
attribute of ``@Column``. You have to define all the definitions that follow
|
||||
the name of a column here.
|
||||
|
||||
.. note::
|
||||
|
||||
Using columnDefinition will break change-detection in SchemaTool.
|
||||
|
||||
Identifiers / Primary Keys
|
||||
--------------------------
|
||||
|
||||
Every entity class needs an identifier/primary key. You designate
|
||||
the field that serves as the identifier with the ``@Id`` marker
|
||||
annotation. Here is an example:
|
||||
the field that serves as the identifier with the ``@Id``
|
||||
annotation:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@ -466,14 +328,12 @@ annotation. Here is an example:
|
||||
name:
|
||||
length: 50
|
||||
|
||||
Without doing anything else, the identifier is assumed to be
|
||||
manually assigned. That means your code would need to properly set
|
||||
the identifier property before passing a new entity to
|
||||
This definition is missing an ID generation strategy, which means that your code needs to assign
|
||||
the identifier manually before passing a new entity to
|
||||
``EntityManager#persist($entity)``.
|
||||
|
||||
A common alternative strategy is to use a generated value as the
|
||||
identifier. To do this, you use the ``@GeneratedValue`` annotation
|
||||
like this:
|
||||
Doctrine can alternatively generate identifiers for entities using generation strategies,
|
||||
using database sequences or auto incrementing numbers.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@ -531,7 +391,6 @@ 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 and MsSQL and SEQUENCE for Oracle
|
||||
@ -648,12 +507,10 @@ To designate a composite primary key / identifier, simply put the
|
||||
Quoting Reserved Words
|
||||
----------------------
|
||||
|
||||
It may sometimes be necessary to quote a column or table name
|
||||
because it conflicts with a reserved word of the particular RDBMS
|
||||
in use. This is often referred to as "Identifier Quoting". To let
|
||||
Doctrine know that you would like a table or column name to be
|
||||
quoted in all SQL statements, enclose the table or column name in
|
||||
backticks. Here is an example:
|
||||
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.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -666,18 +523,116 @@ according to the used database platform.
|
||||
|
||||
.. warning::
|
||||
|
||||
Identifier Quoting is not supported for join column
|
||||
names or discriminator column names.
|
||||
Identifier Quoting does not work for join column names or discriminator
|
||||
column names.
|
||||
|
||||
.. warning::
|
||||
.. _reference-basic-mapping-custom-mapping-types:
|
||||
|
||||
Identifier Quoting is a feature that is mainly intended
|
||||
to support legacy database schemas. The use of reserved words and
|
||||
identifier quoting is generally discouraged. Identifier quoting
|
||||
should not be used to enable the use non-standard-characters such
|
||||
as a dash in a hypothetical column ``test-name``. Also Schema-Tool
|
||||
will likely have troubles when quoting is used for case-sensitivity
|
||||
reasons (in Oracle for example).
|
||||
Custom Mapping Types
|
||||
--------------------
|
||||
|
||||
Doctrine allows you to create new mapping types. This can come in
|
||||
handy when you're missing a specific mapping type or when you want
|
||||
to replace the existing implementation of a mapping type.
|
||||
|
||||
In order to create a new mapping type you need to subclass
|
||||
``Doctrine\DBAL\Types\Type`` and implement/override the methods as
|
||||
you wish. Here is an example skeleton of such a custom type class:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace My\Project\Types;
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
|
||||
/**
|
||||
* My custom datatype.
|
||||
*/
|
||||
class MyType extends Type
|
||||
{
|
||||
const MYTYPE = 'mytype'; // modify to match your type name
|
||||
|
||||
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
|
||||
{
|
||||
// return the SQL used to create your column type. To create a portable column type, use the $platform.
|
||||
}
|
||||
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
// This is executed when the value is read from the database. Make your conversions here, optionally using the $platform.
|
||||
}
|
||||
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
// This is executed when the value is written to the database. Make your conversions here, optionally using the $platform.
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return self::MYTYPE; // modify to match your constant name
|
||||
}
|
||||
}
|
||||
|
||||
The following assumptions are apply to mapping types by the ORM:
|
||||
|
||||
- If the value of the field is *NULL* the method
|
||||
``convertToDatabaseValue()`` is not called.
|
||||
- The ``UnitOfWork`` never passes values to the database convert
|
||||
method that did not change in the request.
|
||||
|
||||
When you have implemented the type you still need to let Doctrine
|
||||
know about it. This can be achieved through the
|
||||
``Doctrine\DBAL\Types\Type#addType($name, $className)``
|
||||
method. See the following example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// in bootstrapping code
|
||||
|
||||
// ...
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
|
||||
// ...
|
||||
|
||||
// Register my type
|
||||
Type::addType('mytype', 'My\Project\Types\MyType');
|
||||
|
||||
To convert the underlying database type of your
|
||||
new "mytype" directly into an instance of ``MyType`` when performing
|
||||
schema operations, the type has to be registered with the database
|
||||
platform as well:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$conn = $em->getConnection();
|
||||
$conn->getDatabasePlatform()->registerDoctrineTypeMapping('db_mytype', 'mytype');
|
||||
|
||||
When registering the custom types in the configuration you specify a unique
|
||||
name for the mapping type and map that to the corresponding fully qualified
|
||||
class name. Now the new type can be used when mapping columns:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
class MyPersistentClass
|
||||
{
|
||||
/** @Column(type="mytype") */
|
||||
private $field;
|
||||
}
|
||||
|
||||
Custom ColumnDefinition
|
||||
-----------------------
|
||||
|
||||
You can define a custom definition for each column using the "columnDefinition"
|
||||
attribute of ``@Column``. You have to define all the definitions that follow
|
||||
the name of a column here.
|
||||
|
||||
.. note::
|
||||
|
||||
Using columnDefinition will break change-detection in SchemaTool.
|
||||
|
||||
|
@ -47,7 +47,7 @@ access point to ORM functionality provided by Doctrine.
|
||||
use Doctrine\ORM\Tools\Setup;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
|
||||
$paths = array("/path/to/entities-or-mapping-files");
|
||||
$paths = array("/path/to/entity-files");
|
||||
$isDevMode = false;
|
||||
|
||||
// the connection configuration
|
||||
@ -66,6 +66,7 @@ Or if you prefer XML:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$paths = array("/path/to/xml-mappings");
|
||||
$config = Setup::createXMLMetadataConfiguration($paths, $isDevMode);
|
||||
$entityManager = EntityManager::create($dbParams, $config);
|
||||
|
||||
@ -74,15 +75,15 @@ Or if you prefer YAML:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$paths = array("/path/to/yml-mappings");
|
||||
$config = Setup::createYAMLMetadataConfiguration($paths, $isDevMode);
|
||||
$entityManager = EntityManager::create($dbParams, $config);
|
||||
|
||||
Inside the ``Setup`` methods several assumptions are made:
|
||||
|
||||
- If `$devMode` is true always use an ``ArrayCache`` (in-memory) and regenerate proxies on every request.
|
||||
- If `$devMode` is false, check for Caches in the order APC, Xcache, Memcache (127.0.0.1:11211), Redis (127.0.0.1:6379) unless `$cache` is passed as fourth argument.
|
||||
- If `$devMode` is false, set then proxy classes have to be explicitly created
|
||||
through the command line.
|
||||
- If `$isDevMode` is true caching is done in memory with the ``ArrayCache``. Proxy objects are recreated on every request.
|
||||
- If `$isDevMode` is false, check for Caches in the order APC, Xcache, Memcache (127.0.0.1:11211), Redis (127.0.0.1:6379) unless `$cache` is passed as fourth argument.
|
||||
- If `$isDevMode` is false, set then proxy classes have to be explicitly created through the command line.
|
||||
- If third argument `$proxyDir` is not set, use the systems temporary directory.
|
||||
|
||||
If you want to configure Doctrine in more detail, take a look at the :doc:`Advanced
|
||||
|
@ -2,27 +2,25 @@ Working with Associations
|
||||
=========================
|
||||
|
||||
Associations between entities are represented just like in regular
|
||||
object-oriented PHP, with references to other objects or
|
||||
collections of objects. When it comes to persistence, it is
|
||||
important to understand three main things:
|
||||
object-oriented PHP code using references to other objects or
|
||||
collections of objects.
|
||||
|
||||
Changes to associations in your code are not synchronized to the
|
||||
database directly, only when calling ``EntityManager#flush()``.
|
||||
|
||||
There are other concepts you should know about when working
|
||||
with associations in Doctrine:
|
||||
|
||||
- The :doc:`concept of owning and inverse sides <unitofwork-associations>`
|
||||
in bidirectional associations.
|
||||
- If an entity is removed from a collection, the association is
|
||||
removed, not the entity itself. A collection of entities always
|
||||
only represents the association to the containing entities, not the
|
||||
entity itself.
|
||||
- Collection-valued :ref:`persistent fields <architecture_persistent_fields>` have to be instances of the
|
||||
- When a bidirectional assocation is updated, Doctrine only checks
|
||||
on one of both sides for these changes. This is called the :doc:`owning side <unitofwork-associations>`
|
||||
of the association.
|
||||
- A property with a reference to many entities has to be instances of the
|
||||
``Doctrine\Common\Collections\Collection`` interface.
|
||||
|
||||
Changes to associations in your code are not synchronized to the
|
||||
database directly, but upon calling ``EntityManager#flush()``.
|
||||
|
||||
To describe all the concepts of working with associations we
|
||||
introduce a specific set of example entities that show all the
|
||||
different flavors of association management in Doctrine.
|
||||
|
||||
Association Example Entities
|
||||
----------------------------
|
||||
|
||||
@ -44,10 +42,6 @@ information about its type and if it's the owning or inverse side.
|
||||
* Bidirectional - Many users have Many favorite comments (OWNING SIDE)
|
||||
*
|
||||
* @ManyToMany(targetEntity="Comment", inversedBy="userFavorites")
|
||||
* @JoinTable(name="user_favorite_comments",
|
||||
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@JoinColumn(name="favorite_comment_id", referencedColumnName="id")}
|
||||
* )
|
||||
*/
|
||||
private $favorites;
|
||||
|
||||
@ -55,10 +49,6 @@ information about its type and if it's the owning or inverse side.
|
||||
* Unidirectional - Many users have marked many comments as read
|
||||
*
|
||||
* @ManyToMany(targetEntity="Comment")
|
||||
* @JoinTable(name="user_read_comments",
|
||||
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns={@JoinColumn(name="comment_id", referencedColumnName="id")}
|
||||
* )
|
||||
*/
|
||||
private $commentsRead;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user