[Solar-svn] Revision 2760

pmjones at solarphp.com pmjones at solarphp.com
Tue Sep 18 20:54:39 CDT 2007


branch: Solar_Sql_Model_Record

* [DEL] Method load() no longer has unserializing logic; placed in Model insert() and update() methods.

* [CHG] Method load() now sets data keys internally.

* [ADD] Added pre- and post- methods for save, validate, insert, update, and delete.

* [BRK] Instead of returning true/false from save(), throw exceptions when validation fails.

* [CHG] Method validate() now throws exceptions on validation failure.  Also, validation logic has been moved into this method from the Model class.





Modified: branches/orm/Solar/Sql/Model/Record.php
===================================================================
--- branches/orm/Solar/Sql/Model/Record.php	2007-09-19 01:45:24 UTC (rev 2759)
+++ branches/orm/Solar/Sql/Model/Record.php	2007-09-19 01:54:39 UTC (rev 2760)
@@ -175,8 +175,6 @@
             // use accessor method
             $method = $this->_access_methods['set'][$key];
             $this->$method($val);
-        } elseif ($key == $this->_model->primary_col) {
-            // disallow setting of primary keys; do nothing.
         } else {
             // no accessor method, not a primary key; assign directly.
             $this->_data[$key] = $val;
@@ -274,18 +272,6 @@
             }
         }
         
-        // unseralize columns as-needed
-        foreach ($this->_model->serialize_cols as $key) {
-            // only unserialize if a non-empty string
-            if (! empty($this->_data[$key]) && is_string($this->_data[$key])) {
-                $this->_data[$key] = unserialize($this->_data[$key]);
-                if (! $this->_data[$key]) {
-                    // unserializing failed
-                    $this->_data[$key] = null;
-                }
-            }
-        }
-        
         // load related data as records and collections
         foreach ($this->_model->related as $name => $opts) {
             
@@ -362,6 +348,15 @@
                     $this->_access_methods[$type][$var] = $method;
                 }
             }
+            
+            // put placeholders for each variable; these will be reset by
+            // the load() and/or __set() methods.  need to have this here
+            // because load() uses __set(), and primary keys will be ignored
+            // in that case, leaving the data key unset.  at the same time,
+            // we don't want to override values that are already present.
+            if (! isset($this->_data[$var])) {
+                $this->_data[$var] = null;
+            }
         }
     }
     
@@ -441,8 +436,21 @@
      * 
      * - How to handle one-to-many?
      * - How to handle many-to-many?
-     * - Use a transaction so we can roll back on related failures?
+     * - Use a transaction so we can roll back on failures in related records?
      * 
+     * Hook methods:
+     * 
+     * 1. `_preSave()` runs before all save operations.
+     * 
+     * 2. `_preInsert()` and `_preUpdate()` run before the insert or update.
+     * 
+     * 3. The record is validated, then inserted or updated.
+     * 
+     * 4. `_postInsert()` and `_postUpdate()` run after the insert or update.
+     * 
+     * 5. `_postSave()` runs after all save operations, but before related
+     *    records are saved.
+     * 
      * @param array $data An associative array of data to merge with existing
      * record data.
      * 
@@ -464,20 +472,19 @@
         // only save if we're not clean
         if ($this->_status != 'clean') {
             
+            // pre-save routine
+            $this->_preSave();
+            
             // insert or update based on primary key value
             $primary = $this->_model->primary_col;
             if (empty($this->$primary)) {
-                // no primary key: insert
-                $valid = $this->_model->insertRecord($this);
+                $this->_insert();
             } else {
-                // primary key exists: update
-                $valid = $this->_model->updateRecord($this);
+                $this->_update();
             }
             
-            // if not valid, return before saving related
-            if (! $valid) {
-                return $valid;
-            }
+            // post-save routine
+            $this->_postSave();
         }
         
         // now save each related, but only if instantiated
@@ -493,11 +500,95 @@
                 $this->$name->save();
             }
         }
