Wolf-Tech
Wolf-Tech

Reputation: 1309

Doctrine2 oneToOne composite primary key not nullable

I've a problem with a composite primary key on a OneToOne relation in Doctrine. When one object has no relation (which is normally possible) I got the following error message:

Doctrine\Common\Proxy\Exception\OutOfBoundsException: Missing value for 
primary key idEngine on Farkas\CoreBundle\Entity\Engine

I've found the problem directly in Doctrine:

vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php Line 2116

// TODO: Is this even computed right in all cases of composite keys?
foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
    $joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
    if ($joinColumnValue !== null) {
        if ($targetClass->containsForeignIdentifier) {
            $associatedId[$targetClass->getFieldForColumn($targetColumn)] = $joinColumnValue;
        } else {
            $associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
        }
    }
}

$joinColumnValue is null when the foreign key is empty because the array $data which includes the row data doesn't have the array key for the relation field. The todo comment tells me that something is wrong with composite keys?

Here my two Entities (just a small test, don't think about the logic of my entities^^):

1st

/**
 * Car
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="Farkas\CoreBundle\Entity\CarRepository")
 */
class Car
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id_car", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $idCar;


    /**
     * @var string
     *
     * @ORM\Column(name="brand", type="string", length=255)
     */
    private $brand;

    /**
     * @var string
     *
     *
     * @ORM\OneToOne(targetEntity="Engine", mappedBy="car")
     * @ORM\JoinColumns(
     *    @ORM\JoinColumn(name="engine", referencedColumnName="id_engine"),
     *    @ORM\JoinColumn(name="country", referencedColumnName="country")
     * )
     */
    private $engine;

    /**
     * @var integer
     * @ORM\Id
     * @ORM\Column(name="country", type="integer")
     */
    private $country;
}

2nd

/**
 * Engine
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="Farkas\CoreBundle\Entity\EngineRepository")
 */
class Engine
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id_engine", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $idEngine;

    /**
     * @var string
     *
     * @ORM\Column(name="engineName", type="string", length=255)
     */
    private $engineName;

    /**
     * @var string
     *
     * @ORM\Column(name="description", type="string", length=255)
     */
    private $description;

    /**
     * @var integer
     * @ORM\Id
     * @ORM\Column(name="country", type="integer")
     */
    private $country;

    /**
     * @var integer
     *
     * @ORM\OneToOne(targetEntity="Car", inversedBy="engine")
     * @ORM\JoinColumns(
     *    @ORM\JoinColumn(name="car_id", referencedColumnName="id_car"),
     *    @ORM\JoinColumn(name="country", referencedColumnName="country")
     * )
     */
    private $car;
}

When I try to execute findAll() I got the error message above. When I execute the generated SQL directly in MySQL, everything looks fine:

SELECT t0.id_car AS id_car1, t0.brand AS brand2, t0.country AS country3, 
t0.engine AS engine4, t0.country AS country5 FROM Car t0

It means, the hydration can't work with an empty oneToOne relation.

Upvotes: 2

Views: 2196

Answers (1)

halfer
halfer

Reputation: 20440

(Posted on behalf of the OP.)

As expected it was just an error from my side. This is the solution:

Car Entity

/**
 * @var string
 *
 *
 * @ORM\OneToOne(targetEntity="Engine", mappedBy="car")
 * @ORM\JoinColumns(
 *    @ORM\JoinColumn(name="fk_engine", referencedColumnName="id_engine"),
 *    @ORM\JoinColumn(name="fk_engine_country", referencedColumnName="country")
 * )
 */
private $engine;

Engine Entity

/**
 * @var integer
 *
 * @ORM\OneToOne(targetEntity="Car", inversedBy="engine")
 * @ORM\JoinColumns(
 *    @ORM\JoinColumn(name="fk_car", referencedColumnName="id_car"),
 *    @ORM\JoinColumn(name="fk_car_country", referencedColumnName="country")
 * )
 */
private $car;

The name of the join column is an attribute which will be created as relation fields inside the db. I already had an attribute with the name country inside the table. So finally the name is always the foreign key to identify the relation.

Upvotes: 1

Related Questions