'' // Just to avoid DebugKit warning ); /** * Start quote * * @var string */ public $startQuote = null; /** * End quote * * @var string */ public $endQuote = null; /** * Imitation of DboSource method. * * @param mixed $data Either a string with a column to quote. An array of columns * to quote. * @return string SQL field */ public function name($data) { if (is_object($data) && isset($data->type)) { return $data->value; } if ($data === '*') { return '*'; } if (is_array($data)) { foreach ($data as $i => $dataItem) { $data[$i] = $this->name($dataItem); } return $data; } return (string)$data; } /** * Returns a Model description (metadata) or null if none found. * * @param Model $model * @return array Show only id */ public function describe($model) { return array('id' => array()); } /** * List sources * * @param mixed $data * @return boolean Always false. It's not supported */ public function listSources($data = null) { return false; } /** * Used to read records from the Datasource. The "R" in CRUD * * @param Model $model The model being read. * @param array $queryData An array of query data used to find the data you want * @param null $recursive * @return mixed */ public function read(Model $model, $queryData = array(), $recursive = null) { if (!isset($model->records) || !is_array($model->records) || empty($model->records)) { $this->_requestsLog[] = array( 'query' => 'Model ' . $model->alias, 'error' => __('No records found in model.'), 'affected' => 0, 'numRows' => 0, 'took' => 0 ); return array($model->alias => array()); } $startTime = microtime(true); $data = array(); $i = 0; $limit = false; if ($recursive === null && isset($queryData['recursive'])) { $recursive = $queryData['recursive']; } if ($recursive !== null) { $_recursive = $model->recursive; $model->recursive = $recursive; } if (is_int($queryData['limit']) && $queryData['limit'] > 0) { $limit = $queryData['page'] * $queryData['limit']; } foreach ($model->records as $pos => $record) { // Tests whether the record will be chosen if (!empty($queryData['conditions'])) { $queryData['conditions'] = (array)$queryData['conditions']; if (!$this->conditionsFilter($model, $record, $queryData['conditions'])) { continue; } } $data[$i][$model->alias] = $record; $i++; // Test limit if ($limit !== false && $i == $limit && empty($queryData['order'])) { break; } } if ($queryData['fields'] === 'COUNT') { $this->_registerLog($model, $queryData, microtime(true) - $startTime, 1); if ($limit !== false) { $data = array_slice($data, ($queryData['page'] - 1) * $queryData['limit'], $queryData['limit'], false); } return array(array(array('count' => count($data)))); } // Order if (!empty($queryData['order'])) { if (is_string($queryData['order'][0])) { $field = $queryData['order'][0]; $alias = $model->alias; if (strpos($field, '.') !== false) { list($alias, $field) = explode('.', $field, 2); } if ($alias === $model->alias) { $sort = 'ASC'; if (strpos($field, ' ') !== false) { list($field, $sort) = explode(' ', $field, 2); } if ($data) { $data = Hash::sort($data, '{n}.' . $model->alias . '.' . $field, $sort); } } } } // Limit if ($limit !== false) { $data = array_slice($data, ($queryData['page'] - 1) * $queryData['limit'], $queryData['limit'], false); } // Filter fields if (!empty($queryData['fields'])) { $listOfFields = array(); foreach ((array)$queryData['fields'] as $field) { if (strpos($field, '.') !== false) { list($alias, $field) = explode('.', $field, 2); if ($alias !== $model->alias) { continue; } } $listOfFields[] = $field; } foreach ($data as $id => $record) { foreach ($record[$model->alias] as $field => $value) { if (!in_array($field, $listOfFields)) { unset($data[$id][$model->alias][$field]); } } } } $this->_registerLog($model, $queryData, microtime(true) - $startTime, count($data)); $associations = $model->_associations; if ($model->recursive > -1) { foreach ($associations as $type) { foreach ($model->{$type} as $assoc => $assocData) { $linkModel = $model->{$assoc}; if ($model->useDbConfig == $linkModel->useDbConfig) { $db = $this; } else { $db = ConnectionManager::getDataSource($linkModel->useDbConfig); } if (isset($db)) { if (method_exists($db, 'queryAssociation')) { $stack = array($assoc); $db->queryAssociation($model, $linkModel, $type, $assoc, $assocData, $queryData, true, $data, $model->recursive - 1, $stack); } unset($db); } } } } if ($recursive !== null) { $model->recursive = $_recursive; } return $data; } /** * Conditions Filter * * @param Model $model * @param string $record * @param array $conditions * @param boolean $or * @return boolean */ public function conditionsFilter(Model $model, $record, $conditions, $or = false) { foreach ($conditions as $field => $value) { $return = null; if ($value === '') { continue; } if (is_array($value) && in_array(strtoupper($field), array('AND', 'NOT', 'OR'))) { switch (strtoupper($field)) { case 'AND': $return = $this->conditionsFilter($model, $record, $value); break; case 'NOT': $return = !$this->conditionsFilter($model, $record, $value); break; case 'OR': $return = $this->conditionsFilter($model, $record, $value, true); break; } } else { if (is_array($value)) { $type = 'IN'; } elseif (preg_match('/^(\w+\.?\w+)\s+(=|!=|LIKE|IN|<|<=|>|>=)\s*$/i', $field, $matches)) { $field = $matches[1]; $type = strtoupper($matches[2]); } elseif (preg_match('/^(\w+\.?\w+)\s+(=|!=|LIKE|IN|<|<=|>|>=)\s+(.*)$/i', $value, $matches)) { $field = $matches[1]; $type = strtoupper($matches[2]); $value = $matches[3]; } else { $type = '='; } if (strpos($field, '.') !== false) { list($alias, $field) = explode('.', $field, 2); if ($alias != $model->alias) { continue; } } switch ($type) { case '<': $return = (array_key_exists($field, $record) && $record[$field] < $value); break; case '<=': $return = (array_key_exists($field, $record) && $record[$field] <= $value); break; case '=': $return = (array_key_exists($field, $record) && $record[$field] == $value); break; case '>': $return = (array_key_exists($field, $record) && $record[$field] > $value); break; case '>=': $return = (array_key_exists($field, $record) && $record[$field] >= $value); break; case '!=': $return = (!array_key_exists($field, $record) || $record[$field] != $value); break; case 'LIKE': $value = preg_replace(array('#(^|[^\\\\])_#', '#(^|[^\\\\])%#'), array('$1.', '$1.*'), $value); $return = (isset($record[$field]) && preg_match('#^' . $value . '$#i', $record[$field])); break; case 'IN': $items = array(); if (is_array($value)) { $items = $value; } elseif (preg_match('/^\(\w+(,\s*\w+)*\)$/', $value)) { $items = explode(',', trim($value, '()')); $items = array_map('trim', $items); } $return = (array_key_exists($field, $record) && in_array($record[$field], (array)$items)); break; } } if ($return === $or) { return $or; } } return !$or; } /** * Returns an calculation * * @param model $model * @param string $type Lowercase name type, i.e. 'count' or 'max' * @param array $params Function parameters (any values must be quoted manually) * @return string Calculation method */ public function calculate(Model $model, $type, $params = array()) { return 'COUNT'; } /** * Implemented to make the datasource work with Model::find('count'). * * @return boolean Always false; */ public function expression() { return false; } /** * Queries associations. Used to fetch results on recursive models. * * @param Model $model Primary Model object * @param Model $linkModel Linked model that * @param string $type Association type, one of the model association types ie. hasMany * @param string $association The name of the association * @param array $assocData The data about the association * @param array $queryData * @param boolean $external Whether or not the association query is on an external datasource. * @param array $resultSet Existing results * @param integer $recursive Number of levels of association * @param array $stack */ public function queryAssociation(Model $model, Model $linkModel, $type, $association, $assocData, &$queryData, $external, &$resultSet, $recursive, $stack) { $assocData = array_merge(array('conditions' => null, 'fields' => null, 'order' => null), $assocData); if (isset($queryData['fields'])) { $assocData['fields'] = array_filter(array_merge((array)$queryData['fields'], (array)$assocData['fields'])); } if (isset($queryData['conditions'])) { $assocData['conditions'] = array_filter(array_merge((array)$queryData['conditions'], (array)$assocData['conditions'])); } $query = array( 'fields' => array_filter((array)$assocData['fields']), 'conditions' => array_filter((array)$assocData['conditions']), 'group' => null, 'order' => $assocData['order'], 'limit' => isset($assocData['limit']) ? $assocData['limit'] : null, 'page' => 1, 'offset' => null, 'callbacks' => true, 'recursive' => $recursive === 0 ? -1 : $recursive ); foreach ($resultSet as &$record) { $data = array(); if ($type === 'belongsTo') { if (isset($record[$model->alias][$assocData['foreignKey']])) { $conditions = array_merge($query['conditions'], array($linkModel->alias . '.' . $linkModel->primaryKey => $record[$model->alias][$assocData['foreignKey']])); $limit = 1; $data = $this->read($linkModel, compact('conditions', 'limit') + $query); } } elseif (($type === 'hasMany' || $type === 'hasOne') && $model->recursive > 0) { $conditions = array_merge($query['conditions'], array($linkModel->alias . '.' . $assocData['foreignKey'] => $record[$model->alias][$model->primaryKey])); $limit = $type === 'hasOne' ? 1 : $query['limit']; $data = $this->read($linkModel, compact('conditions', 'limit') + $query); } elseif ($type === 'hasAndBelongsToMany' && $model->recursive > 0) { $joinModel = ClassRegistry::init($assocData['with']); $fields = array($joinModel->alias . '.' . $assocData['associationForeignKey']); $conditions = array($joinModel->alias . '.' . $assocData['foreignKey'] => $record[$model->alias][$model->primaryKey]); $recursive = -1; $ids = $joinModel->getDataSource()->read($joinModel, compact('fields', 'conditions', 'recursive') + $query); if ($ids) { $ids = Hash::extract($ids, "{n}.{$joinModel->alias}.{$assocData['associationForeignKey']}"); $conditions = array_merge($query['conditions'], array($linkModel->alias . '.' . $linkModel->primaryKey => $ids)); $data = $this->read($linkModel, compact('conditions') + $query); } } else { continue; } if (!$data) { $record += array($linkModel->alias => array()); continue; } $formatted = array(); foreach ($data as $associated) { foreach ($associated as $modelName => $associatedData) { if ($modelName === $linkModel->alias) { continue; } $associated[$linkModel->alias][$modelName] = $associatedData; unset($associated[$modelName]); } $formatted[] = $associated; } if ($type === 'hasOne' || $type === 'belongsTo') { $record += array($linkModel->alias => $formatted[0][$linkModel->alias]); continue; } $record += array($linkModel->alias => Hash::extract($formatted, "{n}.{$linkModel->alias}")); } } /** * Get the query log as an array. * * @param boolean $sorted Get the queries sorted by time taken, defaults to false. * @param boolean $clear Clear after return logs * @return array Array of queries run as an array */ public function getLog($sorted = false, $clear = true) { if ($sorted) { $log = sortByKey($this->_requestsLog, 'took', 'desc', SORT_NUMERIC); } else { $log = $this->_requestsLog; } if ($clear) { $this->_requestsLog = array(); } return array('log' => $log, 'count' => count($log), 'time' => array_sum(Hash::extract($log, '{n}.took'))); } /** * Generate a log registry * * @param Model $model * @param array $queryData * @param float $took * @param integer $numRows * @return void */ protected function _registerLog(Model $model, &$queryData, $took, $numRows) { if (!Configure::read('debug')) { return; } $this->_requestsLog[] = array( 'query' => $this->_pseudoSelect($model, $queryData), 'error' => '', 'affected' => 0, 'numRows' => $numRows, 'took' => round($took, 3) ); } /** * Generate a pseudo select to log * * @param Model $model Model * @param array $queryData Query data sent by find * @return string Pseudo query */ protected function _pseudoSelect(Model $model, &$queryData) { $out = '(symbolic) SELECT '; if (empty($queryData['fields'])) { $out .= '*'; } elseif ($queryData['fields']) { $out .= 'COUNT(*)'; } else { $out .= implode(', ', $queryData['fields']); } $out .= ' FROM ' . $model->alias; if (!empty($queryData['conditions'])) { $out .= ' WHERE'; foreach ($queryData['conditions'] as $id => $condition) { if (empty($condition)) { continue; } if (is_array($condition)) { $condition = '(' . implode(', ', $condition) . ')'; if (strpos($id, ' ') === false) { $id .= ' IN'; } } if (is_string($id)) { if (strpos($id, ' ') !== false) { $condition = $id . ' ' . $condition; } else { $condition = $id . ' = ' . $condition; } } if (preg_match('/^(\w+\.)?\w+ /', $condition, $matches)) { if (!empty($matches[1]) && substr($matches[1], 0, -1) !== $model->alias) { continue; } } $out .= ' (' . $condition . ') &&'; } $out = substr($out, 0, -3); } if (!empty($queryData['order'][0])) { $order = $queryData['order']; if (is_array($order[0])) { $new = array(); foreach ($order[0] as $field => $direction) { $new[] = "$field $direction"; } $order = $new; } $out .= ' ORDER BY ' . implode(', ', $order); } if (!empty($queryData['limit'])) { $out .= ' LIMIT ' . (($queryData['page'] - 1) * $queryData['limit']) . ', ' . $queryData['limit']; } return $out; } }