[Solar-svn] Revision 2759

pmjones at solarphp.com pmjones at solarphp.com
Tue Sep 18 20:45:24 CDT 2007


branch: Solar_Sql_Model

* [BRK] Renamed insertRecord() to insert(), it now takes an array of data instead of a record, and returns the data array as inserted.  Serialize/unserialize occurs in this method now, instead of in the Record object.  This makes it behave a bit more like the previous Table class.  Note that validation (filters) are not applied here.

* [BRK] Renamed updateRecord() to update(), it now takes an array of data instead of a record, as well as a WHERE clause, and returns the data array as updated.  Serialize/unserialize occurs in this method now, instead of in the Record object.  This makes it behave a bit more like the previous Table class.  Note that validation (filters) are not applied here.

* [DEL] Removed method newFilterObject() and moved validation into the Record class.

* [BRK] Renamed method deleteRecord() to delete(), it now takes a WHERE clause instead of a record object.

* [ADD] Added method _unserializeCols() from the Record class load() method.





Modified: branches/orm/Solar/Sql/Model.php
===================================================================
--- branches/orm/Solar/Sql/Model.php	2007-09-19 01:30:25 UTC (rev 2758)
+++ branches/orm/Solar/Sql/Model.php	2007-09-19 01:45:24 UTC (rev 2759)
@@ -78,11 +78,6 @@
      * 
      * A Solar_Class_Stack object for fallback hierarchy.
      * 
-     * Used for finding and loading these classes:
-     * 
-     * - Filter
-     * - The proper Model for single-table inheritance
-     * 
      * @var Solar_Class_Stack
      * 
      */
@@ -137,10 +132,9 @@
      */
     protected $_filter_class = null;
     
-    
     // -----------------------------------------------------------------
     //
-    // Table definitions
+    // Table and index definition
     //
     // -----------------------------------------------------------------
     
@@ -187,6 +181,61 @@
     
     /**
      * 
+     * The index specification array for all indexes on this table.
+     * 
+     * Used only in auto-creation.
+     * 
+     * The array should be in this format ...
+     * 
+     * {{code: php
+     *     // the index type: 'normal' or 'unique'
+     *     $type = 'normal';
+     * 
+     *     // index on a single column:
+     *     // CREATE INDEX idx_name ON table_name (col_name)
+     *     $this->_indexes['idx_name'] = array(
+     *         'type' => $type,
+     *         'cols' => 'col_name'
+     *     );
+     * 
+     *     // index on multiple columns:
+     *     // CREATE INDEX idx_name ON table_name (col_1, col_2, ... col_N)
+     *     $this->_indexes['idx_name'] = array(
+     *         'type' => $type,
+     *         'cols' => array('col_1', 'col_2', ..., 'col_N')
+     *     );
+     * 
+     *     // easy shorthand for an index on a single column,
+     *     // giving the index the same name as the column:
+     *     // CREATE INDEX col_name ON table_name (col_name)
+     *     $this->_indexes['col_name'] = $type;
+     * }}
+     * 
+     * The $type may be 'normal' or 'unique'.
+     * 
+     * @var array
+     * 
+     */
+    protected $_indexes = array();
+    
+    // -----------------------------------------------------------------
+    //
+    // Special columns and column behaviors
+    //
+    // -----------------------------------------------------------------
+    
+    /**
+     * 
+     * A list of column names that don't exist in the table, but should be
+     * calculated by the model as-needed.
+     * 
+     * @var array
+     * 
+     */
+    protected $_calculate_cols = array();
+    
+    /**
+     * 
      * A list of column names that use sequence values.
      * 
      * When the column is present in a data array, but its value is null,
@@ -194,8 +243,6 @@
      * 
      * @var array
      * 
-     * 
-     * 
      */
     protected $_sequence_cols = array();
     
@@ -248,44 +295,11 @@
      */
     protected $_foreign_col = null;
     
