diff --git a/README.markdown b/README.markdown index b6b60ac16..bf337b12f 100644 --- a/README.markdown +++ b/README.markdown @@ -1,6 +1,7 @@ # Doctrine 2 ORM Master: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=master)](http://travis-ci.org/doctrine/doctrine2) +2.4: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.4)](http://travis-ci.org/doctrine/doctrine2) 2.3: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.3)](http://travis-ci.org/doctrine/doctrine2) 2.2: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.2)](http://travis-ci.org/doctrine/doctrine2) 2.1: [![Build Status](https://secure.travis-ci.org/doctrine/doctrine2.png?branch=2.1.x)](http://travis-ci.org/doctrine/doctrine2) diff --git a/docs/en/index.rst b/docs/en/index.rst index 58753eb25..eb995278e 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -67,7 +67,7 @@ Advanced Topics * :doc:`Architecture ` * :doc:`Advanced Configuration ` -* :doc:`Limitations and knowns issues ` +* :doc:`Limitations and known issues ` * :doc:`Commandline Tools ` * :doc:`Transactions and Concurrency ` * :doc:`Filters ` @@ -78,6 +78,7 @@ Advanced Topics * :doc:`Change Tracking Policies ` * :doc:`Best Practices ` * :doc:`Metadata Drivers ` +* :doc:`Batch Processing ` Tutorials --------- diff --git a/docs/en/reference/annotations-reference.rst b/docs/en/reference/annotations-reference.rst index 80ed01e7a..2ebc5c1f8 100644 --- a/docs/en/reference/annotations-reference.rst +++ b/docs/en/reference/annotations-reference.rst @@ -519,7 +519,7 @@ details of the database join table. If you do not specify @JoinTable on these relations reasonable mapping defaults apply using the affected table and the column names. -Required attributes: +Optional attributes: - **name**: Database name of the join-table diff --git a/docs/en/reference/transactions-and-concurrency.rst b/docs/en/reference/transactions-and-concurrency.rst index 1b06156e9..4dc18318e 100644 --- a/docs/en/reference/transactions-and-concurrency.rst +++ b/docs/en/reference/transactions-and-concurrency.rst @@ -70,7 +70,6 @@ looks like this: $em->getConnection()->commit(); } catch (Exception $e) { $em->getConnection()->rollback(); - $em->close(); throw $e; } @@ -81,14 +80,12 @@ require an active transaction. Such methods will throw a ``TransactionRequiredException`` to inform you of that requirement. -A more convenient alternative for explicit transaction demarcation -is the use of provided control abstractions in the form of -``Connection#transactional($func)`` and -``EntityManager#transactional($func)``. When used, these control -abstractions ensure that you never forget to rollback the -transaction or close the ``EntityManager``, apart from the obvious -code reduction. An example that is functionally equivalent to the -previously shown code looks as follows: +A more convenient alternative for explicit transaction demarcation is the use +of provided control abstractions in the form of +``Connection#transactional($func)`` and ``EntityManager#transactional($func)``. +When used, these control abstractions ensure that you never forget to rollback +the transaction, in addition to the obvious code reduction. An example that is +functionally equivalent to the previously shown code looks as follows: .. code-block:: php @@ -104,8 +101,8 @@ previously shown code looks as follows: The difference between ``Connection#transactional($func)`` and ``EntityManager#transactional($func)`` is that the latter abstraction flushes the ``EntityManager`` prior to transaction -commit and also closes the ``EntityManager`` properly when an -exception occurs (in addition to rolling back the transaction). +commit and rolls back the transaction when an +exception occurs. Exception Handling ~~~~~~~~~~~~~~~~~~ diff --git a/docs/en/reference/unitofwork.rst b/docs/en/reference/unitofwork.rst index f01c3f91d..cdfb6e0be 100644 --- a/docs/en/reference/unitofwork.rst +++ b/docs/en/reference/unitofwork.rst @@ -157,7 +157,7 @@ wishes to be hydrated. Default result-types include: - SQL to a single result variable Hydration to entities and arrays is one of most complex parts of Doctrine -algorithm-wise. It can built results with for example: +algorithm-wise. It can build results with for example: - Single table selects - Joins with n:1 or 1:n cardinality, grouping belonging to the same parent. diff --git a/docs/en/reference/yaml-mapping.rst b/docs/en/reference/yaml-mapping.rst index 1d7f8b17a..af2d404a5 100644 --- a/docs/en/reference/yaml-mapping.rst +++ b/docs/en/reference/yaml-mapping.rst @@ -72,6 +72,7 @@ of several common elements: # Doctrine.Tests.ORM.Mapping.User.dcm.yml Doctrine\Tests\ORM\Mapping\User: type: entity + repositoryClass: Doctrine\Tests\ORM\Mapping\UserRepository table: cms_users indexes: name_index: diff --git a/docs/en/tutorials/getting-started.rst b/docs/en/tutorials/getting-started.rst index 817c56270..dc65b433c 100644 --- a/docs/en/tutorials/getting-started.rst +++ b/docs/en/tutorials/getting-started.rst @@ -381,7 +381,7 @@ better than in a scenario where updates are done for each entity in isolation. Doctrine follows the UnitOfWork pattern which additionally detects all entities that were fetched and have changed during the request. You don't have to keep track of -entities yourself, when Doctrine already knowns about them. +entities yourself, when Doctrine already knows about them. As a next step we want to fetch a list of all the products. Let's create a new script for this: diff --git a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php index 5688d8652..c336e7a5d 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -190,17 +190,14 @@ class ArrayHydrator extends AbstractHydrator ( ! isset($baseElement[$relationAlias])) ) { $baseElement[$relationAlias] = null; - } else if ( - ( ! isset($baseElement[$relationAlias])) || - ( ! isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]])) - ) { + } else if ( ! isset($baseElement[$relationAlias])) { $baseElement[$relationAlias] = $data; } } $coll =& $baseElement[$relationAlias]; - if ($coll !== null) { + if (is_array($coll)) { $this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne); } } else { diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index fafc497c0..859bd0b00 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -477,7 +477,7 @@ class ObjectHydrator extends AbstractHydrator $targetClass = $this->ce[$relation['targetEntity']]; if ($relation['isOwningSide']) { - //TODO: Just check hints['fetched'] here? + // TODO: Just check hints['fetched'] here? // If there is an inverse mapping on the target class its bidirectional if ($relation['inversedBy']) { $inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']]; diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index c594f3bbf..2540425a2 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -328,7 +328,7 @@ class BasicEntityPersister $identifier = $this->quoteStrategy->getIdentifierColumnNames($versionedClass, $this->platform); $columnName = $this->quoteStrategy->getColumnName($versionField, $versionedClass, $this->platform); - //FIXME: Order with composite keys might not be correct + // FIXME: Order with composite keys might not be correct $sql = 'SELECT ' . $columnName . ' FROM ' . $tableName . ' WHERE ' . implode(' = ? AND ', $identifier) . ' = ?'; @@ -1976,4 +1976,4 @@ class BasicEntityPersister $sql = implode(' AND ', $filterClauses); return $sql ? "(" . $sql . ")" : ""; // Wrap again to avoid "X or Y and FilterConditionSQL" } -} \ No newline at end of file +} diff --git a/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php index 58f0dde13..242a38bcf 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/IdentityFunction.php @@ -82,7 +82,7 @@ class IdentityFunction extends FunctionNode } } - //The table with the relation may be a subclass, so get the table name from the association definition + // The table with the relation may be a subclass, so get the table name from the association definition $tableName = $sqlWalker->getEntityManager()->getClassMetadata($assoc['sourceEntity'])->getTableName(); $tableAlias = $sqlWalker->getSQLTableAlias($tableName, $dqlAlias); diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 5c721a1d5..8ee9fed92 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1878,7 +1878,7 @@ class Parser case ($lookahead === Lexer::T_OPEN_PARENTHESIS): return $this->SimpleArithmeticExpression(); - //this check must be done before checking for a filed path expression + // this check must be done before checking for a filed path expression case ($this->isFunction()): $this->lexer->peek(); // "(" @@ -1896,7 +1896,7 @@ class Parser } break; - //it is no function, so it must be a field path + // it is no function, so it must be a field path case ($lookahead === Lexer::T_IDENTIFIER): $this->lexer->peek(); // lookahead => '.' $this->lexer->peek(); // lookahead => token after '.' @@ -2431,7 +2431,7 @@ class Parser switch ($peek['value']) { case '(': - //Peeks beyond the matched closing parenthesis. + // Peeks beyond the matched closing parenthesis. $token = $this->peekBeyondClosingParenthesis(false); if ($token['type'] === Lexer::T_NOT) { diff --git a/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php b/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php index d40d0786e..a0216bf91 100644 --- a/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php +++ b/lib/Doctrine/ORM/Tools/Export/Driver/AbstractExporter.php @@ -134,7 +134,7 @@ abstract class AbstractExporter } foreach ($this->_metadata as $metadata) { - //In case output is returned, write it to a file, skip otherwise + // In case output is returned, write it to a file, skip otherwise if($output = $this->exportClassMetadata($metadata)){ $path = $this->_generateOutputPath($metadata); $dir = dirname($path); diff --git a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php index e211bb4e0..7ab6f0e65 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -166,7 +166,7 @@ class LimitSubqueryOutputWalker extends SqlWalker if ($this->platform instanceof PostgreSqlPlatform || $this->platform instanceof OraclePlatform) { - //http://www.doctrine-project.org/jira/browse/DDC-1958 + // http://www.doctrine-project.org/jira/browse/DDC-1958 $this->preserveSqlOrdering($AST, $sqlIdentifier, $innerSql, $sql); } @@ -215,7 +215,7 @@ class LimitSubqueryOutputWalker extends SqlWalker } } } - //remove identifier aliases + // remove identifier aliases $sqlOrderColumns = array_diff($sqlOrderColumns, $sqlIdentifier); } diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 9818ca6a8..832d9de3d 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -2406,7 +2406,7 @@ class UnitOfWork implements PropertyChangedListener { $coid = spl_object_hash($coll); - //TODO: if $coll is already scheduled for recreation ... what to do? + // TODO: if $coll is already scheduled for recreation ... what to do? // Just remove $coll from the scheduled recreations? if (isset($this->collectionUpdates[$coid])) { unset($this->collectionUpdates[$coid]); diff --git a/tests/Doctrine/Tests/Mocks/DriverMock.php b/tests/Doctrine/Tests/Mocks/DriverMock.php index af9f869ce..cc33c4c34 100644 --- a/tests/Doctrine/Tests/Mocks/DriverMock.php +++ b/tests/Doctrine/Tests/Mocks/DriverMock.php @@ -85,4 +85,9 @@ class DriverMock implements \Doctrine\DBAL\Driver { return; } + + public function convertExceptionCode(\Exception $exception) + { + return 0; + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2759Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2759Test.php new file mode 100644 index 000000000..a071d4c0c --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2759Test.php @@ -0,0 +1,121 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759Qualification'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759Category'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759QualificationMetadata'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2759MetadataCategory'), + )); + } catch(\Exception $e) { + return; + } + + $qualification = new DDC2759Qualification(); + $qualificationMetadata = new DDC2759QualificationMetadata($qualification); + + $category1 = new DDC2759Category(); + $category2 = new DDC2759Category(); + + $metadataCategory1 = new DDC2759MetadataCategory($qualificationMetadata, $category1); + $metadataCategory2 = new DDC2759MetadataCategory($qualificationMetadata, $category2); + + $this->_em->persist($qualification); + $this->_em->persist($qualificationMetadata); + + $this->_em->persist($category1); + $this->_em->persist($category2); + + $this->_em->persist($metadataCategory1); + $this->_em->persist($metadataCategory2); + + $this->_em->flush(); + $this->_em->clear(); + } + + public function testCorrectNumberOfAssociationsIsReturned() + { + $repository = $this->_em->getRepository(__NAMESPACE__ . '\DDC2759Qualification'); + + $builder = $repository->createQueryBuilder('q') + ->select('q, qm, qmc') + ->innerJoin('q.metadata', 'qm') + ->innerJoin('qm.metadataCategories', 'qmc'); + + $result = $builder->getQuery() + ->getArrayResult(); + + $this->assertCount(2, $result[0]['metadata']['metadataCategories']); + } +} + +/** @Entity @Table(name="ddc_2759_qualification") */ +class DDC2759Qualification +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + + /** @OneToOne(targetEntity="DDC2759QualificationMetadata", mappedBy="content") */ + public $metadata; +} + +/** @Entity @Table(name="ddc_2759_category") */ +class DDC2759Category +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + + /** @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="category") */ + public $metadataCategories; +} + +/** @Entity @Table(name="ddc_2759_qualification_metadata") */ +class DDC2759QualificationMetadata +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + + /** @OneToOne(targetEntity="DDC2759Qualification", inversedBy="metadata") */ + public $content; + + /** @OneToMany(targetEntity="DDC2759MetadataCategory", mappedBy="metadata") */ + protected $metadataCategories; + + public function __construct(DDC2759Qualification $content) + { + $this->content = $content; + } +} + +/** @Entity @Table(name="ddc_2759_metadata_category") */ +class DDC2759MetadataCategory +{ + /** @Id @Column(type="integer") @GeneratedValue */ + public $id; + + /** @ManyToOne(targetEntity="DDC2759QualificationMetadata", inversedBy="metadataCategories") */ + public $metadata; + + /** @ManyToOne(targetEntity="DDC2759Category", inversedBy="metadataCategories") */ + public $category; + + public function __construct(DDC2759QualificationMetadata $metadata, DDC2759Category $category) + { + $this->metadata = $metadata; + $this->category = $category; + } +}