1
0
mirror of synced 2025-03-03 11:23:21 +03:00

Merge branch 'master' of github.com:doctrine/orm-documentation

This commit is contained in:
Benjamin Eberlei 2011-08-27 12:36:56 +02:00
commit e6e1243852
24 changed files with 333 additions and 229 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
en/_exts/configurationblock.pyc
build

8
README.md Normal file
View 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
View 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

View 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

View File

@ -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

View File

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

View File

@ -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

View File

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

View File

@ -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

View File

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

View File

@ -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 ")"

View File

@ -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
~~~~~~~~

View File

@ -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?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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
--------------------

View File

@ -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
~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

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

View File

@ -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));

View File

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

View File

@ -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

View File

@ -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>

View File

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

View File

@ -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

View File

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

View File

@ -1,2 +0,0 @@
#!/bin/bash
sphinx-build en /var/www/docs