web/lib/Zend/Db/Table/Abstract.php
changeset 807 877f952ae2bd
parent 207 621fa6caec0c
child 1230 68c69c656a2c
--- a/web/lib/Zend/Db/Table/Abstract.php	Thu Mar 21 17:31:31 2013 +0100
+++ b/web/lib/Zend/Db/Table/Abstract.php	Thu Mar 21 19:50:53 2013 +0100
@@ -15,9 +15,9 @@
  * @category   Zend
  * @package    Zend_Db
  * @subpackage Table
- * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  * @license    http://framework.zend.com/license/new-bsd     New BSD License
- * @version    $Id: Abstract.php 21078 2010-02-18 18:07:16Z tech13 $
+ * @version    $Id: Abstract.php 24958 2012-06-15 13:44:04Z adamlundrigan $
  */
 
 /**
@@ -41,7 +41,7 @@
  * @category   Zend
  * @package    Zend_Db
  * @subpackage Table
- * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  * @license    http://framework.zend.com/license/new-bsd     New BSD License
  */
 abstract class Zend_Db_Table_Abstract
@@ -70,6 +70,7 @@
     const ON_UPDATE        = 'onUpdate';
 
     const CASCADE          = 'cascade';
+    const CASCADE_RECURSE  = 'cascadeRecurse';
     const RESTRICT         = 'restrict';
     const SET_NULL         = 'setNull';
 
@@ -744,6 +745,7 @@
      * Initialize database adapter.
      *
      * @return void
+     * @throws Zend_Db_Table_Exception
      */
     protected function _setupDatabaseAdapter()
     {
@@ -807,12 +809,23 @@
             //get db configuration
             $dbConfig = $this->_db->getConfig();
 
+            $port = isset($dbConfig['options']['port'])
+                  ? ':'.$dbConfig['options']['port']
+                  : (isset($dbConfig['port'])
+                  ? ':'.$dbConfig['port']
+                  : null);
+
+            $host = isset($dbConfig['options']['host'])
+                  ? ':'.$dbConfig['options']['host']
+                  : (isset($dbConfig['host'])
+                  ? ':'.$dbConfig['host']
+                  : null);
+
             // Define the cache identifier where the metadata are saved
             $cacheId = md5( // port:host/dbname:schema.table (based on availabilty)
-                (isset($dbConfig['options']['port']) ? ':'.$dbConfig['options']['port'] : null)
-                . (isset($dbConfig['options']['host']) ? ':'.$dbConfig['options']['host'] : null)
-                . '/'.$dbConfig['dbname'].':'.$this->_schema.'.'.$this->_name
-                );
+                    $port . $host . '/'. $dbConfig['dbname'] . ':'
+                  . $this->_schema. '.' . $this->_name
+            );
         }
 
         // If $this has no metadata cache or metadata cache misses
