[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