Merge branch 'master' of github.com:doctrine/orm-documentation
This commit is contained in:
commit
e6e1243852
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
en/_exts/configurationblock.pyc
|
||||
build
|
8
README.md
Normal file
8
README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Doctrine ORM Documentation
|
||||
|
||||
## How to Generate
|
||||
|
||||
1. Run ./bin/install-dependencies.sh
|
||||
2. Run ./bin/generate-docs.sh
|
||||
|
||||
It will generate the documentation into the build directory of the checkout.
|
10
bin/generate-docs.sh
Executable file
10
bin/generate-docs.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
EXECPATH=`dirname $0`
|
||||
cd $EXECPATH
|
||||
cd ..
|
||||
|
||||
rm build -Rf
|
||||
sphinx-build en build
|
||||
|
||||
sphinx-build -b latex en build/pdf
|
||||
rubber --into build/pdf --pdf build/pdf/Doctrine2ORM.tex
|
4
bin/install-dependencies.sh
Normal file
4
bin/install-dependencies.sh
Normal file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
sudo apt-get install python25 python25-dev texlive-full rubber
|
||||
sudo easy_install pygments
|
||||
sudo easy_install sphinx
|
14
convert.sh
14
convert.sh
@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
FILES=`find -iname *.txt -print`
|
||||
for FILE in $FILES
|
||||
do
|
||||
# replace the + to # chars
|
||||
sed -i -r 's/^([+]{4})\s/#### /' $FILE
|
||||
sed -i -r 's/^([+]{3})\s/### /' $FILE
|
||||
sed -i -r 's/^([+]{2})\s/## /' $FILE
|
||||
sed -i -r 's/^([+]{1})\s/# /' $FILE
|
||||
sed -i -r 's/(\[php\])/<?php/' $FILE
|
||||
|
||||
# convert markdown to reStructured Text
|
||||
pandoc -f markdown -t rst $FILE > ${FILE%.txt}.rst
|
||||
done
|
@ -38,20 +38,20 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Doctrine 2 ORM'
|
||||
copyright = u'2010, Doctrine Project Team'
|
||||
copyright = u'2010-11, Doctrine Project Team'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '2.0'
|
||||
version = '2.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '2.0.0'
|
||||
release = '2.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
language = 'php'
|
||||
language = 'en'
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
|
@ -34,7 +34,7 @@ will even detect this match correctly when using SchemaTool update commands.
|
||||
|
||||
<?php
|
||||
$conn = $em->getConnection();
|
||||
$conn->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'varchar');
|
||||
$conn->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
|
||||
|
||||
In this case you have to ensure that each varchar field that is an enum in the
|
||||
database only gets passed the allowed values. You can easily enforce this in your
|
||||
|
@ -401,7 +401,6 @@ Optional attributes:
|
||||
- **nullable**: Determine if the related entity is required, or if
|
||||
null is an allowed state for the relation. Defaults to true.
|
||||
- **onDelete**: Cascade Action (Database-level)
|
||||
- **onUpdate**: Cascade Action (Database-level)
|
||||
- **columnDefinition**: DDL SQL snippet that starts after the column
|
||||
name and specifies the complete (non-portable!) column definition.
|
||||
This attribute allows to make use of advanced RMDBS features. Using
|
||||
@ -528,14 +527,16 @@ Optional attributes:
|
||||
- **inversedBy**: The inversedBy attribute designates the field in the
|
||||
entity that is the inverse side of the relationship.
|
||||
- **cascade**: Cascade Option
|
||||
- **fetch**: One of LAZY or EAGER
|
||||
- **fetch**: One of LAZY, EXTRA_LAZY or EAGER
|
||||
- **indexBy**: Index the collection by a field on the target entity.
|
||||
|
||||
**NOTE** For ManyToMany bidirectional relationships either side may
|
||||
.. note::
|
||||
|
||||
For ManyToMany bidirectional relationships either side may
|
||||
be the owning side (the side that defines the @JoinTable and/or
|
||||
does not make use of the mappedBy attribute, thus using a default
|
||||
join table).
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: php
|
||||
@ -635,6 +636,8 @@ Optional attributes:
|
||||
- **mappedBy**: This option specifies the property name on the
|
||||
targetEntity that is the owning side of this relation. Its a
|
||||
required attribute for the inverse side of a relationship.
|
||||
- **fetch**: One of LAZY, EXTRA_LAZY or EAGER.
|
||||
- **indexBy**: Index the collection by a field on the target entity.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -384,6 +384,11 @@ Or you can trigger the validation manually:
|
||||
If the mapping is invalid the errors array contains a positive
|
||||
number of elements with error messages.
|
||||
|
||||
.. warning::
|
||||
|
||||
One mapping option that is not validated is the use of the referenced column name.
|
||||
It has to point to the equivalent primary key otherwise Doctrine will not work.
|
||||
|
||||
.. note::
|
||||
|
||||
One common error is to use a backlash in front of the
|
||||
|
@ -176,7 +176,12 @@ built-in mapping types:
|
||||
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.
|
||||
Doctrine 2.
|
||||
|
||||
.. 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::
|
||||
|
||||
|
@ -557,6 +557,9 @@ clauses:
|
||||
- TRIM([LEADING \| TRAILING \| BOTH] ['trchar' FROM] str) - Trim
|
||||
the string by the given trim char, defaults to whitespaces.
|
||||
- UPPER(str) - Return the upper-case of the given string.
|
||||
- DATE_ADD(date, days) - Add the number of days to a given date.
|
||||
- DATE_SUB(date, days) - Substract the number of days from a given date.
|
||||
- DATE_DIFF(date1, date2) - Calculate the difference in days between date1-date2.
|
||||
|
||||
Arithmetic operators
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
@ -1263,6 +1266,29 @@ number of results:
|
||||
entity might appear in many rows, effectively hydrating less than
|
||||
the specified number of results.
|
||||
|
||||
.. _dql-temporarily-change-fetch-mode:
|
||||
|
||||
Temporarily change fetch mode in DQL
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
While normally all your associations are marked as lazy or extra lazy you will have cases where you are using DQL and don't want to
|
||||
fetch join a second, third or fourth level of entities into your result, because of the increased cost of the SQL JOIN. You
|
||||
can mark a many-to-one or one-to-one association as fetched temporarily to batch fetch these entities using a WHERE .. IN query.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$query = $em->createQuery("SELECT u FROM MyProject\User u");
|
||||
$query->setFetchMode("MyProject\User", "address", "EAGER");
|
||||
$query->execute();
|
||||
|
||||
Given that there are 10 users and corresponding addresses in the database the executed queries will look something like:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT * FROM users;
|
||||
SELECT * FROM address WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
|
||||
|
||||
EBNF
|
||||
----
|
||||
@ -1402,7 +1428,7 @@ Items
|
||||
.. code-block:: php
|
||||
|
||||
UpdateItem ::= IdentificationVariable "." (StateField | SingleValuedAssociationField) "=" NewValue
|
||||
OrderByItem ::= (ResultVariable | StateFieldPathExpression) ["ASC" | "DESC"]
|
||||
OrderByItem ::= (ResultVariable | SingleValuedPathExpression) ["ASC" | "DESC"]
|
||||
GroupByItem ::= IdentificationVariable | SingleValuedPathExpression
|
||||
NewValue ::= ScalarExpression | SimpleEntityExpression | "NULL"
|
||||
|
||||
@ -1424,11 +1450,11 @@ Select Expressions
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
SelectExpression ::= IdentificationVariable | PartialObjectExpression | (AggregateExpression | "(" Subselect ")" | FunctionDeclaration | ScalarExpression) [["AS"] AliasResultVariable]
|
||||
SimpleSelectExpression ::= ScalarExpression | IdentificationVariable |
|
||||
(AggregateExpression [["AS"] AliasResultVariable])
|
||||
SelectExpression ::= IdentificationVariable | PartialObjectExpression | (AggregateExpression | "(" Subselect ")" | FunctionDeclaration | ScalarExpression) [["AS"] AliasResultVariable]
|
||||
SimpleSelectExpression ::= ScalarExpression | IdentificationVariable |
|
||||
(AggregateExpression [["AS"] AliasResultVariable])
|
||||
PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
|
||||
PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
|
||||
PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
|
||||
|
||||
Conditional Expressions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -1441,7 +1467,9 @@ Conditional Expressions
|
||||
ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")"
|
||||
SimpleConditionalExpression ::= ComparisonExpression | BetweenExpression | LikeExpression |
|
||||
InExpression | NullComparisonExpression | ExistsExpression |
|
||||
EmptyCollectionComparisonExpression | CollectionMemberExpression
|
||||
EmptyCollectionComparisonExpression | CollectionMemberExpression |
|
||||
InstanceOfExpression
|
||||
|
||||
|
||||
Collection Expressions
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -1479,7 +1507,7 @@ Arithmetic Expressions
|
||||
ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary
|
||||
ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")"
|
||||
| FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings
|
||||
| FunctionsReturningDatetime | IdentificationVariable | InputParameter
|
||||
| FunctionsReturningDatetime | IdentificationVariable | InputParameter | CaseExpression
|
||||
|
||||
Scalar and Type Expressions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -1487,9 +1515,9 @@ Scalar and Type Expressions
|
||||
.. code-block:: php
|
||||
|
||||
ScalarExpression ::= SimpleArithmeticExpression | StringPrimary | DateTimePrimary | StateFieldPathExpression
|
||||
BooleanPrimary | EntityTypeExpression
|
||||
BooleanPrimary | EntityTypeExpression | CaseExpression
|
||||
StringExpression ::= StringPrimary | "(" Subselect ")"
|
||||
StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression
|
||||
StringPrimary ::= StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression | CaseExpression
|
||||
BooleanExpression ::= BooleanPrimary | "(" Subselect ")"
|
||||
BooleanPrimary ::= StateFieldPathExpression | boolean | InputParameter
|
||||
EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
|
||||
@ -1509,6 +1537,20 @@ Aggregate Expressions
|
||||
AggregateExpression ::= ("AVG" | "MAX" | "MIN" | "SUM") "(" ["DISTINCT"] StateFieldPathExpression ")" |
|
||||
"COUNT" "(" ["DISTINCT"] (IdentificationVariable | SingleValuedPathExpression) ")"
|
||||
|
||||
Case Expressions
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
CaseExpression ::= GeneralCaseExpression | SimpleCaseExpression | CoalesceExpression | NullifExpression
|
||||
GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END"
|
||||
WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression
|
||||
SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END"
|
||||
CaseOperand ::= StateFieldPathExpression | TypeDiscriminator
|
||||
SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression
|
||||
CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
|
||||
NullifExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
|
||||
|
||||
Other Expressions
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
@ -1520,6 +1562,8 @@ QUANTIFIED/BETWEEN/COMPARISON/LIKE/NULL/EXISTS
|
||||
BetweenExpression ::= ArithmeticExpression ["NOT"] "BETWEEN" ArithmeticExpression "AND" ArithmeticExpression
|
||||
ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression )
|
||||
InExpression ::= StateFieldPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")"
|
||||
InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
|
||||
InstanceOfParameter ::= AbstractSchemaName | InputParameter
|
||||
LikeExpression ::= StringExpression ["NOT"] "LIKE" string ["ESCAPE" char]
|
||||
NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL"
|
||||
ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")"
|
||||
|
@ -570,7 +570,9 @@ postUpdate, postRemove, postPersist
|
||||
|
||||
The three post events are called inside ``EntityManager#flush()``.
|
||||
Changes in here are not relevant to the persistence in the
|
||||
database, but you can use this events to
|
||||
database, but you can use these events to alter non-persistable items,
|
||||
like non-mapped fields, logging or even associated classes that are
|
||||
directly mapped by Doctrine.
|
||||
|
||||
postLoad
|
||||
~~~~~~~~
|
||||
|
@ -7,6 +7,17 @@ Frequently Asked Questions
|
||||
what is often asked. If you stumble accross an unanswerd question please write a mail to the mailing-list or
|
||||
join the #doctrine channel on Freenode IRC.
|
||||
|
||||
Database Schema
|
||||
---------------
|
||||
|
||||
How do I set the charset and collation for MySQL tables?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can't set these values inside the annotations, yml or xml mapping files. To make a database
|
||||
work with the default charset and collation you should configure MySQL to use it as default charset,
|
||||
or create the database with charset and collation details. This way they get inherited to all newly
|
||||
created database tables and columns.
|
||||
|
||||
Entity Classes
|
||||
--------------
|
||||
|
||||
@ -144,12 +155,16 @@ EntityGenerator
|
||||
Why does the EntityGenerator not do X?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.
|
||||
The EntityGenerator is not a full fledged code-generator that solves all tasks. Code-Generation
|
||||
is not a first-class priority in Doctrine 2 anymore (compared to Doctrine 1). The EntityGenerator
|
||||
is supposed to kick-start you, but not towards 100%.
|
||||
|
||||
Why does the EntityGenerator not generate inheritance correctly?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.
|
||||
Just from the details of the discriminator map the EntityGenerator cannot guess the inheritance hierachy.
|
||||
This is why the generation of inherited entities does not fully work. You have to adjust some additional
|
||||
code to get this one working correctly.
|
||||
|
||||
Performance
|
||||
-----------
|
||||
@ -172,15 +187,15 @@ What is DQL?
|
||||
DQL stands for Doctrine Query Language, a query language that very much looks like SQL
|
||||
but has some important benefits when using Doctrine:
|
||||
|
||||
* It uses class names and fields instead of tables and columns, separating concerns between backend and your object model.
|
||||
* It utilizes the metadata defined to offer a range of shortcuts when writing. For example you do not have to specify the ON clause of joins, since Doctrine already knows about them.
|
||||
* It adds some functionality that is related to object management and transforms them into SQL.
|
||||
- It uses class names and fields instead of tables and columns, separating concerns between backend and your object model.
|
||||
- It utilizes the metadata defined to offer a range of shortcuts when writing. For example you do not have to specify the ON clause of joins, since Doctrine already knows about them.
|
||||
- It adds some functionality that is related to object management and transforms them into SQL.
|
||||
|
||||
It also has some drawbacks of course:
|
||||
|
||||
* The syntax is slightly different to SQL so you have to learn and remember the differences.
|
||||
* To be vendor independent it can only implement a subset of all the existing SQL dialects. Vendor specific functionality and optimizations cannot be used through DQL unless implemented by you explicitly.
|
||||
* For some DQL constructs subselects are used which are known to be slow in MySQL.
|
||||
- The syntax is slightly different to SQL so you have to learn and remember the differences.
|
||||
- To be vendor independent it can only implement a subset of all the existing SQL dialects. Vendor specific functionality and optimizations cannot be used through DQL unless implemented by you explicitly.
|
||||
- For some DQL constructs subselects are used which are known to be slow in MySQL.
|
||||
|
||||
Can I sort by a function (for example ORDER BY RAND()) in DQL?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -42,6 +42,19 @@ for updates, which means when you call flush on the EntityManager these entities
|
||||
even if properties changed. Read-Only allows to persist new entities of a kind and remove existing
|
||||
ones, they are just not considered for updates.
|
||||
|
||||
Extra-Lazy Collections
|
||||
----------------------
|
||||
|
||||
If entities hold references to large collections you will get performance and memory problems initializing them.
|
||||
To solve this issue you can use the EXTRA_LAZY fetch-mode feature for collections. See the :doc:`tutorial <../tutorials/extra-lazy-associations>`
|
||||
for more information on how this fetch mode works.
|
||||
|
||||
Temporarily change fetch mode in DQL
|
||||
------------------------------------
|
||||
|
||||
See :ref:`Doctrine Query Language chapter <dql-temporarily-change-fetch-mode>`
|
||||
|
||||
|
||||
Apply Best Practices
|
||||
--------------------
|
||||
|
||||
|
@ -17,94 +17,12 @@ solved in the future. Any of this limitations now stated has at
|
||||
least one ticket in the Tracker and is discussed for future
|
||||
releases.
|
||||
|
||||
Foreign Keys as Identifiers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Join-Columns with non-primary keys
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. note::
|
||||
|
||||
Foreign keys as identifiers are currently in master and will be included in Doctrine 2.1
|
||||
|
||||
There are many use-cases where you would want to use an
|
||||
Entity-Attribute-Value approach to modelling and define a
|
||||
table-schema like the following:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
CREATE TABLE product (
|
||||
id INTEGER,
|
||||
name VARCHAR,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
CREATE TABLE product_attributes (
|
||||
product_id INTEGER,
|
||||
attribute_name VARCHAR,
|
||||
attribute_value VARCHAR,
|
||||
PRIMARY KEY (product_id, attribute_name)
|
||||
);
|
||||
|
||||
This is currently *NOT* possible with Doctrine2. You have to define
|
||||
a surrogate key on the ``product_attributes`` table and use a
|
||||
unique-constraint for the ``product_id`` and ``attribute_name``.
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
CREATE TABLE product_attributes (
|
||||
attribute_id, INTEGER,
|
||||
product_id INTEGER,
|
||||
attribute_name VARCHAR,
|
||||
attribute_value VARCHAR,
|
||||
PRIMARY KEY (attribute_id),
|
||||
UNIQUE (product_id, attribute_name)
|
||||
);
|
||||
|
||||
Although we state that we support composite primary keys that does
|
||||
not currently include foreign keys as primary key columns. To see
|
||||
the fundamental difference between the two different
|
||||
``product_attributes`` tables you should see how they translate
|
||||
into a Doctrine Mapping (Using Annotations):
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* Scenario 1: THIS IS NOT POSSIBLE CURRENTLY
|
||||
* @Entity @Table(name="product_attributes")
|
||||
*/
|
||||
class ProductAttribute
|
||||
{
|
||||
/** @Id @ManyToOne(targetEntity="Product") */
|
||||
private $product;
|
||||
|
||||
/** @Id @Column(type="string", name="attribute_name") */
|
||||
private $name;
|
||||
|
||||
/** @Column(type="string", name="attribute_value") */
|
||||
private $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 2: Using the surrogate key workaround
|
||||
* @Entity
|
||||
* @Table(name="product_attributes", uniqueConstraints={@UniqueConstraint(columns={"product_id", "attribute_name"})}))
|
||||
*/
|
||||
class ProductAttribute
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
|
||||
/** @ManyToOne(targetEntity="Product") */
|
||||
private $product;
|
||||
|
||||
/** @Column(type="string", name="attribute_name") */
|
||||
private $name;
|
||||
|
||||
/** @Column(type="string", name="attribute_value") */
|
||||
private $value;
|
||||
}
|
||||
|
||||
The following Jira Issue
|
||||
`contains the feature request to allow @ManyToOne and @OneToOne annotations along the @Id annotation <http://www.doctrine-project.org/jira/browse/DDC-117>`_.
|
||||
It is not possible to use join columns pointing to non-primary keys. Doctrine will think these are the primary
|
||||
keys and create lazy-loading proxies with the data, which can lead to unexpected results. Doctrine can for performance
|
||||
reasons not validate the correctness of this settings at runtime but only through the Validate Schema command.
|
||||
|
||||
Mapping Arrays to a Join Table
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -223,21 +141,6 @@ benefit from custom persister implementations:
|
||||
- `Evaluate possible ways in which stored-procedures can be used <http://www.doctrine-project.org/jira/browse/DDC-445>`_
|
||||
- The previous Filter Rules Feature Request
|
||||
|
||||
Paginating Associations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. note::
|
||||
|
||||
Extra Lazy Collections are currently in master and will be included in Doctrine 2.1
|
||||
|
||||
It is not possible to paginate only parts of an associations at the moment. You can always only
|
||||
load the whole association/collection into memory. This is rather problematic for large collections,
|
||||
but we already plan to add facilities to fix this for Doctrine 2.1
|
||||
|
||||
- `DDC-546 New Fetch Mode EXTRA_LAZY <http://www.doctrine-project.org/jira/browse/DDC-546>`_
|
||||
- `Blog: Working with large collections (Workaround) <http://www.doctrine-project.org/blog/doctrine2-large-collections>`_
|
||||
- `LargeCollections Helper <http://github.com/beberlei/DoctrineExtensions>`_
|
||||
|
||||
Persist Keys of Collections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -229,7 +229,6 @@ which meta-column is the discriminator column of this tree.
|
||||
* @param string $alias The alias of the entity result or joined entity result the discriminator
|
||||
* column should be used for.
|
||||
* @param string $discrColumn The name of the discriminator column in the SQL result set.
|
||||
* @todo Rename: addDiscriminatorColumn
|
||||
*/
|
||||
public function setDiscriminatorColumn($alias, $discrColumn)
|
||||
|
||||
@ -363,4 +362,28 @@ are actually a subtype of User. When using DQL, Doctrine
|
||||
automatically includes the necessary joins for this mapping
|
||||
strategy but with native SQL it is your responsibility.
|
||||
|
||||
ResultSetMappingBuilder
|
||||
-----------------------
|
||||
|
||||
There are some downsides with Native SQL queries. The primary one is that you have to adjust all result set mapping
|
||||
definitions if names of columns change. In DQL this is detected dynamically when the Query is regenerated with
|
||||
the current metadata.
|
||||
|
||||
To avoid this hassle you can use the ``ResultSetMappingBuilder`` class. It allows to add all columns of an entity
|
||||
to a result set mapping. To avoid clashes you can optionally rename specific columns when you are doing the same
|
||||
in your sQL statement:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
$sql = "SELECT u.id, u.name, a.id AS address_id, a.street, a.city " .
|
||||
"FROM users u INNER JOIN address a ON u.address_id = a.id";
|
||||
|
||||
$rsm = new ResultSetMappingBuilder;
|
||||
$rsm->addRootEntityFromClassMetadata('MyProject\User', 'u');
|
||||
$rsm->addJoinedEntityFromClassMetadata('MyProject\Address', 'a', array('id' => 'address_id'));
|
||||
|
||||
For entites with more columns the builder is very convenient to use. It extends the ``ResultSetMapping`` class
|
||||
and as such has all the functionality of it as well. Currently the ``ResultSetMappingBuilder`` does not support
|
||||
entities with inheritance.
|
||||
|
@ -163,7 +163,7 @@ mentioned syntax with "getParameter()" or "getParameters()":
|
||||
// $qb instanceof QueryBuilder
|
||||
|
||||
// See example above
|
||||
$params = qb->getParameters(array(1, 2));
|
||||
$params = $qb->getParameters(array(1, 2));
|
||||
// Equivalent to
|
||||
$param = array($qb->getParameter(1), $qb->getParameter(2));
|
||||
|
||||
@ -240,10 +240,10 @@ To simplify some of these efforts, we introduce what we call as
|
||||
The Expr class
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
To workaround most of the issues that ``add()`` method may cause,
|
||||
To workaround some of the issues that ``add()`` method may cause,
|
||||
Doctrine created a class that can be considered as a helper for
|
||||
building queries. This class is called ``Expr``, which provides a
|
||||
set of useful static methods to help building queries:
|
||||
building expressions. This class is called ``Expr``, which provides a
|
||||
set of useful methods to help build expressions:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -251,13 +251,13 @@ set of useful static methods to help building queries:
|
||||
// $qb instanceof QueryBuilder
|
||||
|
||||
// example8: QueryBuilder port of: "SELECT u FROM User u WHERE u.id = ? OR u.nickname LIKE ? ORDER BY u.surname DESC" using Expr class
|
||||
$qb->add('select', $qb->expr()->select('u'))
|
||||
->add('from', $qb->expr()->from('User', 'u'))
|
||||
->add('where', $qb->expr()->orx(
|
||||
$qb->add('select', new Expr\Select(array('u')))
|
||||
->add('from', new Expr\From('User', 'u'))
|
||||
->add('where', $qb->expr()->orX(
|
||||
$qb->expr()->eq('u.id', '?1'),
|
||||
$qb->expr()->like('u.nickname', '?2')
|
||||
))
|
||||
->add('orderBy', $qb->expr()->orderBy('u.surname', 'ASC'));
|
||||
->add('orderBy', new Expr\OrderBy('u.name', 'ASC'));
|
||||
|
||||
Although it still sounds complex, the ability to programmatically
|
||||
create conditions are the main feature of ``Expr``. Here it is a
|
||||
@ -270,11 +270,11 @@ complete list of supported helper methods available:
|
||||
{
|
||||
/** Conditional objects **/
|
||||
|
||||
// Example - $qb->expr()->andx($cond1 [, $condN])->add(...)->...
|
||||
public function andx($x = null); // Returns Expr\Andx instance
|
||||
// Example - $qb->expr()->andX($cond1 [, $condN])->add(...)->...
|
||||
public function andX($x = null); // Returns Expr\AndX instance
|
||||
|
||||
// Example - $qb->expr()->orx($cond1 [, $condN])->add(...)->...
|
||||
public function orx($x = null); // Returns Expr\Orx instance
|
||||
// Example - $qb->expr()->orX($cond1 [, $condN])->add(...)->...
|
||||
public function orX($x = null); // Returns Expr\OrX instance
|
||||
|
||||
|
||||
/** Comparison objects **/
|
||||
@ -296,6 +296,12 @@ complete list of supported helper methods available:
|
||||
|
||||
// Example - $qb->expr()->gte('u.id', '?1') => u.id >= ?1
|
||||
public function gte($x, $y); // Returns Expr\Comparison instance
|
||||
|
||||
// Example - $qb->expr()->isNull('u.id') => u.id IS NULL
|
||||
public function isNull($x); // Returns string
|
||||
|
||||
// Example - $qb->expr()->isNotNull('u.id') => u.id IS NOT NULL
|
||||
public function isNotNull($x); // Returns string
|
||||
|
||||
|
||||
/** Arithmetic objects **/
|
||||
@ -425,7 +431,7 @@ suggested standard way to build queries:
|
||||
// example8: QueryBuilder port of: "SELECT u FROM User u WHERE u.id = ?1 OR u.nickname LIKE ?2 ORDER BY u.surname DESC" using QueryBuilder helper methods
|
||||
$qb->select(array('u')) // string 'u' is converted to array internally
|
||||
->from('User', 'u')
|
||||
->where($qb->expr()->orx(
|
||||
->where($qb->expr()->orX(
|
||||
$qb->expr()->eq('u.id', '?1'),
|
||||
$qb->expr()->like('u.nickname', '?2')
|
||||
))
|
||||
@ -469,11 +475,11 @@ Here is a complete list of helper methods available in
|
||||
// NOTE: ->where() overrides all previously set conditions
|
||||
//
|
||||
// Example - $qb->where('u.firstName = ?1', $qb->expr()->eq('u.surname', '?2'))
|
||||
// Example - $qb->where($qb->expr()->andx($qb->expr()->eq('u.firstName', '?1'), $qb->expr()->eq('u.surname', '?2')))
|
||||
// Example - $qb->where($qb->expr()->andX($qb->expr()->eq('u.firstName', '?1'), $qb->expr()->eq('u.surname', '?2')))
|
||||
// Example - $qb->where('u.firstName = ?1 AND u.surname = ?2')
|
||||
public function where($where);
|
||||
|
||||
// Example - $qb->andWhere($qb->expr()->orx($qb->expr()->lte('u.age', 40), 'u.numChild = 0'))
|
||||
// Example - $qb->andWhere($qb->expr()->orX($qb->expr()->lte('u.age', 40), 'u.numChild = 0'))
|
||||
public function andWhere($where);
|
||||
|
||||
// Example - $qb->orWhere($qb->expr()->between('u.id', 1, 10));
|
||||
|
@ -246,8 +246,8 @@ the database permanently.
|
||||
|
||||
Notice how both sides of the bidirectional association are always
|
||||
updated. Unidirectional associations are consequently simpler to
|
||||
handle. Also note that if you type-hint your methods, i.e.
|
||||
``setAddress(Address $address)``, then PHP does only allows null
|
||||
handle. Also note that if you use type-hinting in your methods, i.e.
|
||||
``setAddress(Address $address)``, PHP will only allow null
|
||||
values if ``null`` is set as default value. Otherwise
|
||||
setAddress(null) will fail for removing the association. If you
|
||||
insist on type-hinting a typical way to deal with this is to
|
||||
@ -279,8 +279,9 @@ entities that have been re-added to the collection.
|
||||
|
||||
Say you clear a collection of tags by calling
|
||||
``$post->getTags()->clear();`` and then call
|
||||
``$post->getTags()->add($tag)``. This will not recognize tag being
|
||||
already added before and issue two database calls.
|
||||
``$post->getTags()->add($tag)``. This will not recognize the tag having
|
||||
already been added previously and will consequently issue two separate database
|
||||
calls.
|
||||
|
||||
Association Management Methods
|
||||
------------------------------
|
||||
@ -380,9 +381,9 @@ as your preferences.
|
||||
Synchronizing Bidirectional Collections
|
||||
---------------------------------------
|
||||
|
||||
In the case of Many-To-Many associations you as the developer are
|
||||
responsible to keep the collections on the owning and inverse side
|
||||
up in sync, when you apply changes to them. Doctrine can only
|
||||
In the case of Many-To-Many associations you as the developer have the
|
||||
responsibility of keeping the collections on the owning and inverse side
|
||||
in sync when you apply changes to them. Doctrine can only
|
||||
guarantee a consistent state for the hydration, not for your client
|
||||
code.
|
||||
|
||||
@ -468,7 +469,7 @@ code would fail if you removed the call to
|
||||
cascade the persist operation to all nested entities that are new
|
||||
as well.
|
||||
|
||||
More complicated is the deletion of all a users comments when he is
|
||||
More complicated is the deletion of all of a user's comments when he is
|
||||
removed from the system:
|
||||
|
||||
.. code-block:: php
|
||||
@ -590,7 +591,7 @@ and StandingData:
|
||||
}
|
||||
}
|
||||
|
||||
Now two examples what happens when you remove the references:
|
||||
Now two examples of what happens when you remove the references:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -602,7 +603,8 @@ Now two examples what happens when you remove the references:
|
||||
|
||||
$em->flush();
|
||||
|
||||
In this case you have only changed the ``Contact`` entity but you removed
|
||||
the references for standing data and one address reference. When flush is called
|
||||
not only are the references removed but both the old standing data and the one
|
||||
address entity are also deleted from the database.
|
||||
In this case you have not only changed the ``Contact`` entity itself but
|
||||
you have also removed the references for standing data and as well as one
|
||||
address reference. When flush is called not only are the references removed
|
||||
but both the old standing data and the one address entity are also deleted
|
||||
from the database.
|
||||
|
@ -692,10 +692,28 @@ methods on a repository as follows:
|
||||
|
||||
You can also load by owning side associations through the repository:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$number = $em->find('MyProject\Domain\Phonenumber', 1234);
|
||||
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('phone' => $number->getId()));
|
||||
|
||||
Take not that this only works by passing the ID of the associated entity, not yet by passing the associated entity itself.
|
||||
Be careful that this only works by passing the ID of the associated entity, not yet by passing the associated entity itself.
|
||||
|
||||
The ``EntityRepository#findBy()`` method additionally accepts orderings, limit and offset as second to fourth parameters:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$tenUsers = $em->getRepository('MyProject\Domain\User')-findBy(array('age' => 20), array('name' => 'ASC'), 10, 0);
|
||||
|
||||
If you pass an array of values Doctrine will convert the query into a WHERE field IN (..) query automatically:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
$users = $em->getRepository('MyProject\Domain\User')-findBy(array('age' => array(20, 30, 40)));
|
||||
// translates roughly to: SELECT * FROM users WHERE age IN (20, 30, 40)
|
||||
|
||||
An EntityRepository also provides a mechanism for more concise
|
||||
calls through its use of ``__call``. Thus, the following two
|
||||
|
@ -448,7 +448,7 @@ Optional attributes for owning One-to-One:
|
||||
field on the inverse entity that contains the back-reference.
|
||||
- orphan-removal - If true, the inverse side entity is always
|
||||
deleted when the owning side entity is. Defaults to false.
|
||||
- fetch - Either LAZY or FETCH, defaults to LAZY. This attribute
|
||||
- fetch - Either LAZY or EAGER, defaults to LAZY. This attribute
|
||||
makes only sense on the owning side, the inverse side *ALWAYS* has
|
||||
to use the ``FETCH`` strategy.
|
||||
|
||||
@ -501,7 +501,7 @@ Optional attributes:
|
||||
always deleted when the owning side entity is and it is not
|
||||
connected to any other owning side entity anymore. Defaults to
|
||||
false.
|
||||
- fetch - Either LAZY or FETCH, defaults to LAZY.
|
||||
- fetch - Either LAZY or EAGER, defaults to LAZY.
|
||||
|
||||
This definition relies on a bunch of mapping defaults with regards
|
||||
to the naming of the join-column/foreign key. The explicitly
|
||||
@ -547,7 +547,8 @@ Required attributes:
|
||||
Optional attributes:
|
||||
|
||||
|
||||
- fetch - Either LAZY or FETCH, defaults to LAZY.
|
||||
- fetch - Either LAZY, EXTRA_LAZY or EAGER, defaults to LAZY.
|
||||
- index-by: Index the collection by a field on the target entity.
|
||||
|
||||
Defining Many-To-Many Associations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -579,7 +580,8 @@ Optional attributes:
|
||||
- inversed-by - If the association is bidirectional the
|
||||
inversed-by attribute has to be specified with the name of the
|
||||
field on the inverse entity that contains the back-reference.
|
||||
- fetch - Either LAZY or FETCH, defaults to LAZY.
|
||||
- fetch - Either LAZY, EXTRA_LAZY or EAGER, defaults to LAZY.
|
||||
- index-by: Index the collection by a field on the target entity.
|
||||
|
||||
The mapping defaults would lead to a join-table with the name
|
||||
"User\_Group" being created that contains two columns "user\_id"
|
||||
@ -698,4 +700,26 @@ table you can use the ``<indexes />`` and
|
||||
You have to specify the column and not the entity-class field names
|
||||
in the index and unique-constraint definitions.
|
||||
|
||||
Derived Entities ID syntax
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If the primary key of an entity contains a foreign key to another entity we speak of a derived
|
||||
entity relationship. You can define this in XML with the "association-key" attribute in the ``<id>`` tag.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Application\Model\ArticleAttribute">
|
||||
<id name="article" association-key="true" />
|
||||
<id name="attribute" type="string" />
|
||||
|
||||
<field name="value" type="string" />
|
||||
|
||||
<many-to-one field="article" target-entity="Article" inversed-by="attributes" />
|
||||
<entity>
|
||||
|
||||
</doctrine-mapping>
|
||||
|
@ -1,5 +1,5 @@
|
||||
Composite Primary Keys
|
||||
======================
|
||||
Composite and Foreign Keys as Primary Key
|
||||
=========================================
|
||||
|
||||
Doctrine 2 supports composite primary keys natively. Composite keys are a very powerful relational database concept
|
||||
and we took good care to make sure Doctrine 2 supports as many of the composite primary key use-cases.
|
||||
@ -144,55 +144,92 @@ Use-Case 1: Dynamic Attributes
|
||||
|
||||
We keep up the example of an Article with arbitrary attributes, the mapping looks like this:
|
||||
|
||||
.. code-block:: php
|
||||
.. configuration-block::
|
||||
|
||||
<?php
|
||||
namespace Application\Model;
|
||||
.. code-block:: php
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
<?php
|
||||
namespace Application\Model;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
/** @Column(type="string") */
|
||||
private $title;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"}, indexBy="attribute")
|
||||
* @Entity
|
||||
*/
|
||||
private $attributes;
|
||||
|
||||
public function addAttribute($name, $value)
|
||||
class Article
|
||||
{
|
||||
$this->attributes[$name] = new ArticleAttribute($name, $value, $this);
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
/** @Column(type="string") */
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"}, indexBy="attribute")
|
||||
*/
|
||||
private $attributes;
|
||||
|
||||
public function addAttribute($name, $value)
|
||||
{
|
||||
$this->attributes[$name] = new ArticleAttribute($name, $value, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class ArticleAttribute
|
||||
{
|
||||
/** @Id @ManyToOne(targetEntity="Article", inversedBy="attributes") */
|
||||
private $article;
|
||||
|
||||
/** @Id @Column(type="string") */
|
||||
private $attribute;
|
||||
|
||||
/** @Column(type="string") */
|
||||
private $value;
|
||||
|
||||
public function __construct($name, $value, $article)
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class ArticleAttribute
|
||||
{
|
||||
$this->attribute = $name;
|
||||
$this->value = $value;
|
||||
$this->article = $article;
|
||||
/** @Id @ManyToOne(targetEntity="Article", inversedBy="attributes") */
|
||||
private $article;
|
||||
|
||||
/** @Id @Column(type="string") */
|
||||
private $attribute;
|
||||
|
||||
/** @Column(type="string") */
|
||||
private $value;
|
||||
|
||||
public function __construct($name, $value, $article)
|
||||
{
|
||||
$this->attribute = $name;
|
||||
$this->value = $value;
|
||||
$this->article = $article;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Application\Model\ArticleAttribute">
|
||||
<id name="article" association-key="true" />
|
||||
<id name="attribute" type="string" />
|
||||
|
||||
<field name="value" type="string" />
|
||||
|
||||
<many-to-one field="article" target-entity="Article" inversed-by="attributes" />
|
||||
<entity>
|
||||
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Application\Model\ArticleAttribute:
|
||||
type: entity
|
||||
id:
|
||||
article:
|
||||
associationKey: true
|
||||
attribute:
|
||||
type: string
|
||||
fields:
|
||||
value:
|
||||
type: string
|
||||
manyToOne:
|
||||
article:
|
||||
targetEntity: Article
|
||||
inversedBy: attributes
|
||||
|
||||
|
||||
Use-Case 2: Simple Derived Identity
|
||||
@ -311,4 +348,4 @@ a simple surrogate key. This performance impact is mostly due to additional PHP
|
||||
necessary to handle this kind of keys, most notably when using derived identifiers.
|
||||
|
||||
On the SQL side there is not much overhead as no additional or unexpected queries have to be
|
||||
executed to manage entities with derived foreign keys.
|
||||
executed to manage entities with derived foreign keys.
|
||||
|
@ -1,12 +1,8 @@
|
||||
Extra Lazy Associations
|
||||
=======================
|
||||
|
||||
.. note::
|
||||
|
||||
This feature is scheduled for version 2.1 of Doctrine and not included in the 2.0.x series.
|
||||
|
||||
In many cases associations between entities can get pretty large. Even in a simple scenario like a blog.
|
||||
where posts can be commented, you always have to assume that a post draws hundrets of comments.
|
||||
where posts can be commented, you always have to assume that a post draws hundreds of comments.
|
||||
In Doctrine 2.0 if you accessed an association it would always get loaded completly into memory. This
|
||||
can lead to pretty serious performance problems, if your associations contain several hundrets or thousands
|
||||
of entities.
|
||||
@ -28,7 +24,7 @@ For each of this three methods the following semantics apply:
|
||||
Additionally even with Doctrine 2.0 the following methods do not trigger the collection load:
|
||||
|
||||
- ``Collection#add($entity)``
|
||||
- ``Collection#offsetSet($key, $entity)`` - ArrayAccess with no specific key ``$coll[] = $entity`, it does
|
||||
- ``Collection#offsetSet($key, $entity)`` - ArrayAccess with no specific key ``$coll[] = $entity``, it does
|
||||
not work when setting specific keys like ``$coll[0] = $entity``.
|
||||
|
||||
With extra lazy collections you can now not only add entities to large collections but also paginate them
|
||||
|
@ -182,8 +182,8 @@ or collections of all the relations that haven't been explicitly
|
||||
retrieved from the database yet.
|
||||
|
||||
To be able to use lazyload with collections, simple PHP arrays have
|
||||
to be replaced by a generic collection interface Doctrine which
|
||||
tries to act as array as much as possible using ArrayAccess,
|
||||
to be replaced by a generic collection interface for Doctrine which
|
||||
tries to act as as much like an array as possible by using ArrayAccess,
|
||||
IteratorAggregate and Countable interfaces. The class is the most
|
||||
simple implementation of this interface.
|
||||
|
||||
|
@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
sphinx-build en /var/www/docs
|
Loading…
x
Reference in New Issue
Block a user