Doctrine - Get old value in onFlush event listener

Rafael Adel picture Rafael Adel · May 12, 2014 · Viewed 7.8k times · Source

I'm trying to implement a feature that let's the user specify his mutual status and select a another user, just like that one in Facebook.

My database schema is like:

UserInfo entity:

class UserInfo
{

    /**
     * @ORM\OneToOne(targetEntity="User", inversedBy="user_info")
     */
    protected $user;

    /**
     * @ORM\Column(type="text", nullable=true)
     * @Assert\NotBlank
     */
    protected $status; //Value taken from a dropdown list

    /**
     * @ORM\OneToOne(targetEntity="User", inversedBy="relationship_with_table")
     */
    protected $relationship_user;
}

User entity:

class User
{

    /**
    * @ORM\OneToOne(targetEntity="UserInfo", mappedBy ="user", cascade={"persist","remove"})
    */
    protected $user_info;

    /**
     * @ORM\OneToOne(targetEntity="UserInfo", mappedBy="relationship_user")
     */
    protected $relationship_with_table;
}

UserInfoType :

class UserInfoType extends AbstractType
{

    public function buildForm(FormBuilderInterface $builder, array $option)
    {
        $builder
            ->add("birthday", "birthday")
            ->add("gender", "choice", array("choices" => array("0" => "Male", "1" => "Female")))
            ->add("status", "choice", array("choices" => array(
                    "Single" => "Single",
                    "In relationship" => "In relationship",
                    "Engaged" => "Engaged",
                    "Married" => "Married"
                )))
            ->add("relationship_user", "thrace_select2_entity", array(
                    "class" => 'Zgh\FEBundle\Entity\User',
                    'label' => 'User',
                    'empty_value' => 'Select user',
                    "configs" => array(
                        "width" => '100%',
                    ),
                ))
            ->add("city", "text")
            ->add("work", "text")
            ->add("facebook", "url")
            ->add("twitter", "url")
        ;
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
                "data_class" => 'Zgh\FEBundle\Entity\UserInfo',
                "cascade_validation" => true,
            ));
    }

    public function getName()
    {
        return "user_info";
    }
}

Now here's the event listener, It checks the user status and change the other user one, It works as expected. But only has one flaw, If the user is married to (A) and then changed it to married to (B), The user (A) still gets the Married to and don't get reset.

All what i want to do is before attaching user relationship to (B), I want to retrieve (A) and reset it. How can this be done inside onFlush event.

class DoctrinePreUpdateHandler implements EventSubscriber
{
    public function getSubscribedEvents()
    {
        return array(
            "onFlush",
        );
    }

    public function onFlush(OnFlushEventArgs $args)
    {
        $em = $args->getEntityManager();
        $uow = $em->getUnitOfWork();

        $updates = $uow->getScheduledEntityUpdates();

        foreach ($updates as $entity) {

            if($entity instanceof UserInfo){

                $target_user_info = null;

                if($entity->getStatus() == "Single"){
                    if($entity->getRelationshipUser() != null){
                        $target_user_info = $entity->getRelationshipUser()->getUserInfo();
                        $target_user_info->setStatus("Single");
                        $target_user_info->setRelationshipUser(null);
                    }
                    $entity->setRelationshipUser(null);
                } else {
                    $target_user_info = $entity->getRelationshipUser()->getUserInfo();
                    $target_user_info->setStatus($entity->getStatus());
                    $target_user_info->setRelationshipUser($entity->getUser());
                }

                if($target_user_info != null){
                    $em->persist($target_user_info);
                    $uow->computeChangeSet($em->getClassMetadata(get_class($target_user_info)), $target_user_info);
                }
            }
        }
    }
}

Answer

Markus picture Markus · May 12, 2014

Doctrine UoW has a method propertyChanged(...) that tracks changes to entity properties. These changes are stored in the entityChangeSets-Property of the UoW.

I think it should be possible to call $uow->getEntityChangeSet($entity) which returns an array where keys are $entity's properties and values are [$oldValue, $newValue].

I'm not sure if you have to call computeChangeSet() for the old relationship user, but i hope you can figure this out and leave a comment?