+    }
+    
+    protected function _insert()
+    {
+        // filter the data (sanitize and validate)
+        $this->_preValidate();
+        $this->validate();
+        $this->_postValidate();
         
-        // done!
-        return true;
+        // convert to array for the SQL command
+        $data = $this->toArray();
+        
+        // attempt to insert the record, and catch SQL exceptions.
+        try {
+            // pre-logic, insert, reload, status, post-logic
+            $this->_preInsert();
+            $result = $this->_model->insert($data);
+            $this->load($result);
+            $this->setStatus('inserted');
+            $this->_postInsert();
+        } catch (Solar_Sql_Adapter_Exception_QueryFailed $e) {
+            // something went wrong at the database
+            $this->setStatus('invalid');
+            $this->setInvalid('*', $e->getInfo('pdo_text'));
+        }
     }
     
+    protected function _update()
+    {
+        // filter the data (sanitize and validate)
+        $this->_preValidate();
+        $this->validate();
+        $this->_postValidate();
+        
+        // convert to array for the SQL command
+        $data = $this->toArray();
+        
+        // remove primary-key column from the data and build a WHERE clause
+        $primary = $this->_model->primary_col;
+        unset($data[$primary]);
+        $where = array("$primary = ?" => $this->$primary);
+        
+        // attempt the update logic
+        try {
+            // pre-logic, insert, reload, status, post-logic
+            $this->_preUpdate();
+            $result = $this->_model->update($data, $where);
+            $this->load($result);
+            $this->setStatus('updated');
+            $this->_postUpdate();
+        } catch (Solar_Sql_Adapter_Exception_QueryFailed $e) {
+            // something went wrong at the database
+            $this->setStatus('invalid');
+            $this->setInvalid('*', $e->getInfo('pdo_text'));
+        }
+    }
+    
+    protected function _preSave()
+    {
+    }
+    
+    protected function _postSave()
+    {
+    }
+    
+    protected function _preInsert()
+    {
+    }
+    
+    protected function _postInsert()
+    {
+    }
+    
+    protected function _preUpdate()
+    {
+    }
+    
+    protected function _postUpdate()
+    {
+    }
+    
+    protected function _preValidate()
+    {
+    }
+    
+    protected function _postValidate()
+    {
+    }
+    
     /**
      * 
      * Deletes this record from the database.
@@ -507,7 +598,14 @@
      */
     public function delete()
     {
-        $this->_model->deleteRecord($this);
+        $this->_checkDeleted();
+        
+        $primary = $this->_model->primary_col;
+        $where = array("$primary = ?" => $this->$primary);
+        
+        $this->_preDelete();
+        $this->_model->delete($where);
+        $this->_postDelete();
     }
     
     /**
@@ -685,12 +783,40 @@
      */
     public function validate()
     {
-        $filter = $this->_model->newFilterObject();
+        // create a filter object based on the model's filter class
+        $filter = Solar::factory($this->_model->filter_class);
+        
+        // set filters as specified by the model
+        foreach ($this->_model->filters as $key => $list) {
+            $filter->addChainFilters($key, $list);
+        }
+        
+        // set which elements are required by the table itself
+        foreach ($this->_model->table_cols as $key => $info) {
+            if ($info['autoinc']) {
+                // autoinc are not required
+                $flag = false;
+            } elseif (in_array($key, $this->_model->sequence_cols)) {
+                // auto-sequence are not required
+                $flag = false;
+            } else {
+                // go with the col info
+                $flag = $info['require'];
+            }
+            
+            // set the requirement flag
+            $filter->setChainRequire($key, $flag);
+        }
+        
+        // tell the filter to use the model for locale strings
+        $filter->setChainLocaleObject($this->_model);
+        
+        // apply filters
         $valid = $filter->applyChain($this);
-        
         if (! $valid) {
             $this->_status = 'invalid';
             $this->_invalid = $filter->getChainInvalid();
+            throw $this->_exception('ERR_INVALID', array($this->_invalid));
         }
         
         return $valid;




More information about the Solar-svn mailing list