0

I have two entities Rental and Item. Items are associated to Rentals via a join table including some metadata, so that the join table effectively becomes a third entity RentedItem.

Because a RentedItem can be identified by its associated Rental and Item, it doesn't need its own ID but uses a compound key consisting of those two foreign keys as primary key instead.

/**
 * @ORM\Entity
 */
class Rental
{
    // ...
    /**
     * @ORM\OneToMany(targetEntity="RentedItem", mappedBy="rental", cascade={"all"})
     * @var ArrayCollection $rented_items
     */
    protected $rented_items;
    // ...
}

/**
 * @ORM\Entity
 */
class Item
{
    // ...
    // Note: The Item has no notion of any references to it.
}

/**
 * @ORM\Entity
 */
class RentedItem
{
    // ...
    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Rental", inversedBy="rented_items")
     * @var Rental $rental
     */
    protected $rental;
    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Item")
     * @var Item $item
     */
    protected $item;
    /**
     * @ORM\Column(type="boolean")
     * @var bool $is_returned
     */
    protected $is_returned = false;
    // ...
}

A Rental can be created or altered via a RESTful API, including some of its related objects. The corresponding controller uses the DoctrineObject hydrator of the ZF2 DoctrineModule to hydrate the rental object with the given form data. The new data is passed to the hydrator as an array of the form

$data = [
    // We only use the customer's ID to create a reference to an
    // existing customer. Altering or creating a customer via the
    // RestfulRentalController is not possible
    'customer' => 1, 
    'from_date' => '2016-03-09',
    'to_date' => '2016-03-22',
    // Rented items however should be alterable via the RestfulRentalController,
    // because they don't have their own API. Therefore we pass
    // the complete array representation to the hydrator
    'rented_items' => [
        [
            // Again, just as we did with the customer, 
            // only use the referenced item's ID, so that
            // changing an item is not possible
            'item' => 6, 
            'is_returned' => false
            // NOTE: obviously, the full array representation of
            // a rented item would also contain the 'rental' reference,
            // but since this is a new rental, there is no id yet, and
            // the reference should be implicitly clear via the array hirarchy
        ],
        [
            'item' => 42,
            'is_returned' => false
        ]
    ]
];

Usually the hydrator sets up the references correctly, even for completely new entities and new relations. However, with this complex association, hydrating the Rental fails. The code

$hydrator = new \DoctrineModule\Stdlib\Hydrator\DoctrineObject($entity_manager);
$rental = $hydrator->hydrate($data, $rental);

fails with the following exception

Doctrine\ORM\ORMException

File:

    /vagrant/app/vendor/doctrine/orm/lib/Doctrine/ORM/ORMException.php:294

Message:

    The identifier rental is missing for a query of Entity\RentedItem

Do I have to manually set up the references for the rented items? Or may this be caused by a faulty configuration or something?

Subsurf
  • 1,256
  • 1
  • 17
  • 28
  • have you tried adding `@ORM\OneToMany(targetEntity="RentedItem", mappedBy="rental", cascade={"persist"})` – Vytautas Mar 09 '16 at 10:55
  • It is actually set to `cascade={"all"}`, but the exception is thrown by `$hydrator->hydrate()`, long before flushing. So I think the cascade option doesn't even affect this problem, or does the hydrator take this setting into account too? – Subsurf Mar 09 '16 at 13:41

0 Answers0