<?php
/**
*
* LICENSE
*
* This source file is subject to the new BSD license
* It is available through the world-wide-web at this URL:
* http://www.petala-azul.com/bsd.txt
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to geral@petala-azul.com so we can send you a copy immediately.
*
* @package Bvb_Grid
* @copyright Copyright (c) (http://www.petala-azul.com)
* @license http://www.petala-azul.com/bsd.txt New BSD License
* @version $Id: Select.php 941 2010-03-11 15:09:52Z bento.vilas.boas@gmail.com $
* @author Bento Vilas Boas <geral@petala-azul.com >
*/
class Bvb_Grid_Source_Zend_Select implements Bvb_Grid_Source_Interface
{
protected $_select;
protected $_server;
protected $_describeTables;
protected $_cache;
protected $_fields;
function __construct (Zend_Db_Select $select)
{
$this->_select = $select;
$this->init($this->_select);
return $this;
}
/**
* Define the query using Zend_Db_Select instance
*
* @param Zend_Db_Select $select
* @return $this
*/
public function init (Zend_Db_Select $select)
{
$this->_setDb($select->getAdapter());
$adapter = get_class($select->getAdapter());
$adapter = str_replace("Zend_Db_Adapter_", "", $adapter);
if ( stripos($adapter, 'mysql') !== false ) {
$this->_server = 'mysql';
} else {
$adapter = str_replace('Pdo_', '', $adapter);
$this->_server = strtolower($adapter);
}
return $this;
}
/**
* Set db
* @param Zend_Db_Adapter_Abstract $db
*/
protected function _setDb (Zend_Db_Adapter_Abstract $db)
{
$this->_db = $db;
return $this;
}
function hasCrud ()
{
return true;
}
function getRecord ($table, array $condition)
{
$select = new Zend_Db_Select($this->_getDb());
$select->from($table);
foreach ( $condition as $field => $value ) {
if ( stripos($field, '.') !== false ) {
$field = substr($field, stripos($field, '.') + 1);
}
$select->where($field . '=?', $value);
}
if ( $this->_cache['use'] == 1 ) {
$hash = 'Bvb_Grid' . md5($select->__toString());
if ( ! $result = $this->_cache['instance']->load($hash) ) {
$final = $select->query(Zend_Db::FETCH_ASSOC);
$return = $final->fetchAll();
$this->_cache['instance']->save($result, $hash, array($this->_cache['tag']));
}
} else {
$final = $select->query(Zend_Db::FETCH_ASSOC);
$return = $final->fetchAll();
}
$final = array();
foreach ( $return[0] as $key => $value ) {
$final[$key] = $value;
}
if ( count($final) == 0 ) {
return false;
}
return $final;
}
/**
* Build the fields based on Zend_Db_Select
* @param $fields
* @param $tables
*/
function buildFields ()
{
$fields = $this->_select->getPart(Zend_Db_Select::COLUMNS);
$tables = $this->_select->getPart(Zend_Db_Select::FROM);
$returnFields = array();
foreach ( $fields as $field => $value ) {
/**
* Select all fields from the table
*/
if ( $value[1] == '*' ) {
if ( array_key_exists($value[0], $tables) ) {
$tableFields = $this->getDescribeTable($tables[$value[0]]['tableName']);
}
$tableFields = array_keys($tableFields);
foreach ( $tableFields as $field ) {
$title = ucwords(str_replace('_', ' ', $field));
$returnFields[$field] = array('title' => $title, 'field' => $value[0] . '.' . $field);
}
} else {
$explode = explode('.', $value[1]);
$title = ucwords(str_replace("_", ' ', end($explode)));
if ( is_object($value[1]) ) {
$title = ucwords(str_replace('_', ' ', $value[2]));
$returnFields[$value[2]] = array('title' => $title, 'field' => $value[0] . '.' . $value[2]);
} elseif ( strlen($value[2]) > 0 ) {
$title = ucwords(str_replace('_', ' ', $value[2]));
$returnFields[$value[2]] = array('title' => $title, 'field' => $value[0] . '.' . $value[1]);
} else {
$title = ucwords(str_replace('_', ' ', $value[1]));
$returnFields[$value[1]] = array('title' => $title, 'field' => $value[0] . '.' . $value[1]);
}
}
}
$this->_fields = $returnFields;
return $returnFields;
$this->_allFieldsAdded = true;
if ( count($this->_updateColumnQueue) > 0 ) {
foreach ( $this->_updateColumnQueue as $field => $options ) {
if ( ! array_key_exists($field, $this->_data['fields']) ) continue;
$this->updateColumn($field, $options);
}
$this->_updateColumnQueue = array();
}
return $this;
}
/**
* Get table description and then save it to a array.
*
* @param array|string $table
* @return array
*/
function getDescribeTable ($table)
{
if ( ! isset($this->_describeTables[$table]) || ! is_array($this->_describeTables[$table]) ) {
if ( $this->_cache['use'] == 1 ) {
$hash = 'Bvb_Grid' . md5($table);
if ( ! $result = $this->_cache['instance']->load($hash) ) {
$result = $this->_getDb()->describeTable($table);
$this->_cache['instance']->save($result, $hash, array($this->_cache['tag']));
}
} else {
$result = $this->_getDb()->describeTable($table);
}
$this->_describeTables[$table] = $result;
}
return $this->_describeTables[$table];
}
function _getDb ()
{
return $this->_db;
}
function execute ()
{
$final = $this->_select->query(Zend_Db::FETCH_ASSOC);
if ( $this->_cache['use'] == 1 ) {
$hash = 'Bvb_Grid' . md5($this->_select->__toString());
if ( ! $result = $this->_cache['instance']->load($hash) ) {
$result = $final->fetchAll();
$this->_cache['instance']->save($result, $hash, array($this->_cache['tag']));
}
} else {
$result = $final->fetchAll();
}
return $result;
}
function fetchDetail (array $where)
{
foreach ( $where as $field => $value ) {
if ( array_key_exists($field, $this->_fields) ) {
$field = $this->_fields[$field]['field'];
}
$this->_select->where($field . '=?', $value);
}
$this->_select->reset(Zend_Db_Select::LIMIT_COUNT);
$this->_select->reset(Zend_Db_Select::LIMIT_OFFSET);
if ( $this->_cache['use'] == 1 ) {
$hash = 'Bvb_Grid' . md5($this->_select->__toString());
if ( ! $result = $this->_cache['instance']->load($hash) ) {
$final = $this->_select->query(Zend_Db::FETCH_ASSOC);
$result = $final->fetchAll();
$this->_cache['instance']->save($result, $hash, array($this->_cache['tag']));
}
} else {
$final = $this->_select->query(Zend_Db::FETCH_ASSOC);
$result = $final->fetchAll();
}
if ( ! isset($result[0]) ) {
return false;
}
return $result[0];
}
/**
* Count the rows total without the limit
*
* @return void
*/
function getTotalRecords ()
{
$selectCount = clone $this->_select;
$selectCount->reset(Zend_Db_Select::COLUMNS);
$selectCount->reset(Zend_Db_Select::LIMIT_OFFSET);
$selectCount->reset(Zend_Db_Select::LIMIT_COUNT);
$selectCount->reset(Zend_Db_Select::ORDER);
$selectCount->columns(new Zend_Db_Expr('COUNT(*) AS total '));
if ( $this->_cache['use'] == 1 ) {
$hash = 'Bvb_Grid' . md5($selectCount->__toString());
if ( ! $result = $this->_cache['instance']->load($hash) ) {
$final = $selectCount->query(Zend_Db::FETCH_ASSOC);
$result = $final->fetchAll();
if ( count($result) > 1 ) {
$result = count($result);
} else {
$result = $result[0]['total'];
}
$this->_cache['instance']->save($result, $hash, array($this->_cache['tag']));
}
} else {
$final = $selectCount->query(Zend_Db::FETCH_ASSOC);
$result = $final->fetchAll();
if ( count($result) > 1 ) {
$result = count($result);
} else {
$result = $result[0]['total'];
}
}
return $result;
}
function getTableList ()
{
return $this->_select->getPart(Zend_Db_Select::FROM);
}
function getFilterValuesBasedOnFieldDefinition ($field)
{
$tableList = $this->getTableList();
$explode = explode('.', $field);
$tableName = reset($explode);
$field = end($explode);
if ( array_key_exists($tableName, $tableList) ) {
$tableName = $tableList[$tableName]['tableName'];
}
$table = $this->getDescribeTable($tableName);
if ( ! isset($table[$field]) ) {
return 'text';
}
$type = $table[$field]['DATA_TYPE'];
$return = 'text';
if ( substr($type, 0, 4) == 'enum' ) {
preg_match_all('/\'(.*?)\'/', $type, $result);
$return = array_combine($result[1], $result[1]);
}
return $return;
}
function getFieldType ($field)
{
$tableList = $this->getTableList();
$explode = explode('.', $field);
$tableName = reset($explode);
$field = end($explode);
if ( array_key_exists($tableName, $tableList) ) {
$tableName = $tableList[$tableName]['tableName'];
}
$table = $this->getDescribeTable($tableName);
$type = $table[$field]['DATA_TYPE'];
if ( substr($type, 0, 3) == 'set' ) {
return 'set';
}
return $type;
}
function getMainTable ()
{
$return = array();
$from = $this->_select->getPart(Zend_Db_Select::FROM);
foreach ( $from as $key => $tables ) {
if ( $tables['joinType'] == 'from' || count($from) == 1 ) {
$return['table'] = $tables['tableName'];
break;
}
}
if ( count($return) == 0 ) {
$table = reset($from);
$return['table'] = $table['tableName'];
}
return $return;
}
function buildQueryOrder ($field, $order, $reset = false)
{
foreach ( $this->_select->getPart(Zend_Db_Select::COLUMNS) as $col ) {
if ( ($col[0] . '.' . $col[2] == $field) && is_object($col[1]) ) {
$field = $col[2];
}
}
if ( $reset === true ) {
$this->_select->reset('order');
}
$this->_select->order($field . ' ' . $order);
return $this;
}
function buildQueryLimit ($start, $offset)
{
$this->_select->limit($start, $offset);
}
function getSelectObject ()
{
return $this->_select;
}
function getSelectOrder ()
{
$result = $this->_select->getPart(Zend_Db_Select::ORDER);
if ( count($result) == 0 ) {
return array();
}
return $result[0];
}
function getDistinctValuesForFilters ($field, $value)
{
$distinct = clone $this->_select;
$distinct->reset(Zend_Db_Select::COLUMNS);
$distinct->reset(Zend_Db_Select::ORDER);
$distinct->reset(Zend_Db_Select::LIMIT_COUNT);
$distinct->reset(Zend_Db_Select::LIMIT_OFFSET);
$distinct->columns(array('field' => new Zend_Db_Expr("DISTINCT({$field})")));
$distinct->columns(array('value' => $value));
$distinct->order(' value ASC');
if ( $this->_cache['use'] == 1 ) {
$hash = 'Bvb_Grid' . md5($distinct->__toString());
if ( ! $result = $this->_cache['instance']->load($hash) ) {
$result = $distinct->query(Zend_Db::FETCH_ASSOC);
$result = $result->fetchAll();
$this->_cache['instance']->save($result, $hash, array($this->_cache['tag']));
}
} else {
$result = $distinct->query(Zend_Db::FETCH_ASSOC);
$result = $result->fetchAll();
}
$final = array();
foreach ( $result as $value ) {
$final[$value['field']] = $value['value'];
}
return $final;
}
function getSqlExp (array $value)
{
if ( is_array($value) ) {
$valor = '';
foreach ( $value['functions'] as $final ) {
$valor .= $final . '(';
}
$valor .= $value['value'] . str_repeat(')', count($value['functions']));
} else {
$valor = "$value(" . $value['value'] . ")";
}
$select = clone $this->_select;
$select->reset(Zend_Db_Select::COLUMNS);
$select->reset(Zend_Db_Select::ORDER);
$select->reset(Zend_Db_Select::LIMIT_COUNT);
$select->reset(Zend_Db_Select::LIMIT_OFFSET);
$select->reset(Zend_Db_Select::GROUP);
$select->columns(new Zend_Db_Expr($valor . ' AS TOTAL'));
if ( $this->_cache['use'] == 1 ) {
$hash = 'Bvb_Grid' . md5($select->__toString());
if ( ! $result = $this->_cache['instance']->load($hash) ) {
$final = $select->query(Zend_Db::FETCH_ASSOC);
$result = $final->fetchColumn();
$this->_cache['instance']->save($result, $hash, array($this->_cache['tag']));
}
} else {
$final = $select->query(Zend_Db::FETCH_ASSOC);
$result = $final->fetchColumn();
}
return $result;
}
function getColumns ()
{
return $this->_select->getPart('columns');
}
function addFullTextSearch ($filter, $field)
{
$full = $field['search'];
if ( ! isset($full['indexes']) ) {
$indexes = $field['field'];
} elseif ( is_array($full['indexes']) ) {
$indexes = implode(',', array_values($full['indexes']));
} elseif ( is_string($full['indexes']) ) {
$indexes = $full['indexes'];
}
$extra = isset($full['extra']) ? $full['extra'] : 'boolean';
if ( ! in_array($extra, array('boolean', 'queryExpansion', false)) ) {
throw new Bvb_Grid_Exception('Unrecognized value in extra key');
}
if ( $extra == 'boolean' ) {
$extra = 'IN BOOLEAN MODE';
} elseif ( $extra == 'queryExpansion' ) {
$extra = ' WITH QUERY EXPANSION ';
} else {
$extra = '';
}
if ( $extra == 'IN BOOLEAN MODE' ) {
$filter = preg_replace("/\s+/", " +", $this->_getDb()->quote(' ' . $filter));
} else {
$filter = $this->_getDb()->quote($filter);
}
$this->_select->where(new Zend_Db_Expr("MATCH ($indexes) AGAINST ($filter $extra) "));
return;
}
function addCondition ($filter, $op, $completeField)
{
$explode = explode('.', $completeField['field']);
$field = end($explode);
$simpleField = false;
$columns = $this->getColumns();
foreach ( $columns as $value ) {
if ( $field == $value[2] ) {
if ( is_object($value[1]) ) {
$field = $value[1]->__toString();
$simpleField = true;
} else {
$field = $value[0] . '.' . $value[1];
}
break;
} elseif ( $field == $value[0] ) {
$field = $value[0] . '.' . $value[1];
break;
}
}
if ( strpos($field, '.') === false && $simpleField === false ) {
$field = $completeField['field'];
}
/**
* Reserved words from myslq dont contain any special charaters.
* But select expressions may.
*
* SELECT IF(City.Population>500000,1,0)....
*
* We can not quoteIdentifier this fields...
*/
if ( preg_match("/^[a-z_]$/i", $field) ) {
$field = $this->_getDb()->quoteIdentifier($field);
}
switch ($op) {
case 'equal':
case '=':
$this->_select->where($field . ' = ?', $filter);
break;
case 'REGEX':
$this->_select->where($field . " REGEXP " . $this->_getDb()->quote($filter));
break;
case 'rlike':
$this->_select->where($field . " LIKE " . $this->_getDb()->quote($filter . "%"));
break;
case 'llike':
$this->_select->where($field . " LIKE " . $this->_getDb()->quote("%" . $filter));
break;
case '>=':
$this->_select->where($field . " >= ?", $filter);
break;
case '>':
$this->_select->where($field . " > ?", $filter);
break;
case '<>':
case '!=':
$this->_select->where($field . " <> ?", $filter);
break;
case '<=':
$this->_select->where($field . " <= ?", $filter);
break;
case '<':
$this->_select->where($field . " < ?", $filter);
break;
case 'IN':
$filter = explode(',', $filter);
$this->_select->where($field . " IN (?)", $filter);
break;
case 'range':
$start = substr($filter, 0, strpos($filter, '<>'));
$end = substr($filter, strpos($filter, '<>') + 2);
$this->_select->where($field . " between " . $this->_getDb()->quote($start) . " and " . $this->_getDb()->quote($end));
break;
case 'like':
default:
$this->_select->where($field . " LIKE " . $this->_getDb()->quote("%" . $filter . "%"));
break;
}
}
/**
* Returns server name (mysql|pgsql|etc)
*/
function getSourceName ()
{
return $this->_server;
}
function insert ($table, array $post)
{
if ( $this->_cache['use'] == 1 ) {
$this->_cache['instance']->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array($this->_cache['tag']));
}
return $this->_getDb()->insert($table, $post);
}
function update ($table, array $post, array $condition)
{
if ( $this->_cache['use'] == 1 ) {
$this->_cache['instance']->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array($this->_cache['tag']));
}
return $this->_getDb()->update($table, $post, $this->buildWhereCondition($condition));
}
function delete ($table, array $condition)
{
if ( $this->_cache['use'] == 1 ) {
$this->_cache['instance']->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array($this->_cache['tag']));
}
return $this->_getDb()->delete($table, $this->buildWhereCondition($condition));
}
function buildWhereCondition (array $condition)
{
$where = '';
foreach ( $condition as $field => $value ) {
if ( stripos($field, '.') !== false ) {
$field = substr($field, stripos($field, '.') + 1);
}
$where .= 'AND ' . $this->_getDb()->quoteIdentifier($field) . ' = ' . $this->_getDb()->quote($value) . ' ';
}
return " ( " . substr($where, 3) . " )";
}
function resetOrder ()
{
$this->_select->reset('order');
}
function setCache ($cache)
{
if ( ! is_array($cache) ) {
$cache = array('use' => 0);
}
if ( isset($cache['use']['db']) && $cache['use']['db'] == 1 ) {
$cache['use'] = 1;
} else {
$cache['use'] = 0;
}
$this->_cache = $cache;
}
function buildForm ()
{
$table = $this->getMainTable();
$cols = $this->getDescribeTable($table['table']);
return $this->buildFormElements($cols);
}
function buildFormElements ($cols, $info = array())
{
$final = array();
$form = array();
foreach ( $cols as $column => $detail ) {
$label = ucwords(str_replace('_', ' ', $column));
$next = false;
if ( $detail['PRIMARY'] == 1 ) {
continue;
}
if ( ! isset($info['referenceMap']) ) {
$info['referenceMap'] = array();
}
if ( count($info['referenceMap']) > 0 ) {
foreach ( $info['referenceMap'] as $dep ) {
if ( is_array($dep['columns']) && in_array($column, $dep['columns']) ) {
$refColumn = $dep['refColumns'][array_search($column, $dep['columns'])];
} elseif ( is_string($dep['columns']) && $column == $dep['columns'] ) {
$refColumn = $dep['refColumns'];
} else {
continue;
}
$t = new $dep['refTableClass']();
$in = $t->info();
if ( (count($in['cols']) == 1 && count($in['primary']) == 0) || count($in['primary']) > 1 ) {
throw new Exception('Columns:' . count($in['cols']) . ' Keys:' . count($in['primary']));
# break;
}
if ( count($in['primary']) == 1 ) {
$field1 = array_shift($in['primary']);
$field2 = $refColumn;
}
$final['values'][$column] = array();
$r = $t->fetchAll()->toArray();
if ( $detail['NULLABLE'] == 1 ) {
$final['values'][$column][""] = "-- Empty --";
}
foreach ( $r as $field ) {
$final['values'][$column][$field[$field1]] = $field[$field2];
}
$form['elements'][$column] = array('select', array('multiOptions' => $final['values'][$column], 'label' => $label));
$next = true;
}
}
if ( $next === true ) {
continue;
}
if ( stripos($detail['DATA_TYPE'], 'enum') !== false ) {
preg_match_all('/\'(.*?)\'/', $detail['DATA_TYPE'], $result);
$options = array();
foreach ( $result[1] as $match ) {
$options[$match] = ucfirst($match);
}
$form['elements'][$column] = array('select', array('multiOptions' => $options, 'required' => ($detail['NULLABLE'] == 1) ? false : true, 'label' => $label));
continue;
}
if ( stripos($detail['DATA_TYPE'], 'set') !== false ) {
preg_match_all('/\'(.*?)\'/', $detail['DATA_TYPE'], $result);
$options = array();
foreach ( $result[1] as $match ) {
$options[$match] = ucfirst($match);
}
$form['elements'][$column] = array('multiCheckbox', array('multiOptions' => $options, 'required' => ($detail['NULLABLE'] == 1) ? false : true, 'label' => $label));
continue;
}
switch ($detail['DATA_TYPE']) {
case 'varchar':
case 'char':
$length = $detail['LENGTH'];
$form['elements'][$column] = array('text', array('validators' => array(array('stringLength', false, array(0, $length))), 'size' => 40, 'label' => $label, 'required' => ($detail['NULLABLE'] == 1) ? false : true, 'value' => (! is_null($detail['DEFAULT']) ? $detail['DEFAULT'] : "")));
break;
case 'date':
$form['elements'][$column] = array('text', array('validators' => array(array('Date')), 'size' => 10, 'label' => $label, 'required' => ($detail['NULLABLE'] == 1) ? false : true, 'value' => (! is_null($detail['DEFAULT']) ? $detail['DEFAULT'] : "")));
break;
case 'datetime':
case 'timestamp':
$form['elements'][$column] = array('text', array('validators' => array(array(new Zend_Validate_Date('Y-m-d H:i:s'))), 'size' => 19, 'label' => $label, 'required' => ($detail['NULLABLE'] == 1) ? false : true, 'value' => (! is_null($detail['DEFAULT']) ? $detail['DEFAULT'] : "")));
break;
case 'text':
case 'mediumtext':
case 'longtext':
case 'smalltext':
$form['elements'][$column] = array('textarea', array('label' => $label, 'required' => ($detail['NULLABLE'] == 1) ? false : true, 'filters' => array('StripTags')));
break;
case 'int':
case 'bigint':
case 'mediumint':
case 'smallint':
case 'tinyint':
$isZero = (! is_null($detail['DEFAULT']) && $detail['DEFAULT'] == "0") ? true : false;
$form['elements'][$column] = array('text', array('validators' => array('Digits'), 'label' => $label, 'size' => 10, 'required' => ($isZero == false && $detail['NULLABLE'] == 1) ? false : true, 'value' => (! is_null($detail['DEFAULT']) ? $detail['DEFAULT'] : "")));
break;
case 'float':
case 'decimal':
case 'double':
$form['elements'][$column] = array('text', array('validators' => array('Float'), 'size' => 10, 'label' => $label, 'required' => ($detail['NULLABLE'] == 1) ? false : true, 'value' => (! is_null($detail['DEFAULT']) ? $detail['DEFAULT'] : "")));
break;
default:
break;
}
}
return $form;
}
/**
* Get the primary table key
* This is important because we only allow edit, add or remove records
* From tables that have on primary key
*
* @return array
*/
function getPrimaryKey ($table)
{
$pk = $this->getDescribeTable($table);
$tb = $this->getTableList();
$keys = array();
foreach ( $pk as $pkk => $primary ) {
if ( $primary['PRIMARY'] == 1 ) {
foreach ( $tb as $key => $value ) {
if ( $value['tableName'] == $primary['TABLE_NAME'] ) {
$prefix = $key . '.';
break;
}
}
$keys[] = $prefix . $pkk;
}
}
return $keys;
}
}
|