Browse files

Merge pull request #741 from doctrine/DDC-1884

Fixed DDC-1884.
  • Loading branch information...
2 parents 0e01099 + 157588f commit 08e38858ed36b13b2655f2971308361714798596 @guilhermeblanco guilhermeblanco committed Jul 30, 2013
View
53 lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php
@@ -146,6 +146,7 @@ protected function hydrateRowData(array $row, array &$cache, array &$result)
$baseElement =& $this->_resultPointers[$parent];
} else {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
+
continue;
}
@@ -167,6 +168,7 @@ protected function hydrateRowData(array $row, array &$cache, array &$result)
if ( ! $indexExists || ! $indexIsValid) {
$element = $data;
+
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element;
} else {
@@ -183,9 +185,15 @@ protected function hydrateRowData(array $row, array &$cache, array &$result)
} else {
$oneToOne = true;
- if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($baseElement[$relationAlias])) {
+ if (
+ ( ! isset($nonemptyComponents[$dqlAlias])) &&
+ ( ! isset($baseElement[$relationAlias]))
+ ) {
$baseElement[$relationAlias] = null;
- } else if ( ! isset($baseElement[$relationAlias])) {
+ } else if (
+ ( ! isset($baseElement[$relationAlias])) ||
+ ( ! isset($this->_identifierMap[$path][$id[$parent]][$id[$dqlAlias]]))
+ ) {
$baseElement[$relationAlias] = $data;
}
}
@@ -195,7 +203,6 @@ protected function hydrateRowData(array $row, array &$cache, array &$result)
if ($coll !== null) {
$this->updateResultPointer($coll, $index, $dqlAlias, $oneToOne);
}
-
} else {
// It's a root result element
@@ -204,41 +211,43 @@ protected function hydrateRowData(array $row, array &$cache, array &$result)
// if this row has a NULL value for the root result id then make it a null result.
if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
- if ($this->_rsm->isMixed) {
- $result[] = array($entityKey => null);
- } else {
- $result[] = null;
- }
+ $result[] = $this->_rsm->isMixed
+ ? array($entityKey => null)
+ : null;
+
$resultKey = $this->_resultCounter;
++$this->_resultCounter;
+
continue;
}
// Check for an existing element
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
- $element = $rowData[$dqlAlias];
- if ($this->_rsm->isMixed) {
- $element = array($entityKey => $element);
- }
+ $element = $this->_rsm->isMixed
+ ? array($entityKey => $rowData[$dqlAlias])
+ : $rowData[$dqlAlias];
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
$result[$resultKey] = $element;
} else {
$resultKey = $this->_resultCounter;
$result[] = $element;
+
++$this->_resultCounter;
}
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
} else {
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
$resultKey = $index;
+
/*if ($this->_rsm->isMixed) {
$result[] =& $result[$index];
++$this->_resultCounter;
}*/
}
+
$this->updateResultPointer($result, $index, $dqlAlias, false);
}
}
@@ -247,11 +256,9 @@ protected function hydrateRowData(array $row, array &$cache, array &$result)
if (isset($scalars)) {
if ( ! isset($resultKey) ) {
// this only ever happens when no object is fetched (scalar result only)
- if (isset($this->_rsm->indexByMap['scalars'])) {
- $resultKey = $row[$this->_rsm->indexByMap['scalars']];
- } else {
- $resultKey = $this->_resultCounter - 1;
- }
+ $resultKey = isset($this->_rsm->indexByMap['scalars'])
+ ? $row[$this->_rsm->indexByMap['scalars']]
+ : $this->_resultCounter - 1;
}
foreach ($scalars as $name => $value) {
@@ -279,19 +286,19 @@ private function updateResultPointer(array &$coll, $index, $dqlAlias, $oneToOne)
return;
}
- if ($index !== false) {
- $this->_resultPointers[$dqlAlias] =& $coll[$index];
+ if ($oneToOne) {
+ $this->_resultPointers[$dqlAlias] =& $coll;
return;
}
- if ( ! $coll) {
+ if ($index !== false) {
+ $this->_resultPointers[$dqlAlias] =& $coll[$index];
+
return;
}
- if ($oneToOne) {
- $this->_resultPointers[$dqlAlias] =& $coll;
-
+ if ( ! $coll) {
return;
}
View
42 tests/Doctrine/Tests/Models/Taxi/Car.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Doctrine\Tests\Models\Taxi;
+
+/**
+ * @Entity
+ * @Table(name="taxi_car")
+ */
+class Car
+{
+ /**
+ * @Id
+ * @Column(type="string", length=25)
+ * @GeneratedValue(strategy="NONE")
+ */
+ private $brand;
+
+ /**
+ * @Column(type="string", length=255);
+ */
+ private $model;
+
+ /**
+ * @OneToMany(targetEntity="Ride", mappedBy="car")
+ */
+ private $freeCarRides;
+
+ /**
+ * @OneToMany(targetEntity="PaidRide", mappedBy="car")
+ */
+ private $carRides;
+
+ public function setBrand($brand)
+ {
+ $this->brand = $brand;
+ }
+
+ public function setModel($model)
+ {
+ $this->model = $model;
+ }
+}
View
37 tests/Doctrine/Tests/Models/Taxi/Driver.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Doctrine\Tests\Models\Taxi;
+
+/**
+ * @Entity
+ * @Table(name="taxi_driver")
+ */
+class Driver
+{
+ /**
+ * @Id
+ * @Column(type="integer")
+ * @GeneratedValue(strategy="AUTO")
+ */
+ private $id;
+
+ /**
+ * @Column(type="string", length=255);
+ */
+ private $name;
+
+ /**
+ * @OneToMany(targetEntity="Ride", mappedBy="driver")
+ */
+ private $freeDriverRides;
+
+ /**
+ * @OneToMany(targetEntity="PaidRide", mappedBy="driver")
+ */
+ private $driverRides;
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+}
View
42 tests/Doctrine/Tests/Models/Taxi/PaidRide.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Doctrine\Tests\Models\Taxi;
+
+/**
+ * Same as Ride but with an extra column that is not part of the composite primary key
+ *
+ * @Entity
+ * @Table(name="taxi_paid_ride")
+ */
+class PaidRide
+{
+ /**
+ * @Id
+ * @ManyToOne(targetEntity="Driver", inversedBy="driverRides")
+ * @JoinColumn(name="driver_id", referencedColumnName="id")
+ */
+ private $driver;
+
+ /**
+ * @Id
+ * @ManyToOne(targetEntity="Car", inversedBy="carRides")
+ * @JoinColumn(name="car", referencedColumnName="brand")
+ */
+ private $car;
+
+ /**
+ * @Column(type="decimal", precision=6, scale=2)
+ */
+ private $fare;
+
+ public function __construct(Driver $driver, Car $car)
+ {
+ $this->driver = $driver;
+ $this->car = $car;
+ }
+
+ public function setFare($fare)
+ {
+ $this->fare = $fare;
+ }
+}
View
32 tests/Doctrine/Tests/Models/Taxi/Ride.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Doctrine\Tests\Models\Taxi;
+
+/**
+ * Test model that contains only Id-columns
+ *
+ * @Entity
+ * @Table(name="taxi_ride")
+ */
+class Ride
+{
+ /**
+ * @Id
+ * @ManyToOne(targetEntity="Driver", inversedBy="freeDriverRides")
+ * @JoinColumn(name="driver_id", referencedColumnName="id")
+ */
+ private $driver;
+
+ /**
+ * @Id
+ * @ManyToOne(targetEntity="Car", inversedBy="freeCarRides")
+ * @JoinColumn(name="car", referencedColumnName="brand")
+ */
+ private $car;
+
+ public function __construct(Driver $driver, Car $car)
+ {
+ $this->driver = $driver;
+ $this->car = $car;
+ }
+}
View
158 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1884Test.php
@@ -0,0 +1,158 @@
+<?php
+namespace Doctrine\Tests\ORM\Functional\Ticket;
+
+use Doctrine\Tests\Models\Taxi\Car,
+ Doctrine\Tests\Models\Taxi\Driver,
+ Doctrine\Tests\Models\Taxi\Ride,
+ Doctrine\Tests\Models\Taxi\PaidRide;
+
+require_once __DIR__ . '/../../../TestInit.php';
+
+/**
+ * @group DDC-1884
+ * @author Sander Coolen <sander@jibber.nl>
+ */
+class DDC1884Test extends \Doctrine\Tests\OrmFunctionalTestCase
+{
+ protected function setUp()
+ {
+ $this->useModelSet('taxi');
+ parent::setUp();
+
+ list($bimmer, $crysler, $merc, $volvo) = $this->createCars('Doctrine\Tests\Models\Taxi\Car');
+ list($john, $foo) = $this->createDrivers('Doctrine\Tests\Models\Taxi\Driver');
+ $this->_em->flush();
+
+ $ride1 = new Ride($john, $bimmer);
+ $ride2 = new Ride($john, $merc);
+ $ride3 = new Ride($john, $volvo);
+ $ride4 = new Ride($foo, $merc);
+
+ $this->_em->persist($ride1);
+ $this->_em->persist($ride2);
+ $this->_em->persist($ride3);
+ $this->_em->persist($ride4);
+
+ $ride5 = new PaidRide($john, $bimmer);
+ $ride5->setFare(10.50);
+
+ $ride6 = new PaidRide($john, $merc);
+ $ride6->setFare(16.00);
+
+ $ride7 = new PaidRide($john, $volvo);
+ $ride7->setFare(20.70);
+
+ $ride8 = new PaidRide($foo, $merc);
+ $ride8->setFare(32.15);
+
+ $this->_em->persist($ride5);
+ $this->_em->persist($ride6);
+ $this->_em->persist($ride7);
+ $this->_em->persist($ride8);
+
+ $this->_em->flush();
+ }
+
+ private function createCars($class)
+ {
+ $bimmer = new $class;
+ $bimmer->setBrand('BMW');
+ $bimmer->setModel('7-Series');
+
+ $crysler = new $class;
+ $crysler->setBrand('Crysler');
+ $crysler->setModel('300');
+
+ $merc = new $class;
+ $merc->setBrand('Mercedes');
+ $merc->setModel('C-Class');
+
+ $volvo = new $class;
+ $volvo->setBrand('Volvo');
+ $volvo->setModel('XC90');
+
+ $this->_em->persist($bimmer);
+ $this->_em->persist($crysler);
+ $this->_em->persist($merc);
+ $this->_em->persist($volvo);
+
+ return array($bimmer, $crysler, $merc, $volvo);
+ }
+
+ private function createDrivers($class)
+ {
+ $john = new $class;
+ $john->setName('John Doe');
+
+ $foo = new $class;
+ $foo->setName('Foo Bar');
+
+ $this->_em->persist($foo);
+ $this->_em->persist($john);
+
+ return array($john, $foo);
+ }
+
+ /**
+ * 1) Ride contains only columns that are part of its composite primary key
+ * 2) We use fetch joins here
+ */
+ public function testSelectFromInverseSideWithCompositePkAndSolelyIdentifierColumnsUsingFetchJoins()
+ {
+ $qb = $this->_em->createQueryBuilder();
+
+ $result = $qb->select('d, dr, c')
+ ->from('Doctrine\Tests\Models\Taxi\Driver', 'd')
+ ->leftJoin('d.freeDriverRides', 'dr')
+ ->leftJoin('dr.car', 'c')
+ ->where('d.name = ?1')
+ ->setParameter(1, 'John Doe')
+ ->getQuery()
+ ->getArrayResult();
+
+ $this->assertCount(1, $result);
+ $this->assertArrayHasKey('freeDriverRides', $result[0]);
+ $this->assertCount(3, $result[0]['freeDriverRides']);
+ }
+
+ /**
+ * 1) PaidRide contains an extra column that is not part of the composite primary key
+ * 2) Again we will use fetch joins
+ */
+ public function testSelectFromInverseSideWithCompositePkUsingFetchJoins()
+ {
+ $qb = $this->_em->createQueryBuilder();
+
+ $result = $qb->select('d, dr, c')
+ ->from('Doctrine\Tests\Models\Taxi\Driver', 'd')
+ ->leftJoin('d.driverRides', 'dr')
+ ->leftJoin('dr.car', 'c')
+ ->where('d.name = ?1')
+ ->setParameter(1, 'John Doe')
+ ->getQuery()->getArrayResult();
+
+ $this->assertCount(1, $result);
+ $this->assertArrayHasKey('driverRides', $result[0]);
+ $this->assertCount(3, $result[0]['driverRides']);
+ }
+
+ /**
+ * The other way around will fail too
+ */
+ public function testSelectFromOwningSideUsingFetchJoins()
+ {
+ $qb = $this->_em->createQueryBuilder();
+
+ $result = $qb->select('r, d, c')
+ ->from('Doctrine\Tests\Models\Taxi\PaidRide', 'r')
+ ->leftJoin('r.driver', 'd')
+ ->leftJoin('r.car', 'c')
+ ->where('d.name = ?1')
+ ->setParameter(1, 'John Doe')
+ ->getQuery()->getArrayResult();
+
+ $this->assertCount(3, $result);
+ $this->assertArrayHasKey('driver', $result[0]);
+ $this->assertArrayHasKey('car', $result[0]);
+ }
+}
View
14 tests/Doctrine/Tests/OrmFunctionalTestCase.php
@@ -155,7 +155,13 @@
'Doctrine\Tests\Models\CompositeKeyInheritance\JoinedChildClass',
'Doctrine\Tests\Models\CompositeKeyInheritance\SingleRootClass',
'Doctrine\Tests\Models\CompositeKeyInheritance\SingleChildClass',
- )
+ ),
+ 'taxi' => array(
+ 'Doctrine\Tests\Models\Taxi\PaidRide',
+ 'Doctrine\Tests\Models\Taxi\Ride',
+ 'Doctrine\Tests\Models\Taxi\Car',
+ 'Doctrine\Tests\Models\Taxi\Driver',
+ ),
);
/**
@@ -284,6 +290,12 @@ protected function tearDown()
$conn->executeUpdate('DELETE FROM SingleRootClass');
}
+ if (isset($this->_usedModelSets['taxi'])) {
+ $conn->executeUpdate('DELETE FROM taxi_paid_ride');
+ $conn->executeUpdate('DELETE FROM taxi_ride');
+ $conn->executeUpdate('DELETE FROM taxi_car');
+ $conn->executeUpdate('DELETE FROM taxi_driver');
+ }
$this->_em->clear();
}

0 comments on commit 08e3885

Please sign in to comment.