-
-
Save ceeram/3062745 to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* ODBC for DBO | |
* | |
* PHP versions 4 and 5 | |
* | |
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | |
* Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org) | |
* | |
* Licensed under The MIT License | |
* Redistributions of files must retain the above copyright notice. | |
* | |
* @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org) | |
* @link http://cakephp.org CakePHP(tm) Project | |
* @package datasources | |
* @subpackage datasources.models.datasources.dbo | |
* @since CakePHP Datasources v 0.1 | |
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) | |
*/ | |
App::uses('DboSource', 'Model/Datasource'); | |
/** | |
* Short description for class. | |
* | |
* Long description for class | |
* | |
* @package cake | |
* @subpackage cake.cake.libs.model.datasources.dbo | |
*/ | |
class Odbc extends DboSource { | |
/** | |
* Driver description | |
* | |
* @var string | |
*/ | |
public $description = "ODBC DBO Driver"; | |
/** | |
* Database keyword used to assign aliases to identifiers. | |
* | |
* @var string | |
*/ | |
public $alias = ""; | |
/** | |
* Table/column starting quote | |
* | |
* @var string | |
*/ | |
public $startQuote = ""; | |
/** | |
* Table/column end quote | |
* | |
* @var string | |
*/ | |
public $endQuote = ""; | |
/** | |
* Columns | |
* | |
* @var array | |
*/ | |
//var $columns = array(); | |
var $columns = array('primary_key' => array('name' => 'int(11) DEFAULT NULL auto_increment'), | |
'string' => array('name' => 'varchar', 'limit' => '255'), | |
'text' => array('name' => 'text'), | |
'integer' => array('name' => 'int', 'limit' => '11'), | |
'float' => array('name' => 'float'), | |
'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d h:i:s', 'formatter' => 'date'), | |
'timestamp' => array('name' => 'datetime', 'format' => 'Y-m-d h:i:s', 'formatter' => 'date'), | |
'time' => array('name' => 'time', 'format' => 'h:i:s', 'formatter' => 'date'), | |
'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'), | |
'binary' => array('name' => 'blob'), | |
'boolean' => array('name' => 'tinyint', 'limit' => '1')); | |
/** | |
* Whether or not to cache the results of DboSource::name() and DboSource::conditions() | |
* into the memory cache. Set to false to disable the use of the memory cache. | |
* | |
* @var boolean. | |
*/ | |
public $cacheMethods = true; | |
/** | |
* Connects to the database using options in the given configuration array. | |
* | |
* @return boolean True if the database could be connected, else false | |
*/ | |
public function connect() { | |
$this->config; | |
$this->connected = false; | |
try { | |
$flags = array( | |
PDO::ATTR_PERSISTENT => $this->config['persistent'], | |
PDO::ATTR_EMULATE_PREPARES => true, | |
); | |
if (!empty($this->config['encoding'])) { | |
//$flags[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES ' . $config['encoding']; | |
} | |
$this->_connection = new PDO( | |
"odbc:{$this->config['driver']}", null, null, $flags | |
); | |
$this->_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | |
$this->connected = true; | |
} catch (PDOException $e) { | |
throw new MissingConnectionException(array('class' => $e->getMessage())); | |
} | |
return $this->connected; | |
} | |
/** | |
* Check if the ODBC extension is installed/loaded | |
* | |
* @return boolean | |
*/ | |
public function enabled() { | |
return in_array('odbc', PDO::getAvailableDrivers()); | |
} | |
/** | |
* Returns an array of sources (tables) in the database. | |
* | |
* @return array Array of tablenames in the database | |
*/ | |
function listSources() { | |
$cache = parent::listSources(); | |
if ($cache != null) { | |
return $cache; | |
} | |
$result = $this->_connection->query("SELECT * FROM ODLQTBLE"); | |
$tables = array_map('trim', $result->fetchAll(PDO::FETCH_COLUMN, 1)); | |
unset($result); | |
parent::listSources($tables); | |
return $tables; | |
} | |
/** | |
* Returns an array of the fields in given table name. | |
* | |
* @param Model $model Model object to describe | |
* @return array Fields in table. Keys are name and type | |
*/ | |
function describe($model) { | |
$cache = parent::describe($model); | |
if ($cache != null) { | |
//return $cache; | |
} | |
$fields = array(); | |
$sql = 'SELECT * FROM ' . $this->fullTableName($model, false); | |
$fields = array(); | |
try{ | |
$result = $this->_connection->query($sql); | |
} catch (PDOException $e) { | |
debug($e->getMessage()); | |
} | |
if ($result instanceof PDOStatement == false) { | |
return $fields; | |
} | |
$row = $result->fetch(PDO::FETCH_ASSOC); | |
if (!is_array($row)) { | |
return $fields; | |
} | |
unset($result); | |
$cols = array_keys($row); | |
foreach ($cols as $column) { | |
$fields[$column] = array('type' => 'text', 'null' => true); | |
} | |
$this->_cacheDescription($model->tablePrefix . $model->table, $fields); | |
return $fields; | |
} | |
/** | |
* Enter description here... | |
* | |
* @param unknown_type $results | |
*/ | |
public function resultSet(&$results) { | |
$this->map = array(); | |
$clean = substr($results->queryString, strpos($results->queryString, " ") + 1); | |
$clean = substr($clean, 0, strpos($clean, ' FROM') - strlen($clean)); | |
$parts = explode(", ", $clean); | |
foreach ($parts as $key => $value) { | |
list($table, $name) = pluginSplit($value, false, 0); | |
if (!$table && strpos($name, $this->virtualFieldSeparator) !== false) { | |
$name = substr(strrchr($name, " "), 1); | |
} | |
$this->map[$key] = array($table, $name, "VAR_STRING"); | |
} | |
} | |
/** | |
* Fetches the next row from the current result set | |
* | |
* @return unknown | |
*/ | |
/** | |
* Fetches the next row from the current result set | |
* | |
* @return mixed array with results fetched and mapped to column names or false if there is no results left to fetch | |
*/ | |
public function fetchResult() { | |
if ($row = $this->_result->fetch()) { | |
$resultRow = array(); | |
foreach ($this->map as $col => $meta) { | |
list($table, $column, $type) = $meta; | |
if (strpos($column,'COUNT(')!== false) { | |
$column = 'count'; | |
} | |
$resultRow[$table][$column] = trim($row[$col]); | |
if ($type === 'boolean' && !is_null($row[$col])) { | |
$resultRow[$table][$column] = $this->boolean($resultRow[$table][$column]); | |
} | |
} | |
return $resultRow; | |
} | |
$this->_result->closeCursor(); | |
return false; | |
} | |
/** | |
* Returns a limit statement in the correct format for the particular database. | |
* | |
* @param integer $limit Limit of results returned | |
* @param integer $offset Offset from which to start results | |
* @return string SQL limit/offset statement | |
*/ | |
public function limit($limit, $offset = null) { | |
return null; | |
} | |
/** | |
* Returns an SQL calculation, i.e. COUNT() or MAX() | |
* | |
* @param model $model | |
* @param string $func Lowercase name of SQL function, i.e. 'count' or 'max' | |
* @param array $params Function parameters (any values must be quoted manually) | |
* @return string An SQL calculation function | |
*/ | |
public function calculate($model, $func, $params = array()) { | |
$params = (array)$params; | |
switch (strtolower($func)) { | |
case 'count': | |
if (!isset($params[0])) { | |
$params[0] = '*'; | |
} | |
if (!isset($params[1])) { | |
$params[1] = 'count'; | |
} | |
if (is_object($model) && $model->isVirtualField($params[0])){ | |
$arg = $this->__quoteFields($model->getVirtualField($params[0])); | |
} else { | |
$arg = $this->name($params[0]); | |
} | |
return "COUNT($arg) AS '$params[1]'"; | |
case 'max': | |
case 'min': | |
if (!isset($params[1])) { | |
$params[1] = $params[0]; | |
} | |
if (is_object($model) && $model->isVirtualField($params[0])) { | |
$arg = $this->__quoteFields($model->getVirtualField($params[0])); | |
} else { | |
$arg = $this->name($params[0]); | |
} | |
return strtoupper($func) . '(' . $arg . ') AS ' . $this->name($params[1]); | |
break; | |
} | |
} | |
/** | |
* Returns a quoted and escaped string of $data for use in an SQL statement. | |
* | |
* @param string $data String to be prepared for use in an SQL statement | |
* @param string $column The column into which this data will be inserted | |
* @return string Quoted and escaped data | |
*/ | |
public function value($data, $column = null) { | |
if (is_array($data) && !empty($data)) { | |
return array_map( | |
array(&$this, 'value'), | |
$data, array_fill(0, count($data), $column) | |
); | |
} elseif (is_object($data) && isset($data->type, $data->value)) { | |
if ($data->type == 'identifier') { | |
return $this->name($data->value); | |
} elseif ($data->type == 'expression') { | |
return $data->value; | |
} | |
} elseif (in_array($data, array('{$__cakeID__$}', '{$__cakeForeignKey__$}'), true)) { | |
return $data; | |
} | |
if ($data === null || (is_array($data) && empty($data))) { | |
return 'NULL'; | |
} | |
if (empty($column)) { | |
$column = $this->introspectType($data); | |
} | |
switch ($column) { | |
case 'binary': | |
return $this->_connection->quote($data, PDO::PARAM_LOB); | |
break; | |
case 'boolean': | |
return $this->_connection->quote($this->boolean($data, true), PDO::PARAM_BOOL); | |
break; | |
case 'string': | |
case 'text': | |
if ((is_int($data) || $data === '0') || ( | |
is_numeric($data) && strpos($data, ',') === false && | |
$data[0] != '0' && strpos($data, 'e') === false) | |
) { | |
//return $data; | |
} | |
return "'$data'"; | |
default: | |
if ($data === '') { | |
return 'NULL'; | |
} | |
if (is_float($data)) { | |
return sprintf('%F', $data); | |
} | |
if ((is_int($data) || $data === '0') || ( | |
is_numeric($data) && strpos($data, ',') === false && | |
$data[0] != '0' && strpos($data, 'e') === false) | |
) { | |
//return $data; | |
} | |
return "'$data'"; | |
break; | |
} | |
} | |
/** | |
* Builds and generates an SQL statement from an array. Handles final clean-up before conversion. | |
* | |
* @param array $query An array defining an SQL query | |
* @param Model $model The model object which initiated the query | |
* @return string An executable SQL statement | |
* @see DboSource::renderStatement() | |
*/ | |
public function buildStatement($query, $model) { | |
$query = array_merge(array('offset' => null, 'joins' => array()), $query); | |
if (!empty($query['joins'])) { | |
$count = count($query['joins']); | |
for ($i = 0; $i < $count; $i++) { | |
if (is_array($query['joins'][$i])) { | |
$query['conditions'][] = $query['joins'][$i]['conditions']; | |
} | |
} | |
} | |
return parent::buildStatement($query, $model); | |
} | |
/** | |
* Renders a final SQL JOIN statement | |
* | |
* @param array $data | |
* @return string | |
*/ | |
public function renderJoinStatement($data) { | |
extract($data); | |
return trim(", {$table} {$alias}"); | |
} | |
/** | |
* Generates the fields list of an SQL query. | |
* | |
* @param Model $model | |
* @param string $alias Alias table name | |
* @param mixed $fields | |
* @param boolean $quote If false, returns fields array unquoted | |
* @return array | |
*/ | |
public function fields(Model $model, $alias = null, $fields = array(), $quote = true) { | |
if (empty($fields) && !$model->schema(true)) { | |
$fields = '*'; | |
} | |
return parent::fields($model, $alias, $fields, $quote); | |
} | |
///** | |
// * Creates a WHERE clause by parsing given conditions array. Used by DboSource::conditions(). | |
// * | |
// * @param array $conditions Array or string of conditions | |
// * @param boolean $quoteValues If true, values should be quoted | |
// * @param Model $model A reference to the Model instance making the query | |
// * @return string SQL fragment | |
// */ | |
// public function conditionKeysToString($conditions, $quoteValues = true, $model = null) { | |
// $out = array(); | |
// $data = $columnType = null; | |
// $bool = array('and', 'or', 'not', 'and not', 'or not', 'xor', '||', '&&'); | |
// | |
// foreach ($conditions as $key => $value) { | |
// $join = ' AND '; | |
// $not = null; | |
// | |
// if (is_array($value)) { | |
// $valueInsert = ( | |
// !empty($value) && | |
// (substr_count($key, '?') === count($value) || substr_count($key, ':') === count($value)) | |
// ); | |
// } | |
// | |
// if (is_numeric($key) && empty($value)) { | |
// continue; | |
// } elseif (is_numeric($key) && is_string($value)) { | |
// $out[] = $not . $this->_quoteFields($value); | |
// } elseif ((is_numeric($key) && is_array($value)) || in_array(strtolower(trim($key)), $bool)) { | |
// if (in_array(strtolower(trim($key)), $bool)) { | |
// $join = ' ' . strtoupper($key) . ' '; | |
// } else { | |
// $key = $join; | |
// } | |
// $value = $this->conditionKeysToString($value, $quoteValues, $model); | |
// | |
// if (strpos($join, 'NOT') !== false) { | |
// if (strtoupper(trim($key)) === 'NOT') { | |
// $key = 'AND ' . trim($key); | |
// } | |
// $not = 'NOT '; | |
// } | |
// | |
// if (empty($value[1])) { | |
// if ($not) { | |
// $out[] = $not . '(' . $value[0] . ')'; | |
// } else { | |
// $out[] = $value[0] ; | |
// } | |
// } else { | |
// $out[] = '(' . $not . '(' . implode(') ' . strtoupper($key) . ' (', $value) . '))'; | |
// } | |
// } else { | |
// if (is_object($value) && isset($value->type)) { | |
// if ($value->type === 'identifier') { | |
// $data .= $this->name($key) . ' = ' . $this->name($value->value); | |
// } elseif ($value->type === 'expression') { | |
// if (is_numeric($key)) { | |
// $data .= $value->value; | |
// } else { | |
// $data .= $this->name($key) . ' LIKE ' . $value->value; | |
// } | |
// } | |
// } elseif (is_array($value) && !empty($value) && !$valueInsert) { | |
// $keys = array_keys($value); | |
// if ($keys === array_values($keys)) { | |
// $count = count($value); | |
// if ($count === 1) { | |
// $data = $this->_quoteFields($key) . ' LIKE '; | |
// $close = false; | |
// } else { | |
// $data = $this->_quoteFields($key) . ' IN ('; | |
// } | |
// if ($quoteValues) { | |
// if (is_object($model)) { | |
// $columnType = $model->getColumnType($key); | |
// } | |
// $data .= implode(', ', $this->value($value, $columnType)); | |
// } | |
// if (!empty($close)) { | |
// $data .= ')'; | |
// } | |
// } else { | |
// $ret = $this->conditionKeysToString($value, $quoteValues, $model); | |
// if (count($ret) > 1) { | |
// $data = '(' . implode(') AND (', $ret) . ')'; | |
// } elseif (isset($ret[0])) { | |
// $data = $ret[0]; | |
// } | |
// } | |
// } elseif (is_numeric($key) && !empty($value)) { | |
// $data = $this->_quoteFields($value); | |
// } else { | |
// $data = $this->_parseKey($model, trim($key), $value); | |
// } | |
// | |
// if ($data != null) { | |
// $out[] = $data; | |
// $data = null; | |
// } | |
// } | |
// } | |
// return $out; | |
// } | |
// | |
///** | |
// * Extracts a Model.field identifier and an SQL condition operator from a string, formats | |
// * and inserts values, and composes them into an SQL snippet. | |
// * | |
// * @param Model $model Model object initiating the query | |
// * @param string $key An SQL key snippet containing a field and optional SQL operator | |
// * @param mixed $value The value(s) to be inserted in the string | |
// * @return string | |
// */ | |
// protected function _parseKey($model, $key, $value) { | |
// $operatorMatch = '/^(((' . implode(')|(', $this->_sqlOps); | |
// $operatorMatch .= ')\\x20?)|<[>=]?(?![^>]+>)\\x20?|[>=!]{1,3}(?!<)\\x20?)/is'; | |
// $bound = (strpos($key, '?') !== false || (is_array($value) && strpos($key, ':') !== false)); | |
// | |
// if (strpos($key, ' ') === false) { | |
// $operator = 'LIKE'; | |
// } else { | |
// list($key, $operator) = explode(' ', trim($key), 2); | |
// | |
// if (!preg_match($operatorMatch, trim($operator)) && strpos($operator, ' ') !== false) { | |
// $key = $key . ' ' . $operator; | |
// $split = strrpos($key, ' '); | |
// $operator = substr($key, $split); | |
// $key = substr($key, 0, $split); | |
// } | |
// } | |
// | |
// $virtual = false; | |
// if (is_object($model) && $model->isVirtualField($key)) { | |
// $key = $this->_quoteFields($model->getVirtualField($key)); | |
// $virtual = true; | |
// } | |
// | |
// $type = is_object($model) ? $model->getColumnType($key) : null; | |
// $null = $value === null || (is_array($value) && empty($value)); | |
// | |
// if (strtolower($operator) === 'not') { | |
// $data = $this->conditionKeysToString( | |
// array($operator => array($key => $value)), true, $model | |
// ); | |
// return $data[0]; | |
// } | |
// | |
// $value = $this->value($value, $type); | |
// | |
// if (!$virtual && $key !== '?') { | |
// $isKey = (strpos($key, '(') !== false || strpos($key, ')') !== false); | |
// $key = $isKey ? $this->_quoteFields($key) : $this->name($key); | |
// } | |
// | |
// if ($bound) { | |
// return String::insert($key . ' ' . trim($operator), $value); | |
// } | |
// | |
// if (!preg_match($operatorMatch, trim($operator))) { | |
// $operator .= ' LIKE'; | |
// } | |
// $operator = trim($operator); | |
// | |
// if (is_array($value)) { | |
// $value = implode(', ', $value); | |
// | |
// switch ($operator) { | |
// case '=': | |
// $operator = 'IN'; | |
// break; | |
// case '!=': | |
// case '<>': | |
// $operator = 'NOT IN'; | |
// break; | |
// } | |
// $value = "({$value})"; | |
// } elseif ($null || $value === 'NULL') { | |
// switch ($operator) { | |
// case '=': | |
// $operator = 'IS'; | |
// break; | |
// case '!=': | |
// case '<>': | |
// $operator = 'IS NOT'; | |
// break; | |
// } | |
// } | |
// if ($virtual) { | |
// return "({$key}) {$operator} {$value}"; | |
// } | |
// return "{$key} {$operator} {$value}"; | |
// } | |
} |
@sebastienbarre This is for 2.x you might want to take a took at https://gist.github.com/shrimpwagon/5207702 though as my version is customized for thoroughbred
a DATABASE_CONFIG example would be nice to have in the comments at the top of the code page... I am unclear on how this code is suppsed to get:
$this->_connection = new PDO("odbc:{$this->config['driver']}", null, null, $flags);
filled out correctly... I had to put in:
$this->_connection = new PDO("odbc:MSSQLServer", $this->config['login'], $this->config['password'], $flags);
to get the initial connection... have not progressed past that yet... but I do get my odbc mssql connection. that's a start.
@ceeram, with some minor changes, this works perfect for my cake 2.3 app.
It's selecting ok, but i'm having problems on inserting/updating. I might use 2.x Model instead of my 2.3.
Which 2.x was this script made for?
Interesting. Is this for CakePHP 1.3 or 2.x? Thanks