Re: DB_DataObject - update only if changed

From: Date: Sat, 08 Mar 2003 11:24:55 +0000
Subject: Re: DB_DataObject - update only if changed
References: 1 2  Groups: php.pear.general php.pear.general 
Request: Send a blank email to pear-general+get-4110@lists.php.net to get a copy of this message
Thanks - I decided to overload the update() method - to only update changed items : $dataobject->get(132); $original = $dataobject; // clone/copy it.. $dataobject->setFrom($_POST); if ($dataobject->validate()) {
    $dataobject->update($original);
} // otherwise an error... as it was the simplest/ most flexible way to do it (eg. if you use methods other than just setFrom() it can still take advantage of it..) Is in CVS now -should come out with the next release sometime this week.. Regards Alan Geoff Hopson wrote:
Sure - here you go... Geoff in php file somewhere... define('DB_DATAOBJECT_DIRTY', 128); // dirty flag in class definition...
    /**
     * This is a merger between the setFrom and update methods, so that only the
     * attributes that have changed are actually updated, instead of the entire
     * attribute set.
     *
     *
     * @param    array | object  $from
     * @access   public
     * @return   boolean , true on success
     * @author      Geoff Hopson <ghopson@mac.com>
     */
    function updateFrom(&$from)
    {
        $keys  = $this->_get_keys();
        $items = $this->_get_table();
        if (!$items) {
            DB_DataObject::raiseError("updateFrom:Could not find table definition for {$this->__table}", DB_DATAOBJECT_ERROR_INVALIDCONFIG);
            return;
        }
        foreach ($items as $k => $v) {
            if (in_array($k,$keys)) {
                continue; // dont overwrite keys
            }
            if (!$k) {
                continue; // ignore empty keys!!! what
            }
            if (is_object($from) && @isset($from->$k)) {
                $this->$k = $from->$k;
                continue;
            }
            if (!@isset($from[$k])) {
                continue;
            }
            if (is_object($from[$k])) {
                continue;
            }
            if (is_array($from[$k])) {
                continue;
            }
            if ($this->$k != $from[$k]) {
                $this->$k = $from[$k];
                $items[$k] = $v + DB_DATAOBJECT_DIRTY;
            }
        }
        // now do the update...
        $datasaved = 1;
        $settings  = '';
        $this->_connect();
        $__DB  = &$GLOBALS['_DB_DATAOBJECT']['CONNECTIONS'][$this->_database_dsn_md5];
        foreach($items as $k => $v) {
            if ($v & DB_DATAOBJECT_DIRTY) {
                if (!isset($this->$k)) {
                    continue;
                }
                if ($settings)  {
                    $settings .= ', ';
                }
                /* special values ... at least null is handled...*/
                if ($this->$k === null) {
                    $settings .= "$k = NULL";
                }
                if ($v & DB_DATAOBJECT_STR) {
                    $settings .= $k .' = '. $__DB->quote($this->$k) . ' ';
                    continue;
                }
                if (is_numeric($this->$k)) {
                    $settings .= "$k = {$this->$k} ";
                    continue;
                }
                // at present we only cast to integers
                // - V2 may store additional data about float/int
                $settings .= "$k = " . intval($this->$k) . ' ';
            }
        }
        //$this->_condition=""; // dont clear condition
        if (!$GLOBALS['_DB_DATAOBJECT_PRODUCTION']) {
            $this->debug("got keys as ".serialize($keys),3);
        }
        $this->_build_condition($items,$keys);
        //  echo " $settings, $this->condition ";
        if ($settings && $this->_condition) {
            $r = $this->_query("UPDATE  {$this->__table}  SET {$settings} {$this->_condition} ");
            if (PEAR::isError($r)) {
                $this->raiseError($r);
                return false;
            }
            if ($r < 1) {
                DB_DataObject::raiseError('No Data Affected By update',DB_DATAOBJECT_ERROR_NOAFFECTEDROWS);
                return false;
            }
            $this->_clear_cache();
            return true;
        }
        //DB_DataObject::raiseError("updateFrom: No Data specifed for query $settings , {$this->_condition}", DB_DATAOBJECT_ERROR_NODATA);
        return true;
    }
On Thursday, March 6, 2003, at 10:16 am, Alan Knowles wrote:
Geoff Hopson wrote:
Hi, I have written a method called 'updateFrom', which is basically a merger between setFrom and updateFrom - I use the 'dirty' flag in the setFrom part and check the flag in the update part. Seems to work OK with my limited testing so far If anyone wants it, let me know - it's currently sitting in my subclass of DataObject so I don't corrupt the PEAR package.
can you send me it - I'll probably put it in the distribution under docs - or see how it could be integrated.. Regards Alan
Thanks for the response, Geoff On Wednesday, March 5, 2003, at 02:05 pm, Alan Knowles wrote:
Geoff Hopson wrote:
Hi, I'm playing around with the DB_DataObject stuff (very nice - thanks!), but have a requirement only to update columns that have been changed. Right now the update method seems to set all columns to the current attributes of the object. For example, I have a user object with username, password, name attributes. If I change only the name on a form, the update statement is update users set username='blah', password = 'blah', name = 'newblah' where usr_id = 6; I would like the statement to be: update users set name = 'newblah' where usr_id = 6; Basically, I would like something like setFrom to only set the attributes that have changed and to mark them as dirty somehow. First question: are there plans to do this Now on to my planned solution - all comments appreciated! In setFrom:
    - test the new value against the old value
    - if different, set the old value to the new value AND
        - set the $items so that the value for the attribute (eg DB_DATAOBJECT_STR) is bitwise-ANDed with a new value DB_DATABASE_DIRTY (which I have set to be 128)
I'd be tempted to add another method rather than overloading setFrom too much.. $object->get($someid); $copy = $object->__clone(); $object->setFrom($object); $object->unsetIdentical($copy); // new method??? $object->update(); if you simple unset the identical values (and ignore the keys) it shouldnt build the update with only the changed values... Regards Alan
in update():
    - rejig the query builder to test for the DB_DATABASE_DIRTY bit, and if the attribute is dirty, use it in the query
I'm playing with this right now - I can post the code if and when I get it working, but just wanted to ensure that I wasn't doing something stoopid (PHP novice...I'm allowed some mistakes :-)
Thanks for any input, Geoff -- Geoff Hopson Objectology Ltd. http://www.objectology.co.uk/
-- Geoff Hopson Objectology Ltd. http://www.objectology.co.uk/
-- Can you help out? Need Consulting Services or Know of a Job? http://www.akbkhome.com
-- Geoff Hopson Objectology Ltd. http://www.objectology.co.uk/


« previous php.pear.general (#4110) next »