-    /**
-     * 
-     * The index specification array for all indexes on this table.
-     * 
-     * Used only in auto-creation.
-     * 
-     * The array should be in this format ...
-     * 
-     * {{code: php
-     *     // the index type: 'normal' or 'unique'
-     *     $type = 'normal';
-     * 
-     *     // index on a single column:
-     *     // CREATE INDEX idx_name ON table_name (col_name)
-     *     $this->_indexes['idx_name'] = array(
-     *         'type' => $type,
-     *         'cols' => 'col_name'
-     *     );
-     * 
-     *     // index on multiple columns:
-     *     // CREATE INDEX idx_name ON table_name (col_1, col_2, ... col_N)
-     *     $this->_indexes['idx_name'] = array(
-     *         'type' => $type,
-     *         'cols' => array('col_1', 'col_2', ..., 'col_N')
-     *     );
-     * 
-     *     // easy shorthand for an index on a single column,
-     *     // giving the index the same name as the column:
-     *     // CREATE INDEX col_name ON table_name (col_name)
-     *     $this->_indexes['col_name'] = $type;
-     * }}
-     * 
-     * The $type may be 'normal' or 'unique'.
-     * 
-     * @var array
-     * 
-     */
-    protected $_indexes = array();
+    // -----------------------------------------------------------------
+    //
+    // Other/misc
+    //
+    // -----------------------------------------------------------------
     
     /**
      * 
@@ -306,6 +320,10 @@
      * Default is to use validate*() and sanitize*() methods in the filter
      * class, but if the method exists locally, it will be used instead.
      * 
+     * The filters apply only to Record objects from the model; if you use
+     * the model insert() and update() methods directly, the filters are not
+     * applied.
+     * 
      * Example usage follows; note that "_validate" and "_sanitize" refer
      * to internal (protected) filtering methods that have access to the
      * entire data set being filtered.
@@ -344,7 +362,7 @@
      * 
      * @see $_filter_class
      * 
-     * @see _applyFilters()
+     * @see _addFilter()
      * 
      */
     protected $_filters;
@@ -527,47 +545,6 @@
         $this->_paging = (int) $paging;
     }
     
-    /**
-     * 
-     * Returns a new filter object with the chain already prepared.
-     * 
-     * @return Solar_Sql_Model_Filter
-     * 
-     */
-    public function newFilterObject()
-    {
-        // factory the filter object
-        $obj = Solar::factory($this->_filter_class);
-        
-        // set element filters
-        foreach ($this->_filters as $key => $list) {
-            $obj->addChainFilters($key, $list);
-        }
-        
-        // set which elements are required
-        foreach ($this->_table_cols as $key => $info) {
-            if ($info['autoinc']) {
-                // autoinc are not required
-                $flag = false;
-            } elseif (in_array($key, $this->_sequence_cols)) {
-                // auto-sequence are not required
-                $flag = false;
-            } else {
-                // go with the col info
-                $flag = $info['require'];
-            }
-            
-            // set the requirement flag
-            $obj->setChainRequire($key, $flag);
-        }
-        
-        // tell the filter to use this object for locale strings
-        $obj->setChainLocaleObject($this);
-        
-        // done
-        return $obj;
-    }
-    
     // -----------------------------------------------------------------
     //
     // Fetch
@@ -1616,249 +1593,182 @@
     
     // -----------------------------------------------------------------
     //
-    // Insert, update, or delete a single record of the model.
+    // Insert, update, or delete rows in the model.
     //
     // -----------------------------------------------------------------
     
     /**
      * 
-     * Filters and inserts a Record into the table.
+     * Inserts one row to the model table.
      * 
-     * @param Solar_Sql_Model_Record $record The Record to insert.
+     * While this method automatically sets some values, and handles 
+     * serializing of columns where requested, it *does not* validate the
+     * data in any way.
      * 
-     * @return bool
+     * @param array|Solar_Struct $data The row data to insert.
      * 
+     * @return array The data as inserted, including auto-increment value if
+     * one is defined.
+     * 
      */
