1

I'm working on a ZF2/Apigility application using Doctrine v2.4.2.

The model structure looks like this: an Asset has an Attachment (so, it's a 1:1 relationship):

enter image description here

The problem is that I cannot persist an Asset with an embedded Attachment and get the error

23000, 1048, Column 'attachment_linkassetuuid' cannot be null

It seems that Doctrine tries to save the Attachment first, cannot find a value for the column attachment_linkassetuuid, sets it to null, and the foreign key constraint is broken.

How to define the entities correctly and get the cascade persisting working?


Asset

namespace MyDoctrineDataAccess\Model\Entity;
...
/**
 * Asset
 *
 * @ORM\Table(name="tbl_asset")
 * @ORM\Entity
 */
class Asset
{
    /**
     * @var string @ORM\Column(name="asset_uuid", type="string", length=36, nullable=false)
     * @ORM\Id
     */
    private $uuid;

    /**
     * @var \MyDoctrineDataAccess\Model\Entity\Attachment
     * @ORM\OneToOne(targetEntity="MyDoctrineDataAccess\Model\Entity\Attachment", mappedBy="asset", cascade={"remove", "persist"}, orphanRemoval=true)
     */
    private $attachment;
    ...
}

/**
 * @param \MyDoctrineDataAccess\Model\Entity\Attachment $attachment
 */
public function setAttachment($attachment) {
    $this->attachment = $attachment;
    return $this;
}

/**
 * @return the $attachment
 */
public function getAttachment() {
    return $this->attachment;
}

Attachment

namespace MyDoctrineDataAccess\Model\Entity;
...
/**
 * Attachment
 *
 * @ORM\Table(name="tbl_attachment", indexes={@ORM\Index(name="fk_attachment_uuid", columns={"attachment_linkassetuuid"})})
 * @ORM\Entity
 */
class Attachment
{
    /**
     *
     * @var string @ORM\Column(name="attachment_uuid", type="string", length=36, nullable=false)
     *      @ORM\Id
     */
    private $uuid;

    /**
     *
     * @var \MyDoctrineDataAccess\Model\Entity\Asset
     * @ORM\OneToOne(targetEntity="\MyDoctrineDataAccess\Model\Entity\Asset", inversedBy="attachment", cascade={"persist"})
     * @ORM\JoinColumn(name="attachment_linkassetuuid", referencedColumnName="asset_uuid")
     */
    private $asset;
    ...
}

/**
 *
 * @param \MyDoctrineDataAccess\Model\Entity\Asset $asset
 */
public function setAsset($asset)
{
    $this->asset = $asset;
    return $this;
}

/**
 *
 * @return the $asset
 * 
 */
public function getAsset()
{
    return $this->asset;
}

AssetService

namespace MyApi\V1\Rest\Asset;
...
class AssetService implements ServiceManagerAwareInterface
{
    ...
    public function saveAssets($data)
    {
        $entityManager = $this->serviceManager->get('Doctrine\ORM\EntityManager');
        $assetRepository = $entityManager->getRepository('My\Model\Entity\Asset');
        $hydratorManager = $this->serviceManager->get('hydratormanager');
        $hydrator = $hydratorManager->get('My\\Model\\Entity\\Hydrator\\EntityHydrator');
        foreach ($data as $assetData) {
            $asset = new Asset();
            $hydrator->hydrate($assetData, $asset);
            $entityManager->persist($asset);
            $entityManager->flush();
        }
    }
    ...
}
Wilt
  • 41,477
  • 12
  • 152
  • 203
automatix
  • 14,018
  • 26
  • 105
  • 230
  • Is your hydrator definitely working correctly, does it hydrate the `Asset` *and* the `Attachment`? Also, isn't the hydrated object returned from `hydrate`, unless you've customised it to be passed by reference? e.g. `$hydrated_object = $hydrator->hydrate($assetData, new Asset());` – Ankh Aug 03 '15 at 07:47
  • Hello and thanks for your comment! I've just checked this. At the moment of persisting (`$entityManager->persist($asset);`) the `Asset` entity is built correctly and its property `attachment` is an `Attachment` entity object. – automatix Aug 03 '15 at 11:06

1 Answers1

1

Since @JoinColumn defaults nullable to true according to the Doctrine documentation I don't really understand the error. But I do see that in your Attachment entity @table definition you declare an index:

indexes={@ORM\Index(name="fk_attachment_uuid", columns={"attachment_linkassetuuid"})}

Try once to remove this, rebuild your database and check if it solves your issue. If not then leave a comment and I will have another look.

Check the OneToOne example in the Doctrine documentation. Adding an @index like you do is not mentioned there. Doctrine will add the necessary indexes automatically to your relationship columns.

UPDATE:

I think the problem might be that Asset is not the owning side of the relationship. Check the documentation here. Try to change your setAttachment method like this:

public function setAttachment($attachment) 
{
    $this->attachment = $attachment;
    $attachment->setAsset($this);
    return $this;
}
Wilt
  • 41,477
  • 12
  • 152
  • 203
  • Thank you for your answer! Well, sure, that way I can avoid the error. But what you're actually recommending is removing the foreign key constraint (since it's necessary for removing this index). The next step would be then to make the column `attachment_linkassetuuid`. But it will only result in an inconsistent database with `attachment.attachment_linkassetuuid` always set to `NULL`. – automatix Aug 03 '15 at 11:00
  • @automatix Doctrine should take care of all this, you don't have to manually add a `@index` like you did... – Wilt Aug 03 '15 at 11:21
  • Maybe I misunderstood you. You you didn't mean, I should remove the index (and inevitably the `FOREIGN KEY`), but only the `@index` in the `Attachment` entity class annotation. Just tried this out, but the error `Column 'attachment_linkassetuuid' cannot be null` is still there. – automatix Aug 03 '15 at 11:28
  • Okay. Can you show all setters and getters that are relevant? So in `Asset` you should have `setAttachment()`, `getAttachment()` and in `Attachment` you should have `setAsset()`, `getAsset()` and please also show the `$data` you hydrate. – Wilt Aug 03 '15 at 11:34
  • I've just edited the question and added the getters and setters. The data I hydrate is correct, at least the `$entityManager->persist(...)` gets a complete `Asset` entity object with a nested `Attachment`. – automatix Aug 03 '15 at 12:47
  • Updated it as well. :) It works! Thank you very much! – automatix Aug 03 '15 at 15:06