Original question
I am doing a fetch join in Doctrine, using a join table that has a composite key and no other defined fields, and getting incorrect data loaded into my entity. Is this a bug in Doctrine, or am I doing something wrong?
What follows is a simple example that illustrates the problem.
Create three entities:
/**
* @ORM\Entity
* @ORM\Table(name="driver")
*/
class Driver
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", length=255);
*/
private $name;
/**
* @ORM\OneToMany(targetEntity="DriverRide", mappedBy="driver")
*/
private $driverRides;
function getId() { return $this->id; }
function getName() { return $this->name; }
function getDriverRides() { return $this->driverRides; }
}
/**
* @ORM\Entity
* @ORM\Table(name="driver_ride")
*/
class DriverRide
{
/**
* @ORM\Id
* @ORM\ManyToOne(targetEntity="Driver", inversedBy="driverRides")
* @ORM\JoinColumn(name="driver_id", referencedColumnName="id")
*/
private $driver;
/**
* @ORM\Id
* @ORM\ManyToOne(targetEntity="Car", inversedBy="carRides")
* @ORM\JoinColumn(name="car", referencedColumnName="brand")
*/
private $car;
function getDriver() { return $this->driver; }
function getCar() { return $this->car; }
}
/**
* @ORM\Entity
* @ORM\Table(name="car")
*/
class Car
{
/**
* @ORM\Id
* @ORM\Column(type="string", length=25)
* @ORM\GeneratedValue(strategy="NONE")
*/
private $brand;
/**
* @ORM\Column(type="string", length=255);
*/
private $model;
/**
* @ORM\OneToMany(targetEntity="DriverRide", mappedBy="car")
*/
private $carRides;
function getBrand() { return $this->brand; }
function getModel() { return $this->model; }
function getCarRides() { return $this->carRides; }
}
Populate the corresponding database tables with this data:
INSERT INTO driver (id, name) VALUES (1, 'John Doe');
INSERT INTO car (brand, model) VALUES ('BMW', '7 Series');
INSERT INTO car (brand, model) VALUES ('Crysler', '300');
INSERT INTO car (brand, model) VALUES ('Mercedes', 'C-Class');
INSERT INTO car (brand, model) VALUES ('Volvo', 'XC90');
INSERT INTO car (brand, model) VALUES ('Dodge', 'Dart');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'Crysler');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'Mercedes');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'Volvo');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'BMW');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'Dodge');
Use this code to hydrate a Driver
entity and display its contents:
$qb = $em->createQueryBuilder();
$driver = $qb->select('d, dr, c')
->from('Driver', 'd')
->leftJoin('d.driverRides', 'dr')
->leftJoin('dr.car', 'c')
->where('d.id = 1')
->getQuery()->getSingleResult();
print '<p>' . $driver->getName() . ':';
foreach ($driver->getDriverRides() as $ride) {
print '<br>' . $ride->getCar()->getBrand() . ' ' . $ride->getCar()->getModel();
}
Expected output:
John Doe:
BMW 7 Series
Crysler 300
Dodge Dart
Mercedes C-Class
Volvo XC90
Actual output:
John Doe:
BMW 7 Series
Dodge Dart
Dodge Dart
Volvo XC90
Volvo XC90
There is a strange duplication going on here with the associated entities. Specifically, child entity #3 is duplicated as #2, #5 is duplicated as #4, etc., and child entity #2, #4, etc. are not loaded at all. This pattern is consistent and reproducible.
Is there something wrong with my code? Why is Doctrine failing to correctly map the data from the database tables to the entities?
Additional remarks
The same problem occurs if one queries for all rides (aka associations between drivers and cars) and performs a fetch-join. This is an important case, because the answer by @qaqar-haider does not work for this scenario.
Assume the following table data
INSERT INTO driver (id, name) VALUES (1, 'John Doe');
INSERT INTO driver (id, name) VALUES (2, 'Erika Mustermann');
INSERT INTO car (brand, model) VALUES ('BMW', '7 Series');
INSERT INTO car (brand, model) VALUES ('Crysler', '300');
INSERT INTO car (brand, model) VALUES ('Mercedes', 'C-Class');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'Crysler');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'Mercedes');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'BMW');
INSERT INTO driver_ride (driver_id, car) VALUES (2, 'BMW');
INSERT INTO driver_ride (driver_id, car) VALUES (2, 'Crysler');
Imagine the following query with fetch-joins
$qb = $em->createQueryBuilder();
$rides = $qb->select('dr, d, c')
->from('DriverRide', 'dr')
->leftJoin('dr.driver', 'd')
->leftJoin('dr.car', 'c')
->getQuery()->getResult();
foreach ($rides as $ride) {
print $ride->driver->getName() . ': ' . $ride->getCar()->getModel();
}
Expected output:
John Doe: Chrysler
John Doe: Mercedes
John Doe: BMW
Erika Mustermann: BMW
Erika Mustermann: Chrysler
Actual output:
John Doe: Chrysler
John Doe: Mercedes
John Doe: Mercedes
Erika Mustermann: BMW
Erika Mustermann: BMW
Once again, the number of results is correct, but the associations are mixed up.
This is a simplified test case of the original question and is not subject to any WHERE or GROUP BY clause.