-    public function insertRecord($record)
+    public function insert($data)
     {
-        /**
-         * Auto-set timestamps and sequences
-         */
-        // needed for created/updated timestamps
-        $now = date('Y-m-d H:i:s');
+        // make sure we have an array
+        if ($data instanceof Solar_Struct) {
+            $data = $data->toArray();
+        } else {
+            $data = (array) $data;
+        }
         
+        // remove non-existent table columns from the data
+        foreach ($data as $key => $val) {
+            if (empty($this->_table_cols[$key])) {
+                unset($data[$key]);
+            }
+            
+            // remove empty autoinc columns to soothe postgres, which won't
+            // take explicit NULLs in SERIAL cols.
+            if ($this->_table_cols[$key]['autoinc'] && empty($val)) {
+                unset($data[$key]);
+            }
+        }
+        
         // force the 'created' value if there is a 'created' column
+        $now = date('Y-m-d H:i:s');
         $key = $this->_created_col;
         if ($key) {
-            $record->$key = $now;
+            $data[$key] = $now;
         }
         
-        // force the 'updated' value if there is an 'updated' column
+        // force the 'updated' value if there is an 'updated' column (same as
+        // the 'created' timestamp)
         $key = $this->_updated_col;
         if ($key) {
-            $record->$key = $now;
+            $data[$key] = $now;
         }
         
         // if inheritance is turned on, auto-set the inheritance value,
         // if not already set.
         $key = $this->_inherit_col;
-        if ($key && $this->_inherit_model && empty($record->$key)) {
-            $record->$key = $this->_inherit_model;
+        if ($key && $this->_inherit_model && empty($data[$key])) {
+            $data[$key] = $this->_inherit_model;
         }
         
-        // auto-add sequence values
+        // auto-set sequence values if needed
         foreach ($this->_sequence_cols as $key => $val) {
-            if (empty($record->$key)) {
+            if (empty($data[$key])) {
                 // no value given for the key.
                 // add a new sequence value.
-                $record->$key = $this->_sql->nextSequence($val);
+                $data[$key] = $this->_sql->nextSequence($val);
             }
         }
         
-        /**
-         * Validate, insert, and handle exceptions
-         */
-        
-        // filter the data (sanitize and validate)
-        if (! $record->validate()) {
-            return false;
-        }
-        
-        // convert to array for the SQL command
-        $data = $record->toArray();
-        
-        // remove some data elements before inserting
-        foreach ($data as $key => $val) {
-            // remove non-existent ("virtual") columns from the data, such as
-            // related fields.  
-            if (empty($this->_table_cols[$key])) {
-                unset($data[$key]);
-                // no need to process this key further, it doesn't exist
-                continue;
-            }
-            
-            // remove empty autoinc columns to soothe postgres, which won't
-            // take explicit NULLs in SERIAL cols.
-            if ($this->_table_cols[$key]['autoinc'] && empty($val)) {
-                unset($data[$key]);
-            }
-        }
-        
-        // apply serializing to the data elements as needed
+        // serialize cols and insert
         $this->_serializeCols($data);
+        $this->_sql->insert($this->_table_name, $data);
         
-        // attempt to insert the record, and catch SQL exceptions.
-        try {
-            // insert, which may cause an exception
-            $this->_sql->insert($this->_table_name, $data);
-            
-            // no exception, it worked!
-            $record->setStatus('inserted');
-            
-            // if there was an autoincrement column, set its value in the data.
-            foreach ($this->_table_cols as $key => $val) {
-                if ($val['autoinc']) {
-                    // set the value and leave the loop (only one autoinc
-                    // should be here anyway)
-                    $data[$key] = $this->_sql->lastInsertId($this->_table_name, $key);
-                    break;
-                }
+        // no exception thrown, so it must have worked.
+        // if there was an autoincrement column, set its value in the data.
+        foreach ($this->_table_cols as $key => $val) {
+            if ($val['autoinc']) {
+                // set the value and leave the loop (should be only one)
+                $data[$key] = $this->_sql->lastInsertId($this->_table_name, $key);
+                break;
             }
-            
-        } catch (Solar_Sql_Adapter_Exception_QueryFailed $e) {
-            // something went wrong at the database
-            $record->setStatus('invalid');
-            $record->setInvalid('*', $e->getInfo('pdo_text'));
         }
         
-        // load back to the record; this will automatically unserialize cols.
-        // @todo This does not reflect values from sql-based functions;
-        // would need to re-select from the DB to get those.
-        // note that we reload even if there was an SQL exception
-        $record->load($data);
-        
-        /**
-         * Done!  If clean, the save actually worked, otherwise it failed.
-         */
-        return $record->getStatus() == 'inserted';
+        // unserialize cols and return the data as inserted
+        $this->_unserializeCols($data);
+        return $data;
     }
     
     /**
      * 
-     * Filters and updates a Record in the table.
+     * Updates rows in the model table.
      * 
-     * @param Solar_Sql_Model_Record $record
+     * While this method automatically sets some values, and handles 
+     * serializing of columns where requested, it *does not* validate the
+     * data in any way.
      * 
-     * @return bool
+     * @param array|Solar_Struct $data The row data to insert.
      * 
+     * @param string|array $where The WHERE clause to identify which rows to 
+     * update.
+     * 
+     * @return array The data as updated.
+     * 
      */
-    public function updateRecord($record)
+    public function update($data, $where)
     {
-        /**
-         * Auto-set timestamps and sequences
-         */
-         
+        // make sure we have an array
+        if ($data instanceof Solar_Struct) {
+            $data = $data->toArray();
+        } else {
+            $data = (array) $data;
+        }
+        
+        // remove non-existent table columns from the data
+        foreach ($data as $key => $val) {
+            if (empty($this->_table_cols[$key])) {
+                unset($data[$key]);
+            }
+        }
+        
         // force the 'updated' value
         $key = $this->_updated_col;
         if ($key) {
-            $record->$key = date('Y-m-d H:i:s');
+            $data[$key] = date('Y-m-d H:i:s');
         }
         
         // if inheritance is turned on, auto-set the inheritance value,
         // if not already set.
         $key = $this->_inherit_col;
-        if ($key && $this->_inherit_model && empty($record->$key)) {
-            $record->$key = $this->_inherit_model;
+        if ($key && $this->_inherit_model && empty($this->$key)) {
+            $data[$key] = $this->_inherit_model;
         }
         
-        // auto-add sequence values
+        // auto-set sequences where keys exist and values are empty
         foreach ($this->_sequence_cols as $key => $val) {
-            if (empty($record->$key)) {
-                // key exists, but has no value.
-                // update with new sequence value.
-                $record->$key = $this->_sql->nextSequence($val);
+            if (array_key_exists($key, $data) && empty($data[$key])) {
+                // key is present but no value is given.
+                // add a new sequence value.
+                $data[$key] = $this->_sql->nextSequence($val);
             }
         }
         
-        /**
-         * Validate, update, and handle exceptions
-         */
-        
-        // filter the data (sanitize and validate)
-        if (! $record->validate($record)) {
-            return false;
-        }
-        
-        // convert to array for the SQL command
-        $data = $record->toArray();
-        
-        // remove non-existent ("virtual") columns from the data
-        foreach ($data as $key => $val) {
-            if (empty($this->_table_cols[$key])) {
-                unset($data[$key]);
-            }
-        }
-        
-        // serialize columns that need it
+        // serialize cols and do the update
         $this->_serializeCols($data);
+        $this->_sql->update($this->_table_name, $data, $where);
         
-        // remove primary-key column from the data and build a WHERE clause
-        $primary = $this->_primary_col;
-        unset($data[$primary]);
-        $where = array("$primary = ?" => $record->$primary);
-        
-        // attempt the update
-        try {
-            $this->_sql->update($this->_table_name, $data, $where);
-            $record->setStatus('updated');
-        } catch (Solar_Sql_Adapter_Exception_QueryFailed $e) {
-            // something went wrong at the database
-            $record->setStatus('invalid');
-            $record->setInvalid('*', $e->getInfo('pdo_text'));
-        }
-        
-        // load back to the record; this will automatically unserialize cols.
-        // 
-        // @todo This does not reflect values from sql-based functions;
-        // would need to re-select from the DB to get those.
-        $record->load($data);
-        
-        /**
-         * Done!
-         */
-        return $record->getStatus() == 'updated';
+        // unserialize cols and return the data as updated
+        $this->_unserializeCols($data);
+        return $data;
     }
     
     /**
      * 
-     * Deletes a record from the database.
+     * Deletes rows from the model table.
      * 
-     * Note that it does not delete any related values.
+     * @param string|array $where The WHERE clause to identify which rows to 
+     * delete.
      * 
-     * @param Solar_Sql_Model_Record A record object.
+     * @return array The data as updated.
      * 
-     * @return void
-     * 
-     * @todo Honor deletion cascades and/or null-setting, wrapped in a
-     * transaction for rollback.
-     * 
      */
-    public function deleteRecord($record)
+    public function delete($where)
     {
-        if ($record->getStatus() != 'new') {
-            $primary = $this->_primary_col;
-            $where = array("$primary = ?" => $record->$primary);
-            $this->_sql->delete($this->_table_name, $where);
-        }
-        
-        $record->setStatus('deleted');
+        return $this->_sql->delete($this->_table_name, $where);
     }
     
-    // -----------------------------------------------------------------
-    //
-    // Support methods
-    //
-    // -----------------------------------------------------------------
-    
     /**
      * 
-     * Serializes values in $this->_data based on $this->_serialize_cols.
+     * Serializes data values in-place based on $this->_serialize_cols.
      * 
      * Does not attempt to serialize null values.
      * 
      * If serializing fails, stores 'null' in the data.
      * 
-     * @param array $data Record data.
+     * @param array &$data Record data.
      * 
      * @return void
      * 
@@ -1878,8 +1788,40 @@
     
     /**
      * 
+     * Unerializes data values in-place based on $this->_serialize_cols.
+     * 
+     * Does not attempt to unserialize null values.
+     * 
+     * If unserializing fails, stores 'null' in the data.
+     * 
+     * @param array &$data Record data.
+     * 
+     * @return void
+     * 
+     */
+    protected function _unserializeCols(&$data)
+    {
+        // unseralize columns as-needed
+        foreach ($this->_serialize_cols as $key) {
+            // only unserialize if a non-empty string
+            if (! empty($data[$key]) && is_string($data[$key])) {
+                $data[$key] = unserialize($data[$key]);
+                if (! $data[$key]) {
+                    // unserializing failed
+                    $data[$key] = null;
+                }
+            }
+        }
+    }
+    
+    /**
+     * 
      * Adds a column filter.
      * 
+     * This can be a "real" (table) or "virtual" (calculate) column.
+     * 
+     * Remember, filters are applied only to Record object data.
+     * 
      * @param string $col The column name to filter.
      * 
      * @param string $method The filter method name, e.g. 'validateUnique'.
@@ -1892,7 +1834,7 @@
     protected function _addFilter($col, $method)
     {
         $args = func_get_args();
-        array_shift($args); // $col
+        array_shift($args); // the first param is $col
         $this->_filters[$col][] = $args;
     }
     




More information about the Solar-svn mailing list