Doctrine 2 - ORM
  1. Doctrine 2 - ORM
  2. DDC-280

UnitOfWork changeSet population should take advantage of Comparable technique

    Details

    • Type: Improvement Improvement
    • Status: Open
    • Priority: Minor Minor
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: ORM
    • Security Level: All
    • Labels:
      None

      Description

      Currently our UnitOfWork computes the changeset by checking actual instances of Objects.

      Unable to find source-code formatter for language: php. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml
      // UnitOfWork, lines 501-507 
      
                  foreach ($actualData as $propName => $actualValue) { 
                      $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; 
                      if (is_object($orgValue) && $orgValue !== $actualValue) { 
                          $changeSet[$propName] = array($orgValue, $actualValue); 
                      } else if ($orgValue != $actualValue || ($orgValue === null ^ $actualValue === null))  
      	                    $changeSet[$propName] = array($orgValue, $actualValue);
                      } 
      

      While this is ok when you do new object assignments, it just bypass same instances of same object, since the hash is the same.
      A user on IRC (post-o-matic) has a quite complex object logic that he would like to avoid clone and even instantiate another class.
      I agree with him that cloning is not the ideal technique, mainly because the changeset would always compute the object (since then hashs would be different).

      He implemented this datatype:

      Unable to find source-code formatter for language: php. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml
      class EffortGraphType extends Type 
      { 
          public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform) 
          { 
              return $platform->getClobTypeDeclarationSql($fieldDeclaration); 
          } 
      
          public function convertToPHPValue($value, AbstractPlatform $platform) 
          { 
              return new EffortGraph(unserialize($value)); 
          } 
      
          public function convertToDatabaseValue($value, AbstractPlatform $platform) 
          { 
              return serialize($value->getGraphPoints()); 
          } 
      
          public function getName() 
          { 
              return 'effort_graph'; 
          } 
      }
      

      I was thinking in a possible alternative and it came up to me the same basic idea we have with operators overloading OR Comparable interface of Java. I know in Java it supports way more things, but at least for this situation (as a start point) it would make developer's life easier.

      Basic idea is to have an interface in Doctrine\Common:

      Unable to find source-code formatter for language: php. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml
      namespace Doctrine\Common;
      
      interface Comparable
      {
          public function compareTo($value);
      }
      

      And update our UnitOfWork to take advantage of it:

      Unable to find source-code formatter for language: php. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml
      // UnitOfWork, lines 501-507 
      
                  foreach ($actualData as $propName => $actualValue) { 
                      $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; 
      
                      if (is_object($orgValue)) { 
                          $isDiff = ($orgValue instanceof Doctrine\Common\Comparable) 
                              ? $orgValue->compareTo($actualValue) :  ($orgValue !== $actualValue);
      
                          if ($isDiff) {
                              $changeSet[$propName] = array($orgValue, $actualValue); 
                          }
                      } else if ($orgValue != $actualValue || ($orgValue === null ^ $actualValue === null))  
      	                    $changeSet[$propName] = array($orgValue, $actualValue);
                      } 
      

      In this user's usecase, it'd require him to update the EffortGraph class and implement Comparable interface.
      For his specific situation, he'd need to store original value and updated value, just like we do internally in UnitOfWork for Entities.

        Activity

        Hide
        Guilherme Blanco added a comment -

        What's the final status of this?

        IMHO this should be incorporated, since it adds a powerful support that users can take advantage in our Types.

        Cheers,

        Show
        Guilherme Blanco added a comment - What's the final status of this? IMHO this should be incorporated, since it adds a powerful support that users can take advantage in our Types. Cheers,
        Hide
        Guilherme Blanco added a comment -

        You're the main guy that can give a final word in this subject.

        I'm +1 for this

        Show
        Guilherme Blanco added a comment - You're the main guy that can give a final word in this subject. I'm +1 for this
        Hide
        Robert added a comment -

        +1 for this...

        if you have datetimes in a table and are using the DateTime object, you end up with useless update queries, if you persist an unchanged object...

        Show
        Robert added a comment - +1 for this... if you have datetimes in a table and are using the DateTime object, you end up with useless update queries, if you persist an unchanged object...
        Hide
        Benjamin Eberlei added a comment -

        That is not true.

        Show
        Benjamin Eberlei added a comment - That is not true.
        Hide
        Robert added a comment - - edited

        Sorry, I wasn't clear.

        It does not happen in all cases.

        I have a simple object, that I save to the session. After merging it and flushing the entitiy manager, an update query is generated, which sets all the datetime fields of the object to their current value:

        Unable to find source-code formatter for language: php. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml
        $item = $_SESSION['item'];
        $item = $this->_em->merge($item);
        $this->_em->persist($item);
        $this->_em->flush();
        

        This code results in the expected SELECT query, which refreshes the item from DB, but it also results in an update query which sets the datetime of the object to the same value.
        This query could be omitted, if I could use a comparable interface and a custom type for datetimes, which implement it.

        Show
        Robert added a comment - - edited Sorry, I wasn't clear. It does not happen in all cases. I have a simple object, that I save to the session. After merging it and flushing the entitiy manager, an update query is generated, which sets all the datetime fields of the object to their current value: Unable to find source-code formatter for language: php. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml $item = $_SESSION['item']; $item = $ this ->_em->merge($item); $ this ->_em->persist($item); $ this ->_em->flush(); This code results in the expected SELECT query, which refreshes the item from DB, but it also results in an update query which sets the datetime of the object to the same value. This query could be omitted, if I could use a comparable interface and a custom type for datetimes, which implement it.
        Hide
        Benjamin Eberlei added a comment -

        This rather seems like a bug with the merging. Can you open up a new ticket describing this? Thank you

        Show
        Benjamin Eberlei added a comment - This rather seems like a bug with the merging. Can you open up a new ticket describing this? Thank you

          People

          • Assignee:
            Roman S. Borschel
            Reporter:
            Guilherme Blanco
          • Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated: