2

This is a very very simple behavior, but I can't find the way to achieve it with doctrine. I'm going to explain it reducing the complexity with only two entities.

Having two entites (Author and Book) related like "One Author owns zero or more books" and "One book is owned by zero or one author", I'm trying to unset the relation between an author and one of his books (from the author side), expecting that book.author_id field in database sets as null.

The Entities are defined as follow:

Author

/**
 * Author
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="Poc\PocBundle\Entity\AuthorRepository")
 */
class Author
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;


    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Author
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @ORM\OneToMany(targetEntity="Book", mappedBy="author", cascade={"persist", "remove"})
     */
    private $books;

    public function __construct()
    {
        $this->products = new ArrayCollection();
    }

    /**
     * Add books
     *
     * @param \Poc\PocBundle\Entity\Book $books
     * @return Author
     */
    public function addBook(\Poc\PocBundle\Entity\Book $books)
    {
        $this->books[] = $books;

        return $this;
    }

    /**
     * Remove books
     *
     * @param \Poc\PocBundle\Entity\Book $books
     */
    public function removeBook(\Poc\PocBundle\Entity\Book $books)
    {
        $this->books->removeElement($books);
    }

    /**
     * Get books
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getBooks()
    {
        return $this->books;
    }
}

Book

/**
 * Book
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="Poc\PocBundle\Entity\BookRepository")
 */
class Book
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;


    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Book
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @ORM\ManyToOne(targetEntity="Author", inversedBy="books")
     * @ORM\JoinColumn(name="author_id", referencedColumnName="id", onDelete="SET NULL")
     */
     protected $author;


    /**
     * Set author
     *
     * @param \Poc\PocBundle\Entity\Author $author
     * @return Book
     */
    public function setAuthor(\Poc\PocBundle\Entity\Author $author = null)
    {
        $this->author = $author;

        return $this;
    }

    /**
     * Get author
     *
     * @return \Poc\PocBundle\Entity\Author 
     */
    public function getAuthor()
    {
        return $this->author;
    }
}

In the main controller I do the following.

  • Retrieve an author

  • Retrieve a Book owned by this author

  • Remove book from author

  • Persist Author & flush

  • Retrieve Book

  • Get the author ...

And the Author remains being the same. In database the field book.author_id of this record is not set to NULL as expected, but is still related with the author.

The Controller Code:

    $Book = $this->getDoctrine()
        ->getRepository('PocPocBundle:Book')
        ->find('1');
    $Author = $this->getDoctrine()
        ->getRepository('PocPocBundle:Author')
        ->find('1');

    $Author->removeBook($Book);


    $em = $this->getDoctrine()->getManager();
    $em->persist($Author);
    $em->flush();

    echo "<pre>";
    $Book = $this->getDoctrine()
        ->getRepository('PocPocBundle:Book')
        ->find('1');
    \Doctrine\Common\Util\Debug::dump($Book);

    die;

... And the output

object(stdClass)[286]
      public '__CLASS__' => string 'Poc\PocBundle\Entity\Book' (length=25)
      public 'id' => int 1
      public 'name' => string 'El Quijote' (length=10)
      public 'author' => 
        object(stdClass)[293]
          public '__CLASS__' => string 'Poc\PocBundle\Entity\Author' (length=27)
          public '__IS_PROXY__' => boolean true
          public '__PROXY_INITIALIZED__' => boolean true
          public 'id' => int 1
          public 'name' => string 'Cervantes' (length=9)
          public 'books' => string 'Array(1)' (length=8)

In the output of entity Book (id=1) we can see this books is still related with the author.

Sure, I'm missing something but I can't find where is the gap.

jonaguera
  • 142
  • 2
  • 10
  • Since you are removing why do you persist? It's also better to define `$em = $this->getDoctrine()->getManager();` on top then apply all changes base on `$em` finally you just `$em->flush();` And I don't think it's a good idea to select the book and author separately then remove; how can you make sure the id author with id 1 is related to book with id 1?! – Javad Jun 10 '14 at 15:53
  • In this example, I'm getting the book and the author separately in order to clarify the behavior. The real application doesn't do that, but I've reduce the problem to this example to simplify it. – jonaguera Jun 10 '14 at 15:58
  • Then why do persist if you want to remove? Also after your edit you need to refresh the entity by `$em->refresh($Book);` then you can print all new updated values – Javad Jun 10 '14 at 16:01

1 Answers1

2

If you want to remove the association between the Author and the Book entities, you should delete the association in your Book entity and persist it too by using $em->flush(). $em->persist($entity) is not used for entity removal. Deleting the association in your PHP objects and flush them through the ORM remove the association in the database.

Just check this in the Doctrine's documentation:

http://docs.doctrine-project.org/en/latest/reference/working-with-associations.html#removing-associations

I hope it will work for you. Cheers!