diff --git a/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php index dbcc8c813..a095c2a7d 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php @@ -36,7 +36,11 @@ class SingleScalarHydrator extends AbstractHydrator { $cache = array(); $result = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC); - if (count($result) > 1 || count($result[key($result)]) > 1) { + $num = count($result); + + if ($num == 0) { + throw new \Doctrine\ORM\NoResultException; + } else if ($num > 1 || count($result[key($result)]) > 1) { throw new \Doctrine\ORM\NonUniqueResultException; } $result = $this->_gatherScalarRowData($result[key($result)], $cache); diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 7ae8857fa..6ac796adc 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -243,10 +243,10 @@ class ClassMetadata extends ClassMetadataInfo // Store ReflectionProperty of mapped field $sourceFieldName = $assocMapping->sourceFieldName; - - $refProp = $this->reflClass->getProperty($sourceFieldName); - $refProp->setAccessible(true); - $this->reflFields[$sourceFieldName] = $refProp; + + $refProp = $this->reflClass->getProperty($sourceFieldName); + $refProp->setAccessible(true); + $this->reflFields[$sourceFieldName] = $refProp; } /** diff --git a/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php index 29dedf3e1..637971cb5 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AbstractFileDriver.php @@ -155,7 +155,7 @@ abstract class AbstractFileDriver implements Driver if ($this->_paths) { foreach ((array) $this->_paths as $path) { if ( ! is_dir($path)) { - throw MappingException::driverRequiresConfiguredDirectoryPath(); + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath(); } $iterator = new \RecursiveIteratorIterator( diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 49d13d38d..15296395b 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -465,6 +465,11 @@ class SqlWalker implements TreeWalker $fieldName = array_pop($parts); $dqlAlias = $pathExpr->identificationVariable; $class = $this->_queryComponents[$dqlAlias]['metadata']; + + if (isset($class->inheritedAssociationFields[$fieldName])) { + $class = $this->_em->getClassMetadata($class->inheritedAssociationFields[$fieldName]); + } + $assoc = $class->associationMappings[$fieldName]; if ($assoc->isOwningSide) { @@ -472,8 +477,8 @@ class SqlWalker implements TreeWalker if (count($assoc->sourceToTargetKeyColumns) > 1) { throw QueryException::associationPathCompositeKeyNotSupported(); } - $sql .= $this->walkIdentificationVariable($dqlAlias) . '.' - . reset($assoc->targetToSourceKeyColumns); + $sql .= $this->getSqlTableAlias($class->table['name'], $dqlAlias) . '.' + . reset($assoc->targetToSourceKeyColumns); } else { // 2- Inverse side: NOT (YET?) SUPPORTED throw QueryException::associationPathInverseSideNotSupported(); @@ -683,23 +688,15 @@ class SqlWalker implements TreeWalker $joinAssocPathExpr = $join->joinAssociationPathExpression; $joinedDqlAlias = $join->aliasIdentificationVariable; - $targetQComp = $this->_queryComponents[$joinedDqlAlias]; - $targetClass = $targetQComp['metadata']; - $relation = $targetQComp['relation']; - $sourceClass = $this->_queryComponents[$joinAssocPathExpr->identificationVariable]['metadata']; - + $relation = $this->_queryComponents[$joinedDqlAlias]['relation']; + $targetClass = $this->_em->getClassMetadata($relation->targetEntityName); + $sourceClass = $this->_em->getClassMetadata($relation->sourceEntityName); $targetTableName = $targetClass->getQuotedTableName($this->_platform); - $targetTableAlias = $this->getSqlTableAlias($targetClass->getTableName(), $joinedDqlAlias); - $sourceTableAlias = $this->getSqlTableAlias( - $sourceClass->getTableName(), $joinAssocPathExpr->identificationVariable - ); + $targetTableAlias = $this->getSqlTableAlias($targetClass->table['name'], $joinedDqlAlias); + $sourceTableAlias = $this->getSqlTableAlias($sourceClass->table['name'], $joinAssocPathExpr->identificationVariable); // Ensure we got the owning side, since it has all mapping info - if ( ! $relation->isOwningSide) { - $assoc = $targetClass->associationMappings[$relation->mappedBy]; - } else { - $assoc = $relation; - } + $assoc = ( ! $relation->isOwningSide) ? $targetClass->associationMappings[$relation->mappedBy] : $relation; if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true) { if ($relation->isOneToMany() || $relation->isManyToMany()) { @@ -713,7 +710,7 @@ class SqlWalker implements TreeWalker foreach ($assoc->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) { if ( ! $first) $sql .= ' AND '; else $first = false; - + if ($relation->isOwningSide) { $quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform); $sql .= $sourceTableAlias . '.' . $sourceColumn diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php index 947cac9fb..9bd82c36c 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ClearCache/ResultCommand.php @@ -95,9 +95,9 @@ EOT $deleted = $cacheDriver->delete($id); if (is_array($deleted)) { - $this->_printDeleted($deleted); + $this->_printDeleted($output, $deleted); } else if (is_bool($deleted) && $deleted) { - $this->_printDeleted(array($id)); + $this->_printDeleted($output, array($id)); } $outputed = true; @@ -105,12 +105,12 @@ EOT } // Removing based on --regex - if (($regex = $input->getOption('regex')) !== null && $regexps) { + if (($regexps = $input->getOption('regex')) !== null && $regexps) { foreach($regexps as $regex) { $output->write($outputed ? PHP_EOL : ''); $output->write(sprintf('Clearing Result cache entries that match the regular expression "%s"', $regex) . PHP_EOL); - $this->_printDeleted($cacheDriver->deleteByRegex('/' . $regex. '/')); + $this->_printDeleted($output, $cacheDriver->deleteByRegex('/' . $regex. '/')); $outputed = true; } @@ -122,7 +122,7 @@ EOT $output->write($outputed ? PHP_EOL : ''); $output->write(sprintf('Clearing Result cache entries that have the prefix "%s"', $prefix) . PHP_EOL); - $this->_printDeleted($cacheDriver->deleteByPrefix($prefix)); + $this->_printDeleted($output, $cacheDriver->deleteByPrefix($prefix)); $outputed = true; } @@ -134,7 +134,7 @@ EOT $output->write($outputed ? PHP_EOL : ''); $output->write(sprintf('Clearing Result cache entries that have the suffix "%s"', $suffix) . PHP_EOL); - $this->_printDeleted($cacheDriver->deleteBySuffix($suffix)); + $this->_printDeleted($output, $cacheDriver->deleteBySuffix($suffix)); $outputed = true; } @@ -143,9 +143,9 @@ EOT // Removing ALL entries if ( ! $ids && ! $regexps && ! $prefixes && ! $suffixes) { $output->write($outputed ? PHP_EOL : ''); - $output->write('Clearing ALL Result cache entries'); + $output->write('Clearing ALL Result cache entries' . PHP_EOL); - $this->_printDeleted($cacheDriver->deleteAll()); + $this->_printDeleted($output, $cacheDriver->deleteAll()); $outputed = true; } diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php index 48e6f3eb2..89a6a55d5 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -61,8 +61,8 @@ class ConvertMappingCommand extends Console\Command\Command 'dest-path', InputArgument::REQUIRED, 'The path to generate your entities classes.' ), - new InputArgument( - 'from-database', InputArgument::OPTIONAL, 'The path of mapping information.' + new InputOption( + 'from-database', null, null, 'Whether or not to convert mapping information from existing database.' ), new InputOption( 'extend', null, InputOption::PARAMETER_OPTIONAL, @@ -86,10 +86,7 @@ EOT { $em = $this->getHelper('em')->getEntityManager(); - $metadatas = $em->getMetadataFactory()->getAllMetadata(); - $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); - - if ($input->getArgument('from-database') === true) { + if ($input->getOption('from-database') === true) { $em->getConfiguration()->setMetadataDriverImpl( new \Doctrine\ORM\Mapping\Driver\DatabaseDriver( $em->getConnection()->getSchemaManager() @@ -97,8 +94,14 @@ EOT ); } + $metadatas = $em->getMetadataFactory()->getAllMetadata(); + $metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter')); + // Process destination directory - $destPath = realpath($input->getArgument('dest-path')); + if ( ! is_dir($destPath = $input->getArgument('dest-path'))) { + mkdir($destPath, 0777, true); + } + $destPath = realpath($destPath); if ( ! file_exists($destPath)) { throw new \InvalidArgumentException( diff --git a/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php index 44cdd5688..7c33174f5 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/EnsureProductionSettingsCommand.php @@ -65,12 +65,21 @@ EOT protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output) { $em = $this->getHelper('em')->getEntityManager(); - $em->getConfiguration()->ensureProductionSettings(); - if ($input->getOption('complete') !== null) { - $em->getConnection()->connect(); + $error = false; + try { + $em->getConfiguration()->ensureProductionSettings(); + + if ($input->getOption('complete') !== null) { + $em->getConnection()->connect(); + } + } catch (\Exception $e) { + $error = true; + $output->writeln('' . $e->getMessage() . ''); } - $output->write('Environment is correctly configured for production.'); + if ($error === false) { + $output->write('Environment is correctly configured for production.' . PHP_EOL); + } } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php index 5f71125d0..412cf869b 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/GenerateProxiesCommand.php @@ -79,6 +79,10 @@ EOT $destPath = $em->getConfiguration()->getProxyDir(); } + if ( ! is_dir($destPath)) { + mkdir($destPath, 0777, true); + } + $destPath = realpath($destPath); if ( ! file_exists($destPath)) { diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 9dcc27d4b..c8957041a 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -933,6 +933,9 @@ public function () case ClassMetadataInfo::GENERATOR_TYPE_IDENTITY: return 'IDENTITY'; + case ClassMetadataInfo::GENERATOR_TYPE_NONE: + return 'NONE'; + default: throw new \InvalidArgumentException('Invalid provided IdGeneratorType: ' . $type); } diff --git a/tests/Doctrine/Tests/ORM/Functional/QueryTest.php b/tests/Doctrine/Tests/ORM/Functional/QueryTest.php index 4a13b0b7a..b1074cbc5 100644 --- a/tests/Doctrine/Tests/ORM/Functional/QueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/QueryTest.php @@ -224,7 +224,47 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase public function testGetSingleResultThrowsExceptionOnNoResult() { $this->_em->createQuery("select a from Doctrine\Tests\Models\CMS\CmsArticle a") - ->getSingleResult(); + ->getSingleResult(); + } + + /** + * @expectedException Doctrine\ORM\NoResultException + */ + public function testGetSingleScalarResultThrowsExceptionOnNoResult() + { + $this->_em->createQuery("select a from Doctrine\Tests\Models\CMS\CmsArticle a") + ->getSingleScalarResult(); + } + + /** + * @expectedException Doctrine\ORM\NonUniqueResultException + */ + public function testGetSingleScalarResultThrowsExceptionOnNonUniqueResult() + { + $user = new CmsUser; + $user->name = 'Guilherme'; + $user->username = 'gblanco'; + $user->status = 'developer'; + + $article1 = new CmsArticle; + $article1->topic = "Doctrine 2"; + $article1->text = "This is an introduction to Doctrine 2."; + $user->addArticle($article1); + + $article2 = new CmsArticle; + $article2->topic = "Symfony 2"; + $article2->text = "This is an introduction to Symfony 2."; + $user->addArticle($article2); + + $this->_em->persist($user); + $this->_em->persist($article1); + $this->_em->persist($article2); + + $this->_em->flush(); + $this->_em->clear(); + + $this->_em->createQuery("select a from Doctrine\Tests\Models\CMS\CmsArticle a") + ->getSingleScalarResult(); } public function testSupportsQueriesWithEntityNamespaces() diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC448Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC448Test.php new file mode 100644 index 000000000..91c9e129e --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC448Test.php @@ -0,0 +1,71 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC448MainTable'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC448ConnectedClass'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC448SubTable'), + )); + } + + public function testIssue() + { + $q = $this->_em->createQuery("select b from ".__NAMESPACE__."\\DDC448SubTable b where b.connectedClassId = ?1"); + $this->assertEquals('SELECT d0_.id AS id0, d0_.discr AS discr1, d0_.connectedClassId AS connectedClassId2 FROM SubTable s1_ INNER JOIN DDC448MainTable d0_ ON s1_.id = d0_.id WHERE d0_.connectedClassId = ?', $q->getSQL()); + } +} + +/** + * @Entity + * @InheritanceType("JOINED") + * @DiscriminatorColumn(name="discr", type="smallint") + * @DiscriminatorMap({ + * "0" = "DDC448MainTable", + * "1" = "DDC448SubTable" + * }) + */ +class DDC448MainTable +{ + /** + * @Id + * @Column(name="id", type="integer") + * @GeneratedValue(strategy="AUTO") + */ + private $id; + + /** + * @ManyToOne(targetEntity="DDC448ConnectedClass", cascade={"all"}, fetch="EAGER") + * @JoinColumn(name="connectedClassId", referencedColumnName="id", onDelete="CASCADE", onUpdate="CASCADE", nullable=true) + */ + private $connectedClassId; +} + +/** + * @Entity + * @Table(name="connectedClass") + * @HasLifecycleCallbacks + */ +class DDC448ConnectedClass +{ + /** + * @Id + * @Column(name="id", type="integer") + * @GeneratedValue(strategy="AUTO") + */ + protected $id; // connected with DDC448MainTable +} + +/** + * @Entity + * @Table(name="SubTable") + */ +class DDC448SubTable extends DDC448MainTable +{ +} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC493Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC493Test.php new file mode 100644 index 000000000..5503d033e --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC493Test.php @@ -0,0 +1,69 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC493Customer'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC493Distributor'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC493Contact') + )); + } + + public function testIssue() + { + $q = $this->_em->createQuery("select u, u.contact.data from ".__NAMESPACE__."\\DDC493Distributor u"); + $this->assertEquals('SELECT d0_.id AS id0, d1_.data AS data1, d0_.discr AS discr2, d0_.contact AS contact3 FROM DDC493Distributor d2_ INNER JOIN DDC493Customer d0_ ON d2_.id = d0_.id INNER JOIN DDC493Contact d1_ ON d0_.contact = d1_.id', $q->getSQL()); + } +} + +/** + * @Entity + * @InheritanceType("JOINED") + * @DiscriminatorColumn(name="discr", type="string") + * @DiscriminatorMap({"distributor" = "DDC493Distributor", "customer" = "DDC493Customer"}) + */ +class DDC493Customer { + /** + * @Id + * @Column(type="integer") + * @GeneratedValue(strategy="AUTO") + */ + public $id; + /** + * @OneToOne(targetEntity="DDC493Contact", cascade={"remove","persist"}) + * @JoinColumn(name="contact", referencedColumnName="id") + */ + public $contact; + +} + +/** + * @Entity + */ +class DDC493Distributor extends DDC493Customer { +} + +/** + * @Entity + */ +class DDC493Contact +{ + /** + * @Id + * @Column(type="integer") + * @GeneratedValue(strategy="AUTO") + */ + public $id; + /** @Column(type="string") */ + public $data; +} + + + + diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC513Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC513Test.php new file mode 100644 index 000000000..125f297db --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC513Test.php @@ -0,0 +1,71 @@ +_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC513OfferItem'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC513Item'), + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC513Price'), + )); + } + + public function testIssue() + { + $q = $this->_em->createQuery("select u from ".__NAMESPACE__."\\DDC513OfferItem u left join u.price p"); + $this->assertEquals('SELECT d0_.id AS id0, d0_.discr AS discr1, d0_.price AS price2 FROM DDC513OfferItem d1_ INNER JOIN DDC513Item d0_ ON d1_.id = d0_.id LEFT JOIN DDC513Price d2_ ON d0_.price = d2_.id', $q->getSQL()); + } +} + +/** + * @Entity + */ +class DDC513OfferItem extends DDC513Item +{ +} + +/** + * @Entity + * @InheritanceType("JOINED") + * @DiscriminatorColumn(name="discr", type="string") + * @DiscriminatorMap({"item" = "DDC513Item", "offerItem" = "DDC513OfferItem"}) + */ +class DDC513Item +{ + /** + * @Id + * @Column(type="integer") + * @GeneratedValue(strategy="AUTO") + */ + public $id; + + /** + * @OneToOne(targetEntity="DDC513Price", cascade={"remove","persist"}) + * @JoinColumn(name="price", referencedColumnName="id") + */ + public $price; +} + +/** + * @Entity + */ +class DDC513Price { + /** + * @Id + * @Column(type="integer") + * @GeneratedValue(strategy="AUTO") + */ + public $id; + + /** @Column(type="string") */ + public $data; +} + + + + diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.CTI.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.CTI.dcm.xml index 9c8c8f98e..472c16558 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.CTI.dcm.xml +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.ORM.Mapping.CTI.dcm.xml @@ -3,7 +3,7 @@ xsi="http://www.w3.org/2001/XMLSchema-instance" schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> - +