From 3a80896173c8deb9cf82d1e916a678730b33573f Mon Sep 17 00:00:00 2001 From: Wil Moore III Date: Fri, 8 Apr 2011 10:44:55 -0700 Subject: [PATCH 01/17] Changed registerDoctrineTypeMapping('enum', 'varchar'); to registerDoctrineTypeMapping('enum', 'string'); as 'string' is the correct type to map to whereas varchar is only an alias. --- en/cookbook/mysql-enums.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/cookbook/mysql-enums.rst b/en/cookbook/mysql-enums.rst index 0e6e388df..b8cef7b21 100644 --- a/en/cookbook/mysql-enums.rst +++ b/en/cookbook/mysql-enums.rst @@ -34,7 +34,7 @@ will even detect this match correctly when using SchemaTool update commands. 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 From 8af162711e878c8387c0ad8019552c0bd89afe62 Mon Sep 17 00:00:00 2001 From: Michael Ridgway Date: Tue, 28 Jun 2011 16:34:12 -0400 Subject: [PATCH 02/17] Removed onUpdate property --- en/reference/annotations-reference.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/en/reference/annotations-reference.rst b/en/reference/annotations-reference.rst index e444ca7b9..932ec4539 100644 --- a/en/reference/annotations-reference.rst +++ b/en/reference/annotations-reference.rst @@ -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 From 175faeb5f22b6287ca253e52bcd0406fcc68fc1d Mon Sep 17 00:00:00 2001 From: Michael Ridgway Date: Mon, 11 Jul 2011 13:25:15 -0400 Subject: [PATCH 03/17] [DDC-1270] Fixed invalid expr()->*() function calls; Added isNull and isNotNull functions; Fixed casing on orX and andX --- en/reference/query-builder.rst | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/en/reference/query-builder.rst b/en/reference/query-builder.rst index 51adcb2b1..521f31d2a 100644 --- a/en/reference/query-builder.rst +++ b/en/reference/query-builder.rst @@ -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)); From f4074dbe1df079705c709a8073b37ed2cdba5c70 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 12 Jul 2011 22:58:24 +0200 Subject: [PATCH 04/17] Removed solved limitations and added missing ones --- en/reference/limitations-and-known-issues.rst | 107 +----------------- 1 file changed, 5 insertions(+), 102 deletions(-) diff --git a/en/reference/limitations-and-known-issues.rst b/en/reference/limitations-and-known-issues.rst index 64134251c..74d87c25d 100644 --- a/en/reference/limitations-and-known-issues.rst +++ b/en/reference/limitations-and-known-issues.rst @@ -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 - - `_. +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 `_ - 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 `_ -- `Blog: Working with large collections (Workaround) `_ -- `LargeCollections Helper `_ - Persist Keys of Collections ~~~~~~~~~~~~~~~~~~~~~~~~~~~ From c876f9edb2030efc756a13fe649bf9a02b25f207 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 12 Jul 2011 23:01:42 +0200 Subject: [PATCH 05/17] Add warning to Association Mapping chapter that referencedColumnName have to primary key columns --- en/reference/association-mapping.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/en/reference/association-mapping.rst b/en/reference/association-mapping.rst index bd46217a1..1b42dcc3f 100644 --- a/en/reference/association-mapping.rst +++ b/en/reference/association-mapping.rst @@ -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 From 660ead4b0e6879f73b91caae077661bd7aa5574e Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 13 Jul 2011 19:04:21 +0200 Subject: [PATCH 06/17] FAQ and Composite Key renaming --- en/reference/faq.rst | 12 ++++++------ en/tutorials/composite-primary-keys.rst | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/en/reference/faq.rst b/en/reference/faq.rst index 019ab1bdd..4e0ae7537 100644 --- a/en/reference/faq.rst +++ b/en/reference/faq.rst @@ -172,15 +172,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? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/en/tutorials/composite-primary-keys.rst b/en/tutorials/composite-primary-keys.rst index 7c7fa9baa..83ccd73ac 100644 --- a/en/tutorials/composite-primary-keys.rst +++ b/en/tutorials/composite-primary-keys.rst @@ -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. From 91b2c82c58bae2051df4e8c8b227c033df525143 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 13 Jul 2011 20:31:01 +0200 Subject: [PATCH 07/17] Brought most of the documentation up to date on 2.1 --- en/reference/annotations-reference.rst | 10 +- en/reference/dql-doctrine-query-language.rst | 26 +++++ en/reference/faq.rst | 25 +++- en/reference/improving-performance.rst | 13 +++ en/reference/native-sql.rst | 25 +++- en/reference/working-with-objects.rst | 20 +++- en/reference/xml-mapping.rst | 32 +++++- en/tutorials/composite-primary-keys.rst | 115 ++++++++++++------- en/tutorials/extra-lazy-associations.rst | 4 - 9 files changed, 213 insertions(+), 57 deletions(-) diff --git a/en/reference/annotations-reference.rst b/en/reference/annotations-reference.rst index e444ca7b9..3f739800d 100644 --- a/en/reference/annotations-reference.rst +++ b/en/reference/annotations-reference.rst @@ -528,14 +528,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 +637,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: diff --git a/en/reference/dql-doctrine-query-language.rst b/en/reference/dql-doctrine-query-language.rst index de26eda9a..bd32720d1 100644 --- a/en/reference/dql-doctrine-query-language.rst +++ b/en/reference/dql-doctrine-query-language.rst @@ -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 + + 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 ---- diff --git a/en/reference/faq.rst b/en/reference/faq.rst index 4e0ae7537..91d39a291 100644 --- a/en/reference/faq.rst +++ b/en/reference/faq.rst @@ -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 -------------- @@ -23,7 +34,7 @@ Doctrine does not support to set the default values in columns through the "DEFA This is not necessary however, you can just use your class properties as default values. These are then used upon insert: -.. code-block:: +.. code-block:: php class User { @@ -47,7 +58,7 @@ or adding entities to a collection twice. You have to check for both conditions in the code before calling ``$em->flush()`` if you know that unique constraint failures can occur. -In `Symfony 2`_ for example there is a Unique Entity Validator +In `Symfony 2 `_ for example there is a Unique Entity Validator to achieve this task. For collections you can check with ``$collection->contains($entity)`` if an entity is already @@ -128,7 +139,7 @@ Can I use Inheritance with Doctrine 2? Yes, you can use Single- or Joined-Table Inheritance in Doctrine 2. -See the documentation chapter on :doc:`inheritance mapping `_ for +See the documentation chapter on :doc:`inheritance mapping ` for the details. Why does Doctrine not create proxy objects for my inheritance hierachy? @@ -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 ----------- diff --git a/en/reference/improving-performance.rst b/en/reference/improving-performance.rst index c5b99a189..9bc4c8632 100644 --- a/en/reference/improving-performance.rst +++ b/en/reference/improving-performance.rst @@ -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 ` + + Apply Best Practices -------------------- diff --git a/en/reference/native-sql.rst b/en/reference/native-sql.rst index dfec36ff8..01b837d4a 100644 --- a/en/reference/native-sql.rst +++ b/en/reference/native-sql.rst @@ -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 + + 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. diff --git a/en/reference/working-with-objects.rst b/en/reference/working-with-objects.rst index a47b13ef4..c235828ae 100644 --- a/en/reference/working-with-objects.rst +++ b/en/reference/working-with-objects.rst @@ -692,10 +692,28 @@ methods on a repository as follows: You can also load by owning side associations through the repository: +.. code-block:: php + + 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 + + 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 + + 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 diff --git a/en/reference/xml-mapping.rst b/en/reference/xml-mapping.rst index ef351da2d..758ff5b7c 100644 --- a/en/reference/xml-mapping.rst +++ b/en/reference/xml-mapping.rst @@ -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 ```` 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 ```` tag. + +.. code-block:: xml + + + + + + + + + + + + + diff --git a/en/tutorials/composite-primary-keys.rst b/en/tutorials/composite-primary-keys.rst index 83ccd73ac..a091f573b 100644 --- a/en/tutorials/composite-primary-keys.rst +++ b/en/tutorials/composite-primary-keys.rst @@ -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:: - 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 + + + + + + + + + + + + + + + .. 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. \ No newline at end of file +executed to manage entities with derived foreign keys. diff --git a/en/tutorials/extra-lazy-associations.rst b/en/tutorials/extra-lazy-associations.rst index b854f72c1..ae58eb23c 100644 --- a/en/tutorials/extra-lazy-associations.rst +++ b/en/tutorials/extra-lazy-associations.rst @@ -1,10 +1,6 @@ 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. In Doctrine 2.0 if you accessed an association it would always get loaded completly into memory. This From d8125768a32780dcf5bdaa4c0b5b53e8500d0127 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 13 Jul 2011 21:19:09 +0200 Subject: [PATCH 08/17] Copy new build process for docs into orm docs from DBAL --- .gitignore | 2 ++ README.md | 8 ++++++++ bin/generate-docs.sh | 10 ++++++++++ bin/install-dependencies.sh | 4 ++++ convert.sh | 14 -------------- en/conf.py | 8 ++++---- generate-docs.sh | 2 -- 7 files changed, 28 insertions(+), 20 deletions(-) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 bin/generate-docs.sh create mode 100644 bin/install-dependencies.sh delete mode 100755 convert.sh delete mode 100755 generate-docs.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..75ec69b23 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +en/_exts/configurationblock.pyc +build \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..4315116f8 --- /dev/null +++ b/README.md @@ -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. \ No newline at end of file diff --git a/bin/generate-docs.sh b/bin/generate-docs.sh new file mode 100755 index 000000000..7d06d2a8d --- /dev/null +++ b/bin/generate-docs.sh @@ -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 \ No newline at end of file diff --git a/bin/install-dependencies.sh b/bin/install-dependencies.sh new file mode 100644 index 000000000..86b3bdff7 --- /dev/null +++ b/bin/install-dependencies.sh @@ -0,0 +1,4 @@ +#!/bin/bash +sudo apt-get install python25 python25-dev texlive-full rubber +sudo easy_install pygments +sudo easy_install sphinx \ No newline at end of file diff --git a/convert.sh b/convert.sh deleted file mode 100755 index 27e4a5b85..000000000 --- a/convert.sh +++ /dev/null @@ -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\])/ ${FILE%.txt}.rst -done \ No newline at end of file diff --git a/en/conf.py b/en/conf.py index a0192a66a..09d8c262f 100644 --- a/en/conf.py +++ b/en/conf.py @@ -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: diff --git a/generate-docs.sh b/generate-docs.sh deleted file mode 100755 index 33e811f6c..000000000 --- a/generate-docs.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -sphinx-build en /var/www/docs From ecb13a87dcbab294289be7663f35f6522c481f91 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Wed, 13 Jul 2011 21:31:31 +0200 Subject: [PATCH 09/17] Bugfix --- en/tutorials/extra-lazy-associations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/tutorials/extra-lazy-associations.rst b/en/tutorials/extra-lazy-associations.rst index ae58eb23c..b0b90947a 100644 --- a/en/tutorials/extra-lazy-associations.rst +++ b/en/tutorials/extra-lazy-associations.rst @@ -24,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 From bfe5bea68dae875ae4053a579556c03d97c9ea5e Mon Sep 17 00:00:00 2001 From: Dan Patrick Date: Sat, 30 Jul 2011 17:15:44 -0500 Subject: [PATCH 10/17] Fixed grammatical errors in working-with-associations.rst --- en/reference/working-with-associations.rst | 28 ++++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/en/reference/working-with-associations.rst b/en/reference/working-with-associations.rst index f426ecf03..bec659035 100644 --- a/en/reference/working-with-associations.rst +++ b/en/reference/working-with-associations.rst @@ -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. \ No newline at end of file +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. From 6cd7d21db1e1fc4a6c09398e3271f71f09d18415 Mon Sep 17 00:00:00 2001 From: Dan Patrick Date: Mon, 1 Aug 2011 18:04:14 -0500 Subject: [PATCH 11/17] Small grammar changes in section explaining lazyload/ArrayCollection. --- en/tutorials/getting-started-xml-edition.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/en/tutorials/getting-started-xml-edition.rst b/en/tutorials/getting-started-xml-edition.rst index 24ea4ab67..4678834d9 100644 --- a/en/tutorials/getting-started-xml-edition.rst +++ b/en/tutorials/getting-started-xml-edition.rst @@ -126,8 +126,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. From 1de9b906fd745e32204643d1c58141bc79c024d0 Mon Sep 17 00:00:00 2001 From: Christian Raue Date: Fri, 5 Aug 2011 02:10:40 +0300 Subject: [PATCH 12/17] completed the sentence in section "postUpdate, postRemove, postPersist" --- en/reference/events.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/en/reference/events.rst b/en/reference/events.rst index c8b116cc9..0b20ba4a9 100644 --- a/en/reference/events.rst +++ b/en/reference/events.rst @@ -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 ~~~~~~~~ From 5ee8861350856c86d90af5dcf11a798a3d2594eb Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sat, 6 Aug 2011 19:12:42 +0200 Subject: [PATCH 13/17] Add note about comparing datetime and object by reference --- en/reference/basic-mapping.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/en/reference/basic-mapping.rst b/en/reference/basic-mapping.rst index 12cf47c7b..e29760a4a 100644 --- a/en/reference/basic-mapping.rst +++ b/en/reference/basic-mapping.rst @@ -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:: From 189c729f15d2fafecf92662cad9553c2ec3dccd7 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Mon, 8 Aug 2011 02:08:19 -0300 Subject: [PATCH 14/17] Added support to CaseExpression. --- en/reference/dql-doctrine-query-language.rst | 21 +++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/en/reference/dql-doctrine-query-language.rst b/en/reference/dql-doctrine-query-language.rst index bd32720d1..955f7ee1c 100644 --- a/en/reference/dql-doctrine-query-language.rst +++ b/en/reference/dql-doctrine-query-language.rst @@ -1469,6 +1469,7 @@ Conditional Expressions InExpression | NullComparisonExpression | ExistsExpression | EmptyCollectionComparisonExpression | CollectionMemberExpression + Collection Expressions ~~~~~~~~~~~~~~~~~~~~~~ @@ -1505,7 +1506,7 @@ Arithmetic Expressions ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary ArithmeticPrimary ::= SingleValuedPathExpression | Literal | "(" SimpleArithmeticExpression ")" | FunctionsReturningNumerics | AggregateExpression | FunctionsReturningStrings - | FunctionsReturningDatetime | IdentificationVariable | InputParameter + | FunctionsReturningDatetime | IdentificationVariable | InputParameter | CaseExpression Scalar and Type Expressions ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1513,9 +1514,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 @@ -1535,6 +1536,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 ~~~~~~~~~~~~~~~~~ From 31f34e95cc018d57b66e9bd769faef9e80e28b3b Mon Sep 17 00:00:00 2001 From: Hugo Hamon Date: Sun, 14 Aug 2011 18:21:31 +0200 Subject: [PATCH 15/17] [QueryBuilder] added missing $ --- en/reference/query-builder.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/reference/query-builder.rst b/en/reference/query-builder.rst index 521f31d2a..dc156bbe2 100644 --- a/en/reference/query-builder.rst +++ b/en/reference/query-builder.rst @@ -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)); From a5ac76b1925bbcdb90d73b28069ab3518d6cecde Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Mon, 15 Aug 2011 01:57:02 -0300 Subject: [PATCH 16/17] Added new support to DQL: ORDER BY now supports SingleValuedPathExpression and INSTANCE OF now supports multi-value checking. --- en/reference/dql-doctrine-query-language.rst | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/en/reference/dql-doctrine-query-language.rst b/en/reference/dql-doctrine-query-language.rst index 955f7ee1c..3cfce0204 100644 --- a/en/reference/dql-doctrine-query-language.rst +++ b/en/reference/dql-doctrine-query-language.rst @@ -1428,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" @@ -1450,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 ~~~~~~~~~~~~~~~~~~~~~~~ @@ -1467,7 +1467,8 @@ Conditional Expressions ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")" SimpleConditionalExpression ::= ComparisonExpression | BetweenExpression | LikeExpression | InExpression | NullComparisonExpression | ExistsExpression | - EmptyCollectionComparisonExpression | CollectionMemberExpression + EmptyCollectionComparisonExpression | CollectionMemberExpression | + InstanceOfExpression Collection Expressions @@ -1561,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 ")" From ffa2a545fa0d34c135629fe1048c26001a139a7d Mon Sep 17 00:00:00 2001 From: Joseph Rouff Date: Thu, 25 Aug 2011 01:36:21 +0300 Subject: [PATCH 17/17] Fix typo --- en/tutorials/extra-lazy-associations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/tutorials/extra-lazy-associations.rst b/en/tutorials/extra-lazy-associations.rst index b0b90947a..d6ed40d3d 100644 --- a/en/tutorials/extra-lazy-associations.rst +++ b/en/tutorials/extra-lazy-associations.rst @@ -2,7 +2,7 @@ Extra Lazy Associations ======================= 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.