I have a bi-directional OneToMany relationship using Doctrine 2 as an ORM within the ZendFramework 1.11.2.
Note: Doctrine did not create the database tables. The database is MySQL.
For some reason, when I persist and flush a new link entity to the link table (see below) the foreign key field (container_id) gets set to NULL. However, if the '@' symbol is removed from the 'ManyToOne(targetEntity="Shepherd\Navigation\Domain\Container\Model", inversedBy="links")' line, the foreign key field is populated properly.
Since the entity is added properly to the database when the '@' symbol is removed, there is something wrong with the OneToMany relationship somewhere.
For example, if I have a link model named $link (see pseudo-code below)...
$link (Shepherd\Navigation\Domain\Link\Model)
{
id: '' // auto generated value
cid: 23 // the foreign key value
label: test
uri: test.com
... // other values not listed here for brevity
}
...when the new link model is persisted and the entity manager is flushed, the container_id (foreign key) value from the newly inserted row in the link (shepherd_navigation_link) table is NULL.
$em // Assume $em is the Entity Manager
$em->persist($link);
$em->flush();
// The container_id in the newly added row in the
// link table (shepherd_navigation_link) is NULL
The link table schema:
CREATE TABLE `shepherd_navigation_link` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`container_id` int(10) unsigned DEFAULT NULL,
`node_id` int(10) unsigned DEFAULT NULL,
`parent_id` int(10) unsigned DEFA开发者_如何学编程ULT NULL,
`label` varchar(100) NOT NULL,
`options` text,
`events` text,
`privilege` varchar(100) NOT NULL,
`resource` varchar(100) DEFAULT NULL,
`uri` varchar(300) NOT NULL,
`visible` int(10) unsigned DEFAULT '1',
PRIMARY KEY (`id`),
KEY `container_id` (`container_id`)
) ENGINE=InnoDB
ALTER TABLE `shepherd_navigation_link` ADD FOREIGN KEY (container_id) REFERENCES shepherd_navigation_container(id)
Link entity model:
/**
* @Entity
* @Table(name="shepherd_navigation_link")
*/
class
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue
*/
protected $id;
/**
* @Column(name="container_id", type="integer", nullable=false)
*/
protected $cid;
/**
* @Column(name="node_id", type="integer")
*/
protected $nid;
/**
* @Column(name="parent_id", type="integer", nullable=false)
*/
protected $pid;
/**
* @Column
*/
protected $label;
/**
* @Column(nullable=true)
*/
protected $options;
/**
* @Column(nullable=true)
*/
protected $events;
/**
* @Column
*/
protected $privilege;
/**
* @Column(nullable=true)
*/
protected $resource;
/**
* @Column
*/
protected $uri;
/**
* @Column(type="integer", nullable=true)
*/
protected $visible;
/**
* @OneToMany(targetEntity="Model", mappedBy="parent")
*/
private $children;
/**
* @ManyToOne(targetEntity="Model", inversedBy="children")
*/
private $parent;
/**
*) @ManyToOne(targetEntity="Shepherd\Navigation\Domain\Container\Model", inversedBy="links"
*/
private $container;
/**
* @OneToOne(targetEntity="Shepherd\Navigation\Domain\Link\Position", inversedBy="link")
*/
private $node;
public function __construct()
{
$this->children = new \Doctrine\Common\Collections\ArrayCollection();
}
/** Accessors and Mutators excluded for brevity **/
}
Note: the protected property $cid maps to the container_id column above.
The container table schema:
CREATE TABLE `shepherd_navigation_container` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`description` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
The container entity model:
/**
* @Entity
* @Table(name="shepherd_navigation_container")
*/
class Model
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue
*/
protected $id;
/**
* @Column
*/
protected $name;
/**
* @Column(nullable=true)
*/
protected $description;
/**
* @OneToMany(targetEntity="Shepherd\Navigation\Domain\Link\Model", mappedBy="container")
*/
private $links;
/**
* Constructor
*/
public function __construct()
{
$this->links = new \Doctrine\Common\Collections\ArrayCollection();
}
/** Accessors and Mutators excluded for brevity **/
}
What am I missing? What am I doing wrong?
I figured out the problem (by reading the documentation http://www.doctrine-project.org/docs/orm/2.0/en/tutorials/getting-started-xml-edition.html). It turns out there were actually a few problems.
Problem 1 => I did not provide a method to set the container variable.
// Inside the Link Entity class...
public function setContainer($container)
{
$this->container = $container;
}
Problem 2 => I did not set the container value. In error, I thought Doctrine 2 did this internally, but I found out the container variable needs to be set prior to flushing.
Foolish oversight on my part.
$link = new Link();
$link->setContainer($container);
// $em is the Entity Manager
$em->persist($link);
$em->flush();
Problem 3 => The container ($container) needed to either be persisted prior to flushing or the @OneToMany definition on the container entity needed to change. I chose to update the container entity definition. Take a look here (http://www.doctrine-project.org/docs/orm/2.0/en/reference/working-with-associations.html#transitive-persistence-cascade-operations) for more information.
// Inside the Container Entity class...
/**
* @OneToMany(targetEntity="Shepherd\Navigation\Domain\Link\Model", mappedBy="container", cascade={"persist"})
*/
After making these changes and removing the @OneToOne node relationship in the link entity class (turns out I didn't need it), everything worked fine. I hope this helps someone.
精彩评论