DDC-273: Cascading persists doesn't seem to work with OneToMany relationship for new objects in collection #338

Closed
opened 2026-01-22 12:35:15 +01:00 by admin · 6 comments
Owner

Originally created by @doctrinebot on GitHub (Jan 24, 2010).

Jira issue originally created by user lcf:

I found some issues like this e.g:
http://www.doctrine-project.org/jira/browse/DDC-108
http://www.doctrine-project.org/jira/browse/DDC-121

which seem to be resolved and one opened http://www.doctrine-project.org/jira/browse/DDC-172
opened one is kinda unclear, so I"m sorry if mine is going to be a duplicate.

The problem is not obvious behavior (at least for me)

Author entity:

/****
 * Author entity
 * 
 * @Entity
 * @Table(name="users")
 */
class Default*Model*Author
{
    /****
     * @Id @Column(name="id", type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    protected $_id = null;

    /*** @Column(name="username", type="string") **/
    protected $_name = null;

    /*** @OneToMany(targetEntity="Default_Model_Post", mappedBy="_author", cascade={"persist", "remove"}) **/
    protected $_posts = null;

    public function **construct()
    {
        $this->_posts = new Doctrine\Common\Collections\ArrayCollection();
        // if I use internal php array instead - issue is still here
    }

    public function getPosts()
    {
        return $this->_posts;
    }
}

Post entity (every Author has many of them):

/****
 * Post Entity
 * 
 * @Entity
 * @Table(name="posts")
 */
class Default*Model*Post
{
    /****
     * @Id @Column(name="id", type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    protected $_id = null;

    /*** @Column(name="title", type="string") **/
    protected $_title = null;

    /****
     * @ManyToOne(targetEntity="Default*Model*Author")
     * @JoinColumn(name="author_id", referencedColumnName="id")
     */
    protected $_author;

    public function setTitle($name)
    {
        $this->_title = $name;
    }

    public function getTitle()
    {
        return $this->_title;
    }

    public function getAuthor()
    {
        return $this->_author;
    }

    public function setAuthor(Default*Model*Author $author)
    {
        $this->_author = $author;
    }
}

So, let's say I want to add a newly created post to an author :

// Get the author (no matter if it's managed one, selected from the database or just newly created one)
$author = Zend*Registry::get('entityManager')->find('Default_Model*Author', 113);

// Create a post entity:
$post1 = new Default*Model*Post();
$post1->setTitle('This is my first post ever!');

// Adding post entity to the post collection
$author->getPosts()->add($post1);

// Trying to persist the author along with the posts collection:
Zend_Registry::get('entityManager')->persist($author);
Zend_Registry::get('entityManager')->flush();

gives the following exception:

bq. Message: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'author_id' cannot be null

I'd expect for all those posts in the collection to have author_id set automatically. Now I have to do smth like this:

$post1->setAuthor($author);
$author->getPosts()->add($post1);
Originally created by @doctrinebot on GitHub (Jan 24, 2010). Jira issue originally created by user lcf: I found some issues like this e.g: http://www.doctrine-project.org/jira/browse/[DDC-108](http://www.doctrine-project.org/jira/browse/DDC-108) http://www.doctrine-project.org/jira/browse/[DDC-121](http://www.doctrine-project.org/jira/browse/DDC-121) which seem to be resolved and one opened http://www.doctrine-project.org/jira/browse/[DDC-172](http://www.doctrine-project.org/jira/browse/DDC-172) opened one is kinda unclear, so I"m sorry if mine is going to be a duplicate. The problem is not obvious behavior (at least for me) Author entity: ``` /**** * Author entity * * @Entity * @Table(name="users") */ class Default*Model*Author { /**** * @Id @Column(name="id", type="integer") * @GeneratedValue(strategy="AUTO") */ protected $_id = null; /*** @Column(name="username", type="string") **/ protected $_name = null; /*** @OneToMany(targetEntity="Default_Model_Post", mappedBy="_author", cascade={"persist", "remove"}) **/ protected $_posts = null; public function **construct() { $this->_posts = new Doctrine\Common\Collections\ArrayCollection(); // if I use internal php array instead - issue is still here } public function getPosts() { return $this->_posts; } } ``` Post entity (every Author has many of them): ``` /**** * Post Entity * * @Entity * @Table(name="posts") */ class Default*Model*Post { /**** * @Id @Column(name="id", type="integer") * @GeneratedValue(strategy="AUTO") */ protected $_id = null; /*** @Column(name="title", type="string") **/ protected $_title = null; /**** * @ManyToOne(targetEntity="Default*Model*Author") * @JoinColumn(name="author_id", referencedColumnName="id") */ protected $_author; public function setTitle($name) { $this->_title = $name; } public function getTitle() { return $this->_title; } public function getAuthor() { return $this->_author; } public function setAuthor(Default*Model*Author $author) { $this->_author = $author; } } ``` So, let's say I want to add a newly created post to an author : ``` // Get the author (no matter if it's managed one, selected from the database or just newly created one) $author = Zend*Registry::get('entityManager')->find('Default_Model*Author', 113); // Create a post entity: $post1 = new Default*Model*Post(); $post1->setTitle('This is my first post ever!'); // Adding post entity to the post collection $author->getPosts()->add($post1); // Trying to persist the author along with the posts collection: Zend_Registry::get('entityManager')->persist($author); Zend_Registry::get('entityManager')->flush(); ``` gives the following exception: bq. Message: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'author_id' cannot be null I'd expect for all those posts in the collection to have author_id set automatically. Now I have to do smth like this: ``` $post1->setAuthor($author); $author->getPosts()->add($post1); ```
admin added the Bug label 2026-01-22 12:35:15 +01:00
admin closed this issue 2026-01-22 12:35:15 +01:00
Author
Owner

