Search code examples
phpdoctrine-ormsilex

Why is the ID of the inverse side not being to added to the owning side in Doctrine 2 One to Many relationship?


I have created two classes Website and WebsiteDomain. There can be multiple domains to a website so I have set up a OneToMany relationship in the annotations in the classes.

Website.php

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;

/**
 * Website object for the chosen site
 *
 * @ORM\Entity
 * @ORM\Table(name="websites")
 */
class Website
{

    /**
     * @JMS\Type("integer")
     *
     * @ORM\Id
     * @ORM\Column(type="integer", nullable=false)
     * @ORM\GeneratedValue
     *
     * @var integer $id
     */
    protected $id;

    /**
     * Domains that this website answers on
     *
     * @JMS\Type("ArrayCollection<Turtle\Model\Entity\WebsiteDomain>")
     * @ORM\OneToMany(targetEntity="WebsiteDomain", mappedBy="website", cascade={"persist", "remove"})
     *
     * @var WebsiteDomain
     */
    private $domains;

}

WebsiteDomain.php

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;

/**
 * Website object for the chosen site
 *
 * @ORM\Entity
 * @ORM\Table(name="website_domains")
 */
class WebsiteDomain
{

    /**
     * @JMS\Type("integer")
     *
     * @ORM\Id
     * @ORM\Column(type="integer", nullable=false)
     * @ORM\GeneratedValue
     *
     * @var integer $id
     */
    protected $id;

    /**
     * Website that this domain belongs to
     *
     * @JMS\Type("Website")
     * @ORM\ManyToOne(targetEntity="Website", inversedBy="domains")
     *
     * @var \Turtle\Model\Entity\Website
     */
    private $website;

}

Now when I create a new Website that has multiple domains attached to it all the records are created in the relevant tables, but the webiste_id that the domains belong to is NULL.

| id | name    | description    | parent | storageName |
|----|---------|----------------|--------|-------------|
| 1  | foo_bar | FooBar Website |        | foo_bar     |

| id | website_id | domain       | primary | 
|----|------------|--------------|---------|
| 1  | NULL       | foobar.co.uk | 1       |

The website_id should be null in the last table relating to the website in the first table.

I know this question has been asked many times on here but I have not be able to find an answer. I have played around with different PDO drivers, SQL and MySQL and both exhibit the same problem.

I am creating the records using the following object. The only thing I can think that is that the website_id in the WebsiteDomain is set to null, but if this is the case how can I get Doctrine to override this value?

object(Turtle\Model\Entity\Website)#412 (10) {
  ["name":"Turtle\Model\Entity\Website":private]=>
  string(7) "foo_bar"
  ["displayName":"Turtle\Model\Entity\Website":private]=>
  string(7) "Foo Bar"
  ["description":"Turtle\Model\Entity\Website":private]=>
  string(14) "FooBar Website"
  ["parent":"Turtle\Model\Entity\Website":private]=>
  NULL
  ["domains":"Turtle\Model\Entity\Website":private]=>
  object(Doctrine\Common\Collections\ArrayCollection)#410 (1) {
    ["elements":"Doctrine\Common\Collections\ArrayCollection":private]=>
    array(1) {
      [0]=>
      object(Turtle\Model\Entity\WebsiteDomain)#371 (7) {
        ["website":"Turtle\Model\Entity\WebsiteDomain":private]=>
        NULL
        ["domain":"Turtle\Model\Entity\WebsiteDomain":private]=>
        string(12) "foobar.co.uk"
        ["primary":"Turtle\Model\Entity\WebsiteDomain":private]=>
        bool(true)
        ["id":protected]=>
        NULL
        ["created":protected]=>
        NULL
        ["modified":protected]=>
        NULL
        ["admupdated":protected]=>
        bool(false)
      }
    }
  }
  ["storageName":"Turtle\Model\Entity\Website":private]=>
  string(7) "foo_bar"
  ["id":protected]=>
  NULL
  ["created":protected]=>
  NULL
  ["modified":protected]=>
  NULL
  ["admupdated":protected]=>
  bool(false)
}

This object is being deserialized from an array using the JMS Serializer.

Any pointers are gratfully recieved.


Solution

  • Normally when working with Doctrine, I've seen this happen when I've forgotten to set the parent on the child entity in the parent's addChild method.

    Specifically for your example that would be in the addDomain method of your Website entity. By default, it will be something like this:

    public function addDomain (WebsiteDomain $domain) {
         $this->domains[] = $domain;
         return $this;
    }
    

    But you need to explicitly set the parent in that method.

    public function addDomain (WebsiteDomain $domain) {
         $domain->setWebsite($this);
         $this->domains[] = $domain;
         return $this;
    }
    

    It might seem like the cascade annotation would do this automatically, but it doesn't.

    I'm not sure if this is necessary in your set up. It might not be the same problem you're having, because I'm not familiar with some of the tools you're using, but figured it was worth a try.