Zend_Db::INT_TYPE, 'INTEGER' => Zend_Db::INT_TYPE, 'MEDIUMINT' => Zend_Db::INT_TYPE, 'SMALLINT' => Zend_Db::INT_TYPE, 'TINYINT' => Zend_Db::INT_TYPE, 'BIGINT' => Zend_Db::BIGINT_TYPE, 'SERIAL' => Zend_Db::BIGINT_TYPE, 'DEC' => Zend_Db::FLOAT_TYPE, 'DECIMAL' => Zend_Db::FLOAT_TYPE, 'DOUBLE' => Zend_Db::FLOAT_TYPE, 'DOUBLE PRECISION' => Zend_Db::FLOAT_TYPE, 'FIXED' => Zend_Db::FLOAT_TYPE, 'FLOAT' => Zend_Db::FLOAT_TYPE, ); public $itemName; public $itemsName; public function __construct($options = array()) { $this->setOptions($options); $this->_db = Qs_Db::getInstance(); if (null === $this->_tableAlias) { throw new Qs_Db_Exception('_tableAlias is not defined'); } if (!$this->itemName) { $this->itemName = preg_replace('/([a-z])([A-Z])/', '$1 $2', $this->_tableAlias); } if (!$this->itemsName) { $this->itemsName = $this->itemName . 's'; } if (!$this->_tableName) { $this->_tableName = Qs_Db::getTableName($this->_tableAlias, $this->getIsService()); } $this->_init(); } protected function _init() { } function __call($name, $args) { $matches = array(); if (preg_match('/^get(.*)4Select$/', $name, $matches)) { array_unshift($args, $matches[1]); return call_user_func_array(array($this, '_get4Select'), $args); } $debugData = debug_backtrace(); echo "Fatal error: Call to undefined method " . get_class($this) . ": $name() in {$debugData[1]['file']} on line " . "{$debugData[1]['line']}"; exit; } public function __get($name) { $propertyName = '_' . $name; if (property_exists($this, $propertyName)) { if (null === $this->{$propertyName}) { switch ($name) { case 'select': $this->{$propertyName} = $this->_getSelect(); break; case 'table': $this->{$propertyName} = $this->_getTable(); break; default: throw new Qs_Db_Obj_Exception('Unknown property name: ' . $name); } } } else { $pair = array(); if (strncmp($name, 'table', 5) == 0) { $tableName = substr($name, 5); if ($tableName === false) { $tableName = null; } return $this->_getTable($tableName); } else if ($name == 'pair') { return $this->_getPair(); } else if (preg_match("/^pair(.+)?$/", $name, $pair)) { return $this->_getPair($pair[1]); } throw new Qs_Db_Obj_Exception('Unknown property name: ' . $name); } return $this->{$propertyName}; } protected function _getPair($tableAlias = null, $isService = null) { if (null === $tableAlias) { $tableAlias = $this->_tableAlias; } return array($tableAlias => $this->_getTableName($tableAlias, $isService)); } public function setOptions($options) { if ($options instanceof Zend_Config) { $options = $options->toArray(); } if (isset($options['options'])) { unset($options['options']); } foreach ($options as $key => $value) { $method = 'set' . ucfirst($key); if (method_exists($this, $method)) { $this->$method($value); } else { $this->_options[$key] = $value; } } return $this; } public function getIsService() { return $this->_isService; } public function setIsService($flag) { $this->_isService = (bool) $flag; return $this; } public function setLanguage($language) { $this->_language = $language; return $this; } public function getLanguage() { if (null == $this->_language) { $this->_language = Qs_Constant::get('CURRENT_LANGUAGE'); } return $this->_language; } public function isDefaultLanguage() { return $this->_language === Qs_Constant::get('DEFAULT_LANGUAGE'); } public function setSelect(Zend_Db_Select $select) { $this->_select = $select; return $this; } public function getPaginatorAdapter() { return $this->_getPaginatorSelect(); } protected function _getPaginatorSelect() { $select = (null == $this->_select) ? $this->getListSelect() : $this->_select; $select = clone $select; $select->reset(Zend_Db_Select::LIMIT_COUNT); $select->reset(Zend_Db_Select::LIMIT_OFFSET); return $select; } protected function _getSelect() { if (null == $this->_select) { $this->_select = $this->_db->select(); } return $this->_select; } protected function _getSorter($insertPlacement = null, $tableAlias = null) { if (null === $insertPlacement) { $insertPlacement = $this->_insertPlacement; } $tableAlias = (null == $tableAlias) ? $this->_tableAlias : $tableAlias; if ('PREPEND' == $insertPlacement) { $function = 'MIN'; $diff = -1; } elseif ('APPEND' == $insertPlacement) { $function = 'MAX'; $diff = 1; } else { throw new Qs_Db_Exception('Unknown insert placement: ' . $insertPlacement); } $select = $this->_db->select(); $select->from($this->_getPair($tableAlias), "{$function}({$tableAlias}.sorter)"); $this->_where4Sorter($select); $select->limit(1); return (null === ($sorter = $this->_db->fetchOne($select))) ? 0 : $sorter + $diff; } /** * @param null $alias * @param null $isService * @return Qs_Db_Table */ protected function _getTable($alias = null, $isService = null) { if (null === $alias) { $alias = $this->_tableAlias; } $newTableName = Qs_Db::getTableName( $alias, (null !== $isService) ? $isService : $this->getIsService() ); if (!isset($this->_tables[(int) $isService][$alias])) { $this->_tables[(int) $isService][$alias] = new Qs_Db_Table(array('name' => $newTableName, 'db' => $this->_db)); } return $this->_tables[(int) $isService][$alias]; } protected function _getTableName($alias = null, $isService = null) { if (null === $alias) { $alias = $this->_tableAlias; } return Qs_Db::getTableName( $alias, (null !== $isService) ? $isService : $this->getIsService() ); } protected function _getFromColumns() { return array('*'); } protected function _from(Zend_Db_Select $select = null) { if (null === $select) { $select = $this->select; } $select->from($this->pair, $this->_getFromColumns()); return $select; } protected function _join(Zend_Db_Select $select = null) { if (null === $select) { $select = $this->select; } return $select; } protected function _where(Zend_Db_Select $select = null) { if (null === $select) { $select = $this->select; } if ($this->hasFilter()) { $this->_filterWhere($select); } return $select; } public function getFilterFields() { return $this->_filterFields; } protected function _filterWhere(Zend_Db_Select $select) { if (!empty($this->_filterFields) && array_key_exists('query', $this->_filter)) { Qs_Db_Filter::where($select, $this->_filterFields, $this->_filter['query']); } $filter = $this->_getCleanedFilter($this->_filter); Qs_Db::filter($select, $filter, $this->_tableAlias); return $select; } protected function _where4Sorter(Zend_Db_Select $select = null) { if (null === $select) { $select = $this->select; } return $select; } protected function _group(Zend_Db_Select $select = null) { if (null === $select) { $select = $this->select; } return $select; } protected function _having(Zend_Db_Select $select = null) { if (null === $select) { $select = $this->select; } return $select; } protected function _order(Zend_Db_Select $select = null) { if (null === $select) { $select = $this->select; } return $select; } protected function _limitPage(Zend_Db_Select $select = null) { if (null === $select) { $select = $this->select; } return $select; } protected function _getWherePrimaryKey($alias = null) { return $this->table->prepareWhere((array)$this->_primaryKey, (null === $alias) ? $this->_tableAlias : $alias); } public function initData() { $this->_data = $this->_getFromDb($this->getPrimaryKey()); return $this; } public function initFromForm(array $data) { $this->_arrayParseUrl2Tag($data); $this->_data = $data; return $this; } public static function pregBaseUrl2Tag($content, $url) { // Page Id // $info = Qs_Url::parse($url); // $page = Qs_SiteMap::getPage($info['alias'], false, null, false); // if (is_array($page) && isset($page['id'])) { // $content = str_replace($url, $page['id'], $content); // $content = str_replace('href', 'frwd:page-language="' . $info['language'] . '" frwd:page-id', $content); // return $content; // } // Tag $secure = Qs_SiteMap::isSecurePage($url); if (true === $secure) { $newUrl = str_replace(array(BASE_URL_HTTP, BASE_URL_HTTPS), Qs_Db_Obj::TAG_BASE_URL_HTTPS, $url); } elseif (false === $secure) { $newUrl = str_replace(array(BASE_URL_HTTP, BASE_URL_HTTPS), Qs_Db_Obj::TAG_BASE_URL_HTTP, $url); } elseif (0 === strpos($url, 'https://')) { $newUrl = str_replace(BASE_URL_HTTPS, Qs_Db_Obj::TAG_BASE_URL_HTTPS, $url); } else { $newUrl = str_replace(BASE_URL_HTTP, Qs_Db_Obj::TAG_BASE_URL_HTTP, $url); } $content = str_replace($url, $newUrl, $content); return $content; } protected function _baseUrl2Tag($content) { $content = preg_replace('/\<(?:a|area)[^\>"\']+href\s*=\s*(?:"|\')(https?:\/\/[^"\']+)(?:"|\')(?:[^\>]+)?\>/uie', 'Qs_Db_Obj::pregBaseUrl2Tag(stripslashes(\'$0\'), stripslashes(\'$1\'))', $content); return str_replace(BASE_URL, Qs_Db_Obj::TAG_BASE_URL, $content); } protected function _tag2BaseUrl($content) { return self::tag2BaseUrl($content); } protected static function tag2BaseUrl($content) { return str_replace( array(Qs_Db_Obj::TAG_BASE_URL, Qs_Db_Obj::TAG_BASE_URL_HTTP, Qs_Db_Obj::TAG_BASE_URL_HTTPS), array(BASE_URL, BASE_URL_HTTP, BASE_URL_HTTPS), $content ); } protected function _arrayParseTag2Url(array &$array, $fields = null) { if (null === $fields) { $fields = $this->_urlParseFields; } if (empty($fields) || !is_array($array) || empty($array)) { return false; } foreach ($fields as $field) { if (isset($array[$field])) { $array[$field] = $this->_tag2BaseUrl($array[$field]); } } return true; } protected function _arrayParseUrl2Tag(&$array) { if (empty($this->_urlParseFields) || !is_array($array) || empty($array)) { return false; } foreach ($this->_urlParseFields as $field) { if (isset($array[$field])) { $array[$field] = $this->_baseUrl2Tag($array[$field]); } } return true; } protected function _prepareList(array &$list) { if (empty($this->_urlParseFields) || !is_array($list) || empty($list)) { return false; } foreach (array_keys($list) as $k) { $this->_arrayParseTag2Url($list[$k]); } return true; } public function insert($data = null) { if (null === $data) { $data = $this->_data; } else { $this->_data = $data; } unset($data['id']); $metaData = $this->table->getMetaData(); if (isset($metaData['sorter']) && !isset($data['sorter'])) { $data['sorter'] = $this->_getSorter(); } $this->_db->beginTransaction(); try { $this->_primaryKey = $this->table->insert($data); $this->_insertDependency(); $this->_db->commit(); } catch (Exception $e) { $this->_deleteFiles($data); Qs_Debug::log($e->getMessage(), 3); $this->_db->rollBack(); return $e; } $this->_handleFiles(); return $this->_primaryKey; } protected function _insertDependency() {} public function update($data = null) { if (null === $data) { $data = $this->_data; } else { $this->_data = $data; } unset($data['id']); $this->_db->beginTransaction(); try { $result = $this->table->updateByKey($data, $this->_primaryKey); $this->_updateDependency(); $this->_db->commit(); } catch (Exception $e) { Qs_Debug::log($e->getMessage(), 3); $this->_db->rollBack(); return $e; } $this->_handleFiles(); return $result; } protected function _updateDependency() {} protected function _updateIds($tableName, $referenceField, $fieldName, $list) { $this->_deleteIds($tableName, $referenceField); if (!$this->_primaryKey || !is_array($list) || empty($list)) { return false; } if (is_array($this->_primaryKey)) { throw new Qs_Db_Obj_Exception('_updateIds method is not supports multiple fields primaty key'); } $id = $this->_db->quote(''.$this->_primaryKey, Zend_Db::INT_TYPE); $sql = "INSERT INTO {$tableName} ({$referenceField}, {$fieldName}) VALUES"; foreach ($list as $k => $v) { $type = null; if (is_numeric($v)) { $type = Zend_Db::INT_TYPE; } $sql .= "\n($id, " . $this->_db->quote($v, $type) . '),'; } $sql = rtrim($sql, ',') . ';'; $this->_db->query($sql); return $this; } protected function _deleteIds($tableName, $referenceField) { if (!($primary = $this->getPrimaryKey())) { return false; } $sql = 'DELETE FROM ' . $this->_db->quoteIdentifier($tableName) . ' ' . 'WHERE ' . $this->_db->quoteIdentifier($referenceField) . ' = ' . $this->_db->quote($primary, 'integer'); $this->_db->query($sql); return $this; } protected function _handleFiles() { $session = new Qs_Session_Namespace(CURRENT_PAGE); if (isset($session->files) && !empty($session->files)) { $adapter = new Qs_File_Transfer_Adapter_Db(); foreach ($session->files as $file => $name) { $adapter->delete($file, $name); } unset($session->files); } return $this; } public function updateOrder(array $order) { foreach ($order as $sorter => $id) { $this->table->updateByKey(array('sorter' => (int)$sorter), $id); } return $this; } public function getFileFields() { return $this->_fileFields; } public function setFileFields($fields) { $this->_fileFields = $fields; } protected function _deleteFiles($row = null) { if (is_array($this->_fileFields) && !empty($this->_fileFields)) { $adapter = new Qs_File_Transfer_Adapter_Db(); if (null === $row) { if (false !== ($row = $this->table->findRow($this->getPrimaryKey()))) { $row = $row->toArray(); } } if (is_array($row) && !empty($row)) { foreach ($this->_fileFields as $name) { if (isset($row[$name]) && !empty($row[$name])) { $adapter->delete($row[$name], $name); } } } unset($adapter); } } public function delete() { $this->_deleteDependency(); $this->_deleteFiles(); $this->table->deleteByKey($this->_primaryKey); } protected function _deleteDependency() { return $this; } protected function _prepareListOptions($options = array(), Zend_Db_Select $select = null) { if (null === $select) { $select = $this->select; } if (empty($options)) { return $select; } if (!is_array($options) || empty($options)) { throw new Qs_Db_Obj_Exception('Invalid options ' . print_r($options, true) ); } $allowedCallback = array( 'from', 'join', 'joinCross', 'joinFull', 'joinInner', 'joinLeft', 'joinNatural', 'joinRight', 'where', 'group', 'having', 'order', 'limit', 'limitPage' ); foreach ($options as $part => $params) { if (in_array($part, $allowedCallback) && !empty($params)) { call_user_func_array(array($select, $part), (array)$params); } } return $select; } public function clearData() { $this->_data = null; return $this; } public function getData($field = false, $default = null) { if (null === $this->_data && $this->getPrimaryKey() && $data = $this->_getFromDb($this->getPrimaryKey())) { $this->_data = $data; } if (!$this->_data) { return null; } return Qs_Array::get($this->_data, $field, $default); } protected function _getFromDbColumns() { return array('*'); } protected function _joinFromDb(Zend_Db_Select $select) { return $this; } protected function _getFromDb($key, $field = false) { $select = $this->db->select(); $select->from($this->pair, $this->_getFromDbColumns()); $select->where($this->table->prepareWhere((array) $key, $this->_tableAlias)); $this->_joinFromDb($select); if (false === ($row = $this->db->fetchRow($select))) { return false; } if (false === $field) { $this->_addDependenciesFromDb($row); } $this->_arrayParseTag2Url($row); return Qs_Array::get($row, $field); } protected function _addDependenciesFromDb(&$data) { return $this; } protected function _getDependencyFromDb($dictionaryTable, $fields, $dependedTable, $linkField, array $key) { $select = $this->db->select()->reset(); $where = ''; foreach ($key as $name => $value) { $where .= "t.{$name} = " . ((is_numeric($value))? intval($value) : $this->db->quote($value)); } $where .= " AND d.id = t.{$linkField}"; $select->from(array('d' => $dictionaryTable), $fields) ->join(array('t' => $dependedTable), $where, null); if (is_array($fields) && count($fields) > 1) { $list = $this->db->fetchPairs($select); } else { $list = $this->db->fetchCol($select); } return $list; } /** * @param array $options * @return Zend_Db_Select */ public function getListSelect($options = array()) { $this->select->reset(); foreach (array('from', 'join', 'where', 'group', 'having', 'order', 'limitPage') as $name) { $method = '_' . ucfirst($name); $this->$method(); } $this->_prepareListOptions($options); return $this->select; } public function getListStatement($options = array()) { $select = $this->getListSelect($options); $stmt = $select->query(); return $stmt; } public function getListStatement4XmlSitemap($columns = array('id'), $options = array()) { $select = $this->getListSelect($options); $select->reset('columns')->columns($columns); return $this->db->query($select); } public function getList($options = array()) { $stmt = $this->getListStatement($options); $list = $stmt->fetchAll(); $this->_prepareList($list); return $list; } public function getListItemsCount() { $select = $this->_getPaginatorSelect(); $adapter = new Zend_Paginator_Adapter_DbSelect($select); return $adapter->count(); } public function getReorderOptions($keyColumn, $titleColumn) { $select = $this->getListSelect(); $select = $this->_where4Sorter($select); $select->order($this->_tableAlias . '.sorter'); $list = $this->_db->fetchAll($select); $options = array(); foreach ($list as $row) { $options[$row[$keyColumn]] = $row[$titleColumn]; } return $options; } public function getPrimary() { return $this->table->getPrimary(); } public function getPrimaryKey() { return $this->_primaryKey; } public function getMetaData() { return $this->table->getMetaData(); } public function setPrimaryKey($_primaryKey) { if (is_numeric($_primaryKey)) { $this->_primaryKey = intval($_primaryKey); return $this; } else if (is_string($_primaryKey)) { $this->_primaryKey = strval($_primaryKey); return $this; } $metaData = $this->table->getMetaData(); foreach ($_primaryKey as $name => &$value) { switch($metaData[$name]['DATA_TYPE']) { case 'int': $value = intval($value); break; case 'varchar': case 'char': $value = strval($value); break; default: break; } } if (count($_primaryKey) > 1) { $this->_primaryKey = $_primaryKey; } else { reset($_primaryKey); $this->_primaryKey = current($_primaryKey); } return $this; } protected function _encryptPass($password, $salt = '') { return md5($salt . $password); } protected function _get4Select($tableAlias, $columns = '*', $isService = null, $where = null, $order = null, $limit = null) { $select = $this->db->select(); $select->from(array($this->_getTableName($tableAlias, $isService)), $columns); if (null != $where) { $where = (array) $where; foreach ($where as $_where) { $select->where($_where); } } if (null === $order) { // automatic order initialization $meta = $this->_getTable($tableAlias, $isService)->info(Zend_Db_Table::METADATA); if (array_key_exists('sorter', $meta)) { $order = 'sorter'; } elseif ('*' == $columns) { $primary = $this->_getTable($tableAlias, $isService)->info(Zend_Db_Table::PRIMARY); $fields = Qs_Array::excludeArray($meta, $primary); $order = key($fields); } elseif (is_array($columns)) { $order = next($columns); } } if ($order) { // use $order = false to disable automatic initialization $select->order($order); } if (null != $limit) { $select->limit($limit); } if (is_array($columns) && count($columns) > 2) { return $this->db->fetchAssoc($select); } return $this->db->fetchPairs($select); } public function changeEnumOption($name) { $meta = $this->getMetaData(); if (!isset($meta[$name])) { return false; } $field = $meta[$name]; preg_match_all("/'([^']+)'/", $field['DATA_TYPE'], $matches); $values = $matches[1]; if (false === ($row = $this->table->findRow($this->getPrimaryKey()))) { return false; } $value = $row->$name; if (false !== ($index = array_search($value, $values)) && array_key_exists($index + 1, $values)) { $newValue = $values[$index + 1]; } else { $newValue = (!empty($meta['DEFAULT'])) ? $meta['DEFAULT'] : $values[0]; } $row->$name = $newValue; $row->save(); return true; } public function setFilter(array $filter) { $this->_filter = $filter; return $this; } public function addFilter(array $filter) { if (!is_array($this->_filter)) { $this->_filter = array(); } $this->_filter = array_merge($this->_filter, $filter); return $this; } /** * Remove from filter unused values * @param array $filter * @return array */ protected function _getCleanedFilter($filter) { $fields = array_keys($filter); $fields = array_diff($fields, $this->_filterAllowedEmptyFields); foreach ($fields as $name) { if (is_scalar($filter[$name])) { if ('' == trim($filter[$name])) { unset($filter[$name]); } } else { if (empty($filter[$name])) { unset($filter[$name]); } } } foreach ($this->_filterExcludeFields as $name) { unset($filter[$name]); } return $filter; } public function getFilter($field = false) { if (false === $field) { return $this->_filter; } return Qs_Array::get($this->_filter, $field); } public function hasFilter() { return $this->_hasFilter; } public function disableFilter() { $this->_hasFilter = false; return $this; } public function enableFilter() { $this->_hasFilter = true; return $this; } /** * Converts array of ids in two dimensional array for Qs_Db_Obj->_insertLinks() * @param string $linkIdField * @param array $linkedIds * @param string $itemIdField * @param int $itemId * @param array|null $linkData [OPTIONAL] * @return array|null */ protected function _ids2Links($linkIdField, $linkedIds, $itemIdField, $itemId, array $linkData = null) { $links = array(); if (is_array($linkedIds)) { foreach ($linkedIds as $id) { $row = array($itemIdField => $itemId, $linkIdField => $id); $links[] = (null === $linkData) ? $row : array_merge($linkData, $row); } } return (empty($links)) ? null : $links; } /** * @throws Qs_Db_Obj_Exception * @param string $linkTableAlias * @param array $list Multi dimensional array with data for each link (overrides $linkData) * @param array $linkData Data that should be added to each link * @return int Affected rows count */ protected function _insertLinks($linkTableAlias, array $list = null, array $linkData = array()) { if (empty($list)) { return $this; } $firstRow = reset($list); if (!is_array($list) || !is_array($firstRow)) { throw new Qs_Db_Obj_Exception('Param $list in in wrong format'); } /** @var $table Qs_Db_Table */ $table = $this->_getTable($linkTableAlias); $tableName = $this->_getTableName($linkTableAlias); $metaData = $table->info(Zend_Db_Table::METADATA); $linkData = (array) $linkData; // init date if (isset($metaData['added']) || isset($metaData['changed'])) { $linkData['added'] = $linkData['changed'] = date('Y-m-d H:i:s'); } // init sorter $initSorter = $insertPlacement = null; if (isset($metaData['sorter']) && !isset($firstRow['sorter'])) { if (isset($linkData['sorter'])) { $initSorter = (int) $linkData['sorter']; } else { $insertPlacement = (null === $this->_linkInsertPlacement) ? $this->_insertPlacement : $this->_linkInsertPlacement; $initSorter = $this->_getSorter($insertPlacement, $linkTableAlias); } $linkData['sorter'] = $initSorter; } $fields = array_keys(array_intersect_key($metaData, array_merge($linkData, $firstRow))); $sqlParts = array(); foreach ($list as $row) { $_row = array_merge($linkData, $row); if (!isset($row['sorter']) && null !== $initSorter) { $_row['sorter'] = ('APPEND' === $insertPlacement) ? $initSorter++ : $initSorter--; } $_row = Qs_Array::map($_row, $fields); $_row = array_map(array($this->_db, 'quote'), $_row); $sqlParts[] = '(' . implode(', ', $_row). ')'; } $sql = 'INSERT INTO ' . $tableName . ' (' . implode(', ', $fields) . ') VALUES ' . PHP_EOL . implode(', ' . PHP_EOL, $sqlParts) . ';'; return $this->_db->query($sql)->rowCount(); } }