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/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 diff --git a/en/reference/annotations-reference.rst b/en/reference/annotations-reference.rst index e444ca7b9..f82704778 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 @@ -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: 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 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:: diff --git a/en/reference/dql-doctrine-query-language.rst b/en/reference/dql-doctrine-query-language.rst index de26eda9a..3cfce0204 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 ---- @@ -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 ")" 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 ~~~~~~~~ diff --git a/en/reference/faq.rst b/en/reference/faq.rst index 11259d6c7..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 -------------- @@ -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? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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/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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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/query-builder.rst b/en/reference/query-builder.rst index 51adcb2b1..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)); @@ -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)); 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. 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 7c7fa9baa..a091f573b 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. @@ -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..d6ed40d3d 100644 --- a/en/tutorials/extra-lazy-associations.rst +++ b/en/tutorials/extra-lazy-associations.rst @@ -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 diff --git a/en/tutorials/getting-started-xml-edition.rst b/en/tutorials/getting-started-xml-edition.rst index ef509d710..e3920a5f0 100644 --- a/en/tutorials/getting-started-xml-edition.rst +++ b/en/tutorials/getting-started-xml-edition.rst @@ -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. 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