@@ -873,7 +886,7 @@
             // then throw an exception.
             if (empty($this->_primary)) {
                 require_once 'Zend/Db/Table/Exception.php';
-                throw new Zend_Db_Table_Exception('A table must have a primary key, but none was found');
+                throw new Zend_Db_Table_Exception("A table must have a primary key, but none was found for table '{$this->_name}'");
             }
         } else if (!is_array($this->_primary)) {
             $this->_primary = array(1 => $this->_primary);
@@ -961,8 +974,9 @@
      * You can elect to return only a part of this information by supplying its key name,
      * otherwise all information is returned as an array.
      *
-     * @param  $key The specific info part to return OPTIONAL
+     * @param  string $key The specific info part to return OPTIONAL
      * @return mixed
+     * @throws Zend_Db_Table_Exception
      */
     public function info($key = null)
     {
@@ -1035,14 +1049,24 @@
          */
         if (is_string($this->_sequence) && !isset($data[$pkIdentity])) {
             $data[$pkIdentity] = $this->_db->nextSequenceId($this->_sequence);
+            $pkSuppliedBySequence = true;
         }
 
         /**
          * If the primary key can be generated automatically, and no value was
          * specified in the user-supplied data, then omit it from the tuple.
+         *
+         * Note: this checks for sensible values in the supplied primary key
+         * position of the data.  The following values are considered empty:
+         *   null, false, true, '', array()
          */
-        if (array_key_exists($pkIdentity, $data) && $data[$pkIdentity] === null) {
-            unset($data[$pkIdentity]);
+        if (!isset($pkSuppliedBySequence) && array_key_exists($pkIdentity, $data)) {
+            if ($data[$pkIdentity] === null                                        // null
+                || $data[$pkIdentity] === ''                                       // empty string
+                || is_bool($data[$pkIdentity])                                     // boolean
+                || (is_array($data[$pkIdentity]) && empty($data[$pkIdentity]))) {  // empty array
+                unset($data[$pkIdentity]);
+            }
         }
 
         /**
@@ -1157,6 +1181,22 @@
      */
     public function delete($where)
     {
+        $depTables = $this->getDependentTables();
+        if (!empty($depTables)) {
+            $resultSet = $this->fetchAll($where);
+            if (count($resultSet) > 0 ) {
+                foreach ($resultSet as $row) {
+                    /**
+                     * Execute cascading deletes against dependent tables
+                     */
+                    foreach ($depTables as $tableClass) {
+                        $t = self::getTableFromString($tableClass, $this);
+                        $t->_cascadeDelete($tableClass, $row->getPrimaryKey());
+                    }
+                }
+            }
+        }
+
         $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name;
         return $this->_db->delete($tableSpec, $where);
     }
@@ -1170,27 +1210,56 @@
      */
     public function _cascadeDelete($parentTableClassname, array $primaryKey)
     {
+        // setup metadata
         $this->_setupMetadata();
+        
+        // get this class name
+        $thisClass = get_class($this);
+        if ($thisClass === 'Zend_Db_Table') {
+            $thisClass = $this->_definitionConfigName;
+        }
+        
         $rowsAffected = 0;
+        
         foreach ($this->_getReferenceMapNormalized() as $map) {
             if ($map[self::REF_TABLE_CLASS] == $parentTableClassname && isset($map[self::ON_DELETE])) {
-                switch ($map[self::ON_DELETE]) {
-                    case self::CASCADE:
-                        $where = array();
-                        for ($i = 0; $i < count($map[self::COLUMNS]); ++$i) {
-                            $col = $this->_db->foldCase($map[self::COLUMNS][$i]);
-                            $refCol = $this->_db->foldCase($map[self::REF_COLUMNS][$i]);
-                            $type = $this->_metadata[$col]['DATA_TYPE'];
-                            $where[] = $this->_db->quoteInto(
-                                $this->_db->quoteIdentifier($col, true) . ' = ?',
-                                $primaryKey[$refCol], $type);
+                
+                $where = array();
+                
+                // CASCADE or CASCADE_RECURSE
+                if (in_array($map[self::ON_DELETE], array(self::CASCADE, self::CASCADE_RECURSE))) {
+                    for ($i = 0; $i < count($map[self::COLUMNS]); ++$i) {
+                        $col = $this->_db->foldCase($map[self::COLUMNS][$i]);
+                        $refCol = $this->_db->foldCase($map[self::REF_COLUMNS][$i]);
+                        $type = $this->_metadata[$col]['DATA_TYPE'];
+                        $where[] = $this->_db->quoteInto(
+                            $this->_db->quoteIdentifier($col, true) . ' = ?',
+                            $primaryKey[$refCol], $type);
+                    }
+                }
+                
+                // CASCADE_RECURSE
+                if ($map[self::ON_DELETE] == self::CASCADE_RECURSE) {
+                    
+                    /**
+                     * Execute cascading deletes against dependent tables
+                     */
+                    $depTables = $this->getDependentTables();
+                    if (!empty($depTables)) {
+                        foreach ($depTables as $tableClass) {
+                            $t = self::getTableFromString($tableClass, $this);
+                            foreach ($this->fetchAll($where) as $depRow) {
+                                $rowsAffected += $t->_cascadeDelete($thisClass, $depRow->getPrimaryKey());
+                            }
                         }
-                        $rowsAffected += $this->delete($where);
-                        break;
-                    default:
-                        // no action
-                        break;
+                    }
                 }
+
+                // CASCADE or CASCADE_RECURSE
+                if (in_array($map[self::ON_DELETE], array(self::CASCADE, self::CASCADE_RECURSE))) {
+                    $rowsAffected += $this->delete($where);
+                }
+                
             }
         }
         return $rowsAffected;
@@ -1342,10 +1411,11 @@
      *
      * @param string|array|Zend_Db_Table_Select $where  OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object.
      * @param string|array                      $order  OPTIONAL An SQL ORDER clause.
+     * @param int                               $offset OPTIONAL An SQL OFFSET value.
      * @return Zend_Db_Table_Row_Abstract|null The row results per the
      *     Zend_Db_Adapter fetch mode, or null if no row found.
      */
-    public function fetchRow($where = null, $order = null)
+    public function fetchRow($where = null, $order = null, $offset = null)
     {
         if (!($where instanceof Zend_Db_Table_Select)) {
             $select = $this->select();
@@ -1358,10 +1428,10 @@
                 $this->_order($select, $order);
             }
 
-            $select->limit(1);
+            $select->limit(1, ((is_numeric($offset)) ? (int) $offset : null));
 
         } else {
-            $select = $where->limit(1);
+            $select = $where->limit(1, $where->getPart(Zend_Db_Select::LIMIT_OFFSET));
         }
 
         $rows = $this->_fetch($select);
@@ -1507,4 +1577,38 @@
         return $data;
     }
 
+    public static function getTableFromString($tableName, Zend_Db_Table_Abstract $referenceTable = null)
+    {
+        if ($referenceTable instanceof Zend_Db_Table_Abstract) {
+            $tableDefinition = $referenceTable->getDefinition();
+
+            if ($tableDefinition !== null && $tableDefinition->hasTableConfig($tableName)) {
+                return new Zend_Db_Table($tableName, $tableDefinition);
+            }
+        }
+
+        // assume the tableName is the class name
+        if (!class_exists($tableName)) {
+            try {
+                require_once 'Zend/Loader.php';
+                Zend_Loader::loadClass($tableName);
+            } catch (Zend_Exception $e) {
+                require_once 'Zend/Db/Table/Row/Exception.php';
+                throw new Zend_Db_Table_Row_Exception($e->getMessage(), $e->getCode(), $e);
+            }
+        }
+
+        $options = array();
+
+        if ($referenceTable instanceof Zend_Db_Table_Abstract) {
+            $options['db'] = $referenceTable->getAdapter();
+        }
+
+        if (isset($tableDefinition) && $tableDefinition !== null) {
+            $options[Zend_Db_Table_Abstract::DEFINITION] = $tableDefinition;
+        }
+
+        return new $tableName($options);
+    }
+    
 }