13 * to license@zend.com so we can send you a copy immediately. |
13 * to license@zend.com so we can send you a copy immediately. |
14 * |
14 * |
15 * @category Zend |
15 * @category Zend |
16 * @package Zend_Db |
16 * @package Zend_Db |
17 * @subpackage Table |
17 * @subpackage Table |
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
18 * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) |
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
20 * @version $Id: Abstract.php 21078 2010-02-18 18:07:16Z tech13 $ |
20 * @version $Id: Abstract.php 24958 2012-06-15 13:44:04Z adamlundrigan $ |
21 */ |
21 */ |
22 |
22 |
23 /** |
23 /** |
24 * @see Zend_Db_Adapter_Abstract |
24 * @see Zend_Db_Adapter_Abstract |
25 */ |
25 */ |
68 const REF_COLUMNS = 'refColumns'; |
68 const REF_COLUMNS = 'refColumns'; |
69 const ON_DELETE = 'onDelete'; |
69 const ON_DELETE = 'onDelete'; |
70 const ON_UPDATE = 'onUpdate'; |
70 const ON_UPDATE = 'onUpdate'; |
71 |
71 |
72 const CASCADE = 'cascade'; |
72 const CASCADE = 'cascade'; |
|
73 const CASCADE_RECURSE = 'cascadeRecurse'; |
73 const RESTRICT = 'restrict'; |
74 const RESTRICT = 'restrict'; |
74 const SET_NULL = 'setNull'; |
75 const SET_NULL = 'setNull'; |
75 |
76 |
76 const DEFAULT_NONE = 'defaultNone'; |
77 const DEFAULT_NONE = 'defaultNone'; |
77 const DEFAULT_CLASS = 'defaultClass'; |
78 const DEFAULT_CLASS = 'defaultClass'; |
805 // Define the cache identifier where the metadata are saved |
807 // Define the cache identifier where the metadata are saved |
806 |
808 |
807 //get db configuration |
809 //get db configuration |
808 $dbConfig = $this->_db->getConfig(); |
810 $dbConfig = $this->_db->getConfig(); |
809 |
811 |
|
812 $port = isset($dbConfig['options']['port']) |
|
813 ? ':'.$dbConfig['options']['port'] |
|
814 : (isset($dbConfig['port']) |
|
815 ? ':'.$dbConfig['port'] |
|
816 : null); |
|
817 |
|
818 $host = isset($dbConfig['options']['host']) |
|
819 ? ':'.$dbConfig['options']['host'] |
|
820 : (isset($dbConfig['host']) |
|
821 ? ':'.$dbConfig['host'] |
|
822 : null); |
|
823 |
810 // Define the cache identifier where the metadata are saved |
824 // Define the cache identifier where the metadata are saved |
811 $cacheId = md5( // port:host/dbname:schema.table (based on availabilty) |
825 $cacheId = md5( // port:host/dbname:schema.table (based on availabilty) |
812 (isset($dbConfig['options']['port']) ? ':'.$dbConfig['options']['port'] : null) |
826 $port . $host . '/'. $dbConfig['dbname'] . ':' |
813 . (isset($dbConfig['options']['host']) ? ':'.$dbConfig['options']['host'] : null) |
827 . $this->_schema. '.' . $this->_name |
814 . '/'.$dbConfig['dbname'].':'.$this->_schema.'.'.$this->_name |
828 ); |
815 ); |
|
816 } |
829 } |
817 |
830 |
818 // If $this has no metadata cache or metadata cache misses |
831 // If $this has no metadata cache or metadata cache misses |
819 if (null === $this->_metadataCache || !($metadata = $this->_metadataCache->load($cacheId))) { |
832 if (null === $this->_metadataCache || !($metadata = $this->_metadataCache->load($cacheId))) { |
820 // Metadata are not loaded from cache |
833 // Metadata are not loaded from cache |
871 } |
884 } |
872 // if no primary key was specified and none was found in the metadata |
885 // if no primary key was specified and none was found in the metadata |
873 // then throw an exception. |
886 // then throw an exception. |
874 if (empty($this->_primary)) { |
887 if (empty($this->_primary)) { |
875 require_once 'Zend/Db/Table/Exception.php'; |
888 require_once 'Zend/Db/Table/Exception.php'; |
876 throw new Zend_Db_Table_Exception('A table must have a primary key, but none was found'); |
889 throw new Zend_Db_Table_Exception("A table must have a primary key, but none was found for table '{$this->_name}'"); |
877 } |
890 } |
878 } else if (!is_array($this->_primary)) { |
891 } else if (!is_array($this->_primary)) { |
879 $this->_primary = array(1 => $this->_primary); |
892 $this->_primary = array(1 => $this->_primary); |
880 } else if (isset($this->_primary[0])) { |
893 } else if (isset($this->_primary[0])) { |
881 array_unshift($this->_primary, null); |
894 array_unshift($this->_primary, null); |
959 * Returns table information. |
972 * Returns table information. |
960 * |
973 * |
961 * You can elect to return only a part of this information by supplying its key name, |
974 * You can elect to return only a part of this information by supplying its key name, |
962 * otherwise all information is returned as an array. |
975 * otherwise all information is returned as an array. |
963 * |
976 * |
964 * @param $key The specific info part to return OPTIONAL |
977 * @param string $key The specific info part to return OPTIONAL |
965 * @return mixed |
978 * @return mixed |
|
979 * @throws Zend_Db_Table_Exception |
966 */ |
980 */ |
967 public function info($key = null) |
981 public function info($key = null) |
968 { |
982 { |
969 $this->_setupPrimaryKey(); |
983 $this->_setupPrimaryKey(); |
970 |
984 |
1033 * to the row. We assume that only the first column in a compound |
1047 * to the row. We assume that only the first column in a compound |
1034 * primary key takes a value from a sequence. |
1048 * primary key takes a value from a sequence. |
1035 */ |
1049 */ |
1036 if (is_string($this->_sequence) && !isset($data[$pkIdentity])) { |
1050 if (is_string($this->_sequence) && !isset($data[$pkIdentity])) { |
1037 $data[$pkIdentity] = $this->_db->nextSequenceId($this->_sequence); |
1051 $data[$pkIdentity] = $this->_db->nextSequenceId($this->_sequence); |
|
1052 $pkSuppliedBySequence = true; |
1038 } |
1053 } |
1039 |
1054 |
1040 /** |
1055 /** |
1041 * If the primary key can be generated automatically, and no value was |
1056 * If the primary key can be generated automatically, and no value was |
1042 * specified in the user-supplied data, then omit it from the tuple. |
1057 * specified in the user-supplied data, then omit it from the tuple. |
|
1058 * |
|
1059 * Note: this checks for sensible values in the supplied primary key |
|
1060 * position of the data. The following values are considered empty: |
|
1061 * null, false, true, '', array() |
1043 */ |
1062 */ |
1044 if (array_key_exists($pkIdentity, $data) && $data[$pkIdentity] === null) { |
1063 if (!isset($pkSuppliedBySequence) && array_key_exists($pkIdentity, $data)) { |
1045 unset($data[$pkIdentity]); |
1064 if ($data[$pkIdentity] === null // null |
|
1065 || $data[$pkIdentity] === '' // empty string |
|
1066 || is_bool($data[$pkIdentity]) // boolean |
|
1067 || (is_array($data[$pkIdentity]) && empty($data[$pkIdentity]))) { // empty array |
|
1068 unset($data[$pkIdentity]); |
|
1069 } |
1046 } |
1070 } |
1047 |
1071 |
1048 /** |
1072 /** |
1049 * INSERT the new row. |
1073 * INSERT the new row. |
1050 */ |
1074 */ |
1155 * @param array|string $where SQL WHERE clause(s). |
1179 * @param array|string $where SQL WHERE clause(s). |
1156 * @return int The number of rows deleted. |
1180 * @return int The number of rows deleted. |
1157 */ |
1181 */ |
1158 public function delete($where) |
1182 public function delete($where) |
1159 { |
1183 { |
|
1184 $depTables = $this->getDependentTables(); |
|
1185 if (!empty($depTables)) { |
|
1186 $resultSet = $this->fetchAll($where); |
|
1187 if (count($resultSet) > 0 ) { |
|
1188 foreach ($resultSet as $row) { |
|
1189 /** |
|
1190 * Execute cascading deletes against dependent tables |
|
1191 */ |
|
1192 foreach ($depTables as $tableClass) { |
|
1193 $t = self::getTableFromString($tableClass, $this); |
|
1194 $t->_cascadeDelete($tableClass, $row->getPrimaryKey()); |
|
1195 } |
|
1196 } |
|
1197 } |
|
1198 } |
|
1199 |
1160 $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name; |
1200 $tableSpec = ($this->_schema ? $this->_schema . '.' : '') . $this->_name; |
1161 return $this->_db->delete($tableSpec, $where); |
1201 return $this->_db->delete($tableSpec, $where); |
1162 } |
1202 } |
1163 |
1203 |
1164 /** |
1204 /** |
1168 * @param array $primaryKey |
1208 * @param array $primaryKey |
1169 * @return int Number of affected rows |
1209 * @return int Number of affected rows |
1170 */ |
1210 */ |
1171 public function _cascadeDelete($parentTableClassname, array $primaryKey) |
1211 public function _cascadeDelete($parentTableClassname, array $primaryKey) |
1172 { |
1212 { |
|
1213 // setup metadata |
1173 $this->_setupMetadata(); |
1214 $this->_setupMetadata(); |
|
1215 |
|
1216 // get this class name |
|
1217 $thisClass = get_class($this); |
|
1218 if ($thisClass === 'Zend_Db_Table') { |
|
1219 $thisClass = $this->_definitionConfigName; |
|
1220 } |
|
1221 |
1174 $rowsAffected = 0; |
1222 $rowsAffected = 0; |
|
1223 |
1175 foreach ($this->_getReferenceMapNormalized() as $map) { |
1224 foreach ($this->_getReferenceMapNormalized() as $map) { |
1176 if ($map[self::REF_TABLE_CLASS] == $parentTableClassname && isset($map[self::ON_DELETE])) { |
1225 if ($map[self::REF_TABLE_CLASS] == $parentTableClassname && isset($map[self::ON_DELETE])) { |
1177 switch ($map[self::ON_DELETE]) { |
1226 |
1178 case self::CASCADE: |
1227 $where = array(); |
1179 $where = array(); |
1228 |
1180 for ($i = 0; $i < count($map[self::COLUMNS]); ++$i) { |
1229 // CASCADE or CASCADE_RECURSE |
1181 $col = $this->_db->foldCase($map[self::COLUMNS][$i]); |
1230 if (in_array($map[self::ON_DELETE], array(self::CASCADE, self::CASCADE_RECURSE))) { |
1182 $refCol = $this->_db->foldCase($map[self::REF_COLUMNS][$i]); |
1231 for ($i = 0; $i < count($map[self::COLUMNS]); ++$i) { |
1183 $type = $this->_metadata[$col]['DATA_TYPE']; |
1232 $col = $this->_db->foldCase($map[self::COLUMNS][$i]); |
1184 $where[] = $this->_db->quoteInto( |
1233 $refCol = $this->_db->foldCase($map[self::REF_COLUMNS][$i]); |
1185 $this->_db->quoteIdentifier($col, true) . ' = ?', |
1234 $type = $this->_metadata[$col]['DATA_TYPE']; |
1186 $primaryKey[$refCol], $type); |
1235 $where[] = $this->_db->quoteInto( |
|
1236 $this->_db->quoteIdentifier($col, true) . ' = ?', |
|
1237 $primaryKey[$refCol], $type); |
|
1238 } |
|
1239 } |
|
1240 |
|
1241 // CASCADE_RECURSE |
|
1242 if ($map[self::ON_DELETE] == self::CASCADE_RECURSE) { |
|
1243 |
|
1244 /** |
|
1245 * Execute cascading deletes against dependent tables |
|
1246 */ |
|
1247 $depTables = $this->getDependentTables(); |
|
1248 if (!empty($depTables)) { |
|
1249 foreach ($depTables as $tableClass) { |
|
1250 $t = self::getTableFromString($tableClass, $this); |
|
1251 foreach ($this->fetchAll($where) as $depRow) { |
|
1252 $rowsAffected += $t->_cascadeDelete($thisClass, $depRow->getPrimaryKey()); |
|
1253 } |
1187 } |
1254 } |
1188 $rowsAffected += $this->delete($where); |
1255 } |
1189 break; |
|
1190 default: |
|
1191 // no action |
|
1192 break; |
|
1193 } |
1256 } |
|
1257 |
|
1258 // CASCADE or CASCADE_RECURSE |
|
1259 if (in_array($map[self::ON_DELETE], array(self::CASCADE, self::CASCADE_RECURSE))) { |
|
1260 $rowsAffected += $this->delete($where); |
|
1261 } |
|
1262 |
1194 } |
1263 } |
1195 } |
1264 } |
1196 return $rowsAffected; |
1265 return $rowsAffected; |
1197 } |
1266 } |
1198 |
1267 |
1340 * Fetches one row in an object of type Zend_Db_Table_Row_Abstract, |
1409 * Fetches one row in an object of type Zend_Db_Table_Row_Abstract, |
1341 * or returns null if no row matches the specified criteria. |
1410 * or returns null if no row matches the specified criteria. |
1342 * |
1411 * |
1343 * @param string|array|Zend_Db_Table_Select $where OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object. |
1412 * @param string|array|Zend_Db_Table_Select $where OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object. |
1344 * @param string|array $order OPTIONAL An SQL ORDER clause. |
1413 * @param string|array $order OPTIONAL An SQL ORDER clause. |
|
1414 * @param int $offset OPTIONAL An SQL OFFSET value. |
1345 * @return Zend_Db_Table_Row_Abstract|null The row results per the |
1415 * @return Zend_Db_Table_Row_Abstract|null The row results per the |
1346 * Zend_Db_Adapter fetch mode, or null if no row found. |
1416 * Zend_Db_Adapter fetch mode, or null if no row found. |
1347 */ |
1417 */ |
1348 public function fetchRow($where = null, $order = null) |
1418 public function fetchRow($where = null, $order = null, $offset = null) |
1349 { |
1419 { |
1350 if (!($where instanceof Zend_Db_Table_Select)) { |
1420 if (!($where instanceof Zend_Db_Table_Select)) { |
1351 $select = $this->select(); |
1421 $select = $this->select(); |
1352 |
1422 |
1353 if ($where !== null) { |
1423 if ($where !== null) { |
1356 |
1426 |
1357 if ($order !== null) { |
1427 if ($order !== null) { |
1358 $this->_order($select, $order); |
1428 $this->_order($select, $order); |
1359 } |
1429 } |
1360 |
1430 |
1361 $select->limit(1); |
1431 $select->limit(1, ((is_numeric($offset)) ? (int) $offset : null)); |
1362 |
1432 |
1363 } else { |
1433 } else { |
1364 $select = $where->limit(1); |
1434 $select = $where->limit(1, $where->getPart(Zend_Db_Select::LIMIT_OFFSET)); |
1365 } |
1435 } |
1366 |
1436 |
1367 $rows = $this->_fetch($select); |
1437 $rows = $this->_fetch($select); |
1368 |
1438 |
1369 if (count($rows) == 0) { |
1439 if (count($rows) == 0) { |
1505 $stmt = $this->_db->query($select); |
1575 $stmt = $this->_db->query($select); |
1506 $data = $stmt->fetchAll(Zend_Db::FETCH_ASSOC); |
1576 $data = $stmt->fetchAll(Zend_Db::FETCH_ASSOC); |
1507 return $data; |
1577 return $data; |
1508 } |
1578 } |
1509 |
1579 |
|
1580 public static function getTableFromString($tableName, Zend_Db_Table_Abstract $referenceTable = null) |
|
1581 { |
|
1582 if ($referenceTable instanceof Zend_Db_Table_Abstract) { |
|
1583 $tableDefinition = $referenceTable->getDefinition(); |
|
1584 |
|
1585 if ($tableDefinition !== null && $tableDefinition->hasTableConfig($tableName)) { |
|
1586 return new Zend_Db_Table($tableName, $tableDefinition); |
|
1587 } |
|
1588 } |
|
1589 |
|
1590 // assume the tableName is the class name |
|
1591 if (!class_exists($tableName)) { |
|
1592 try { |
|
1593 require_once 'Zend/Loader.php'; |
|
1594 Zend_Loader::loadClass($tableName); |
|
1595 } catch (Zend_Exception $e) { |
|
1596 require_once 'Zend/Db/Table/Row/Exception.php'; |
|
1597 throw new Zend_Db_Table_Row_Exception($e->getMessage(), $e->getCode(), $e); |
|
1598 } |
|
1599 } |
|
1600 |
|
1601 $options = array(); |
|
1602 |
|
1603 if ($referenceTable instanceof Zend_Db_Table_Abstract) { |
|
1604 $options['db'] = $referenceTable->getAdapter(); |
|
1605 } |
|
1606 |
|
1607 if (isset($tableDefinition) && $tableDefinition !== null) { |
|
1608 $options[Zend_Db_Table_Abstract::DEFINITION] = $tableDefinition; |
|
1609 } |
|
1610 |
|
1611 return new $tableName($options); |
|
1612 } |
|
1613 |
1510 } |
1614 } |