@doctrinebot commented on GitHub (Jan 24, 2010):

Comment created by romanb:

This is expected behavior. Please re-read: http://www.doctrine-project.org/documentation/manual/2_0/en/association-mapping:owning-side-and-inverse-side .

@doctrinebot commented on GitHub (Jan 24, 2010): Comment created by romanb: This is expected behavior. Please re-read: http://www.doctrine-project.org/documentation/manual/2_0/en/association-mapping:owning-side-and-inverse-side .
Author
Owner

@doctrinebot commented on GitHub (Jan 24, 2010):

Issue was closed with resolution "Invalid"

@doctrinebot commented on GitHub (Jan 24, 2010): Issue was closed with resolution "Invalid"
Author
Owner

@doctrinebot commented on GitHub (Aug 22, 2012):

Comment created by halfdan:

Look at this: Alexander marked the author->post association with cascade persist.
By reading this chapter:
[http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html]
one would presume that persisting an author object in this case would also persist all of the new posts.
If this is an expected behavior that means that the documentation is misleading.

@doctrinebot commented on GitHub (Aug 22, 2012): Comment created by halfdan: Look at this: Alexander marked the author->post association with cascade persist. By reading this chapter: [http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html] one would presume that persisting an author object in this case would also persist all of the new posts. If this is an expected behavior that means that the documentation is misleading.
Author
Owner

@doctrinebot commented on GitHub (Aug 22, 2012):

Comment created by stof:

It would, if the changes were tracked in the collection (for instance, the owning side of a ManyToMany). But in this case, your collection is the inversed side of the relation, so Doctrine does not bother at all about the changes you do in it. It does not even try to check if you did some. Relations are only tracked on the owning side.

@doctrinebot commented on GitHub (Aug 22, 2012): Comment created by stof: It would, if the changes were tracked in the collection (for instance, the owning side of a ManyToMany). But in this case, your collection is the **inversed** side of the relation, so Doctrine does not bother at all about the changes you do in it. It does not even try to check if you did some. Relations are only tracked on the owning side.
Author
Owner

@doctrinebot commented on GitHub (Aug 22, 2012):

Comment created by stof:

Btw, note that the link given by Roman in the comment above appears at the 4th line of the page you linked, with the mention it is an important concept

@doctrinebot commented on GitHub (Aug 22, 2012): Comment created by stof: Btw, note that the link given by Roman in the comment above appears at the 4th line of the page you linked, with the mention it is an important concept
Author
Owner

@doctrinebot commented on GitHub (Aug 22, 2012):

Comment created by halfdan:

In the link I linked it reads:

{quote}To have Doctrine handle both cases automatically we can change the User#commentsAuthored property to cascade both the “persist” and the “remove” operation.{quote}

     * Bidirectional - One-To-Many (INVERSE SIDE)
     *
     * @OneToMany(targetEntity="Comment", mappedBy="author", cascade={"persist", "remove"})
     */
    private $commentsAuthored;

Doesn't that mean that even though doctrine only checks owning side for changes, in case of cascade persist it will check both sides? Otherwise, how would cascade persist be useful?

@doctrinebot commented on GitHub (Aug 22, 2012): Comment created by halfdan: In the link I linked it reads: {quote}To have Doctrine handle both cases automatically we can change the User#commentsAuthored property to cascade both the “persist” and the “remove” operation.{quote} ``` /**** * Bidirectional - One-To-Many (INVERSE SIDE) * * @OneToMany(targetEntity="Comment", mappedBy="author", cascade={"persist", "remove"}) */ private $commentsAuthored; ``` Doesn't that mean that even though doctrine only checks owning side for changes, in case of cascade persist it will check both sides? Otherwise, how would cascade persist be useful?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#338