Up to now I used carefully written getters and setters to keep both sides 
of a 1:n association in sync (see code example below). This means, if the 
owning side of the association is set, then the set-method 
(PostalAdress::setPerson() in the example) takes care that the 
ArrayCollection of the far end is also updated. A thought this would be 
necessary due to the remark in the first paragraph of chapter 12.2 at 
http://doctrine-orm.readthedocs.org/en/latest/reference/unitofwork-associations.html
.

So, here is the code example:
<?php
declare(encoding='UTF-8');

namespace MyCompany\MyBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity
 */
class Person  {

  public function addPostalAddress( PostalAddress $postalAddress = null ) {
    if( is_null( $postalAddress ) ) return $this;
    if( $this->postalAddresses->contains( $postalAddress ) ) return $this;
    // Update this side
    $this->postalAddresses->add( $postalAddress );
    // Update opposed side
    $postalAddress->setPerson( $this );
    return $this;
  }

  public function removePostalAddress( PostalAddress $postalAddress = null, 
Person $newPerson = null ) {
    if( is_null( $postalAddress ) ) return $this;
    if( !( $this->postalAddresses->contains( $postalAddress ) ) ) return 
$this;
    if( $newPerson == $this ) return $this;
    // Update this side
    $this->postalAddresses->removeElement( $postalAddress );
    // Update opposed side
    $postalAddress->setPerson( $newPerson );
    return $this;
  }

  /**
   * @ORM\OneToMany( targetEntity = "PostalAddress", mappedBy = "person" )
   * @var Doctrine\Common\Collections\ArrayCollection
   */
  protected $postalAddresses = null;
}


/**
 * @ORM\Entity
 */
class PostalAddress  {

  public function __construct( Person $person ) {
    $this->setPerson( $person );
  }

  public function setPerson( Person $newPerson = null ) {
    if( $this->person == $newPerson ) return $this;
    // Update this side
    $oldPerson = $this->person;
    $this->person = $newPerson;
    // Update opposed side
    if( !is_null( $oldPerson ) ) $oldPerson->removePostalAddress( $this, 
$newPerson );
    if( !is_null( $newPerson ) ) $newPerson->addPostalAddress( $this );
    return $this;
  }

  /**
   * @ORM\ManyToOne( targetEntity = "Person", inversedBy = 
"postalAddresses" )
   * @ORM\JoinColumn( name = "person_id", referencedColumnName = "id", 
nullable = false );
   * @var Person
   */
  protected $person = null;
}

?>
To make it clear, there is no problem with this code above. It works as 
expected.

But then - out of curiosity - I drastically simplified the getters and 
setters to a naive implementation so that they do not take care of updating 
the opposite side. This means the code looked like
<?
phpclass Person  {
  ...
  public function addPostalAddress( PostalAddress $postalAddress = null ) {
    if( $this->postalAddresses->contains( $postalAddress ) ) return $this;
    // Update this side only
    $this->postalAddresses->add( $postalAddress );
    return $this;
  }

  public function removePostalAddress( PostalAddress $postalAddress = null ) 
{
    if( !( $this->postalAddresses->contains( $postalAddress ) ) ) return 
$this;
    // Update this side only
    $this->postalAddresses->removeElement( $postalAddress );
    return $this;
  }
  ...
}

class PostalAddress  {
  ...
  public function setPerson( Person $newPerson = null ) {
    // Update this side only
    $this->person = $newPerson;
  }
  ...
}
?>
I made an interesting observation. If a new entity PostalAddress is 
created, associated with an (already existing) Person and them the 
PostalAddress is persisted to the database for the very first time, then 
the ArrayCollection of the Person entity is not updated in memory. (Of 
course, the ArrayCollection is updated, if the Person entity is reloaded 
from database). E.g. the following code
<?php
$em = $this->getDoctrine()->getManager();
$person = $em->getRepository('HEKdbBundle:Person')->find( 42 );

dump( "Before creation" );
dump( $person->getPostalAddresses() );

$address = new PostalAddress( $person );

dump( "After creation and before persist" );
dump( $person->getPostalAddresses() );

$em->persist( $address );

dump( "After persists and before flush" );
dump( $person->getPostalAddresses() );

$em->flush();

dump( "After flush" );
dump( $person->getPostalAddresses() );
?>
shows that the new Person entity never gets into the ArrayCollection. 
Actually this is the behavior I had expected.

But the inverse way shows a different behavior:
<?php
$em = $this->getDoctrine()->getManager();
$address = $em->getRepository('HEKdbBundle:PostalAddress')->find( $allocId 
);
$person = $address->getPerson();

dump( "Before remove" );
dump( $person->getPostalAddresses() );

$em->remove( $address );

dump( "After remove and before flush" );
dump( $person->getPostalAddresses() );

$em->flush();

dump( "After flush" );
dump( $person->getPostalAddresses() );
?>
Here, the ArrayCollection contains the Address entity until the flush. But 
after the flush the Address entity is removed from the ArrayCollection by 
Doctrine.

Is this asymmetric behavior by intention? When yes, can I rely on it in 
future releases? And just out of curiosity, what is the reason behind this 
intention?


-- 
You received this message because you are subscribed to the Google Groups 
"doctrine-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/doctrine-user.
For more options, visit https://groups.google.com/d/optout.

Reply via email to