diff --git a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php index b147f3dd5..9d04cf5f1 100644 --- a/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php @@ -1001,27 +1001,26 @@ class BasicEntityPersister $columnList .= $assoc2ColumnSQL; } } - - $this->_selectJoinSql .= ' LEFT JOIN'; // TODO: Inner join when all join columns are NOT nullable. $first = true; if ($assoc['isOwningSide']) { + $this->_selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($assoc['joinColumns']); $this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON '; foreach ($assoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) { if ( ! $first) { $this->_selectJoinSql .= ' AND '; } - - $this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = ' - . $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.' . $targetCol . ' '; + $this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.' . $sourceCol . ' = ' + . $this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.' . $targetCol; $first = false; } } else { $eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']); $owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']); - $this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' + $this->_selectJoinSql .= ' ' . $this->getJoinSQLForJoinColumns($owningAssoc['joinColumns']); + $this->_selectJoinSql .= ' ' . $eagerEntity->getQuotedTableName($this->_platform) . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) . ' ON '; foreach ($owningAssoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) { @@ -1030,7 +1029,7 @@ class BasicEntityPersister } $this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.' . $sourceCol . ' = ' - . $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol . ' '; + . $this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol; $first = false; } } @@ -1499,6 +1498,24 @@ class BasicEntityPersister return (bool) $this->_conn->fetchColumn($sql, $params); } + /** + * Generates the appropriate join SQL for the given join column. + * + * @param array $joinColumns The join columns definition of an association. + * @return string LEFT JOIN if one of the columns is nullable, INNER JOIN otherwise. + */ + protected function getJoinSQLForJoinColumns($joinColumns) + { + // if one of the join columns is nullable, return left join + foreach($joinColumns as $joinColumn) { + if(isset($joinColumn['nullable']) && $joinColumn['nullable']){ + return 'LEFT JOIN'; + } + } + + return 'INNER JOIN'; + } + /** * Gets an SQL column alias for a column name. * diff --git a/tests/Doctrine/Tests/ORM/Functional/OneToOneEagerLoadingTest.php b/tests/Doctrine/Tests/ORM/Functional/OneToOneEagerLoadingTest.php index 044a17381..5361aad45 100644 --- a/tests/Doctrine/Tests/ORM/Functional/OneToOneEagerLoadingTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/OneToOneEagerLoadingTest.php @@ -115,6 +115,34 @@ class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $waggon->train); $this->assertNotNull($waggon->train); } + + public function testEagerLoadWithNullableColumnsGeneratesLeftJoin() + { + $train = new Train(); + $this->_em->persist($train); + $this->_em->flush(); + $this->_em->clear(); + + $train = $this->_em->find(get_class($train), $train->id); + $this->assertEquals( + "SELECT t0.id AS id1, t0.driver_id AS driver_id2, t3.id AS id4, t3.name AS name5 FROM Train t0 LEFT JOIN TrainDriver t3 ON t0.driver_id = t3.id WHERE t0.id = ?", + $this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql'] + ); + } + + public function testEagerLoadWithNonNullableColumnsGeneratesInnerJoin() + { + $waggon = new Waggon(); + $this->_em->persist($waggon); + $this->_em->flush(); + $this->_em->clear(); + + $waggon = $this->_em->find(get_class($waggon), $waggon->id); + $this->assertEquals( + "SELECT t0.id AS id1, t0.train_id AS train_id2, t3.id AS id4, t3.driver_id AS driver_id5 FROM Waggon t0 INNER JOIN Train t3 ON t0.train_id = t3.id WHERE t0.id = ?", + $this->_sqlLoggerStack->queries[$this->_sqlLoggerStack->currentQuery]['sql'] + ); + } } /** @@ -130,6 +158,7 @@ class Train /** * Owning side * @OneToOne(targetEntity="TrainDriver", inversedBy="train", fetch="EAGER", cascade={"persist"}) + * @JoinColumn(nullable=true) */ public $driver; /** @@ -195,4 +224,4 @@ class Waggon { $this->train = $train; } -} \ No newline at end of file +}