PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/QuickApps/Plugin/Locale/Model/Datasource/ArraySource.php

http://github.com/QuickAppsCMS/QuickApps-CMS
PHP | 480 lines | 333 code | 17 blank | 130 comment | 80 complexity | ce6ef73be8dc96d83aade1664cf48e3e MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, GPL-3.0
  1. <?php
  2. /**
  3. * Array Datasource
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @package QuickApps.Plugin.Locale.Model.Datasource
  16. * @since CakePHP Datasources v 0.3
  17. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  18. */
  19. App::uses('Set', 'Utility');
  20. App::uses('ConnectionManager', 'Model');
  21. /**
  22. * ArraySource
  23. *
  24. * Datasource by Array
  25. */
  26. class ArraySource extends DataSource {
  27. /**
  28. * Description string for this Data Source.
  29. *
  30. * @var string
  31. */
  32. public $description = 'Array Datasource';
  33. /**
  34. * List of requests ("queries")
  35. *
  36. * @var array
  37. */
  38. protected $_requestsLog = array();
  39. /**
  40. * Base Config
  41. *
  42. * @var array
  43. */
  44. public $_baseConfig = array(
  45. 'driver' => '' // Just to avoid DebugKit warning
  46. );
  47. /**
  48. * Start quote
  49. *
  50. * @var string
  51. */
  52. public $startQuote = null;
  53. /**
  54. * End quote
  55. *
  56. * @var string
  57. */
  58. public $endQuote = null;
  59. /**
  60. * Returns a Model description (metadata) or null if none found.
  61. *
  62. * @param Model $model
  63. * @return array Show only id
  64. */
  65. public function describe(&$model) {
  66. return array('id' => array());
  67. }
  68. /**
  69. * List sources
  70. *
  71. * @param mixed $data
  72. * @return boolean Always false. It's not supported
  73. */
  74. public function listSources($data = null) {
  75. return false;
  76. }
  77. /**
  78. * Used to read records from the Datasource. The "R" in CRUD
  79. *
  80. * @param Model $model The model being read.
  81. * @param array $queryData An array of query data used to find the data you want
  82. * @return mixed
  83. */
  84. public function read(&$model, $queryData = array()) {
  85. if (!isset($model->records) || !is_array($model->records) || empty($model->records)) {
  86. $this->_requestsLog[] = array(
  87. 'query' => 'Model ' . $model->alias,
  88. 'error' => __('No records found in model.', true),
  89. 'affected' => 0,
  90. 'numRows' => 0,
  91. 'took' => 0
  92. );
  93. return array($model->alias => array());
  94. }
  95. $startTime = microtime(true);
  96. $data = array();
  97. $i = 0;
  98. $limit = false;
  99. if (!isset($queryData['recursive'])) {
  100. $queryData['recursive'] = $model->recursive;
  101. }
  102. if (is_integer($queryData['limit']) && $queryData['limit'] > 0) {
  103. $limit = $queryData['page'] * $queryData['limit'];
  104. }
  105. foreach ($model->records as $pos => $record) {
  106. // Tests whether the record will be chosen
  107. if (!empty($queryData['conditions'])) {
  108. $queryData['conditions'] = (array)$queryData['conditions'];
  109. if (!$this->conditionsFilter($model, $record, $queryData['conditions'])) {
  110. continue;
  111. }
  112. }
  113. $data[$i][$model->alias] = $record;
  114. $i++;
  115. // Test limit
  116. if ($limit !== false && $i == $limit && empty($queryData['order'])) {
  117. break;
  118. }
  119. }
  120. if ($queryData['fields'] === 'COUNT') {
  121. $this->_registerLog($model, $queryData, microtime(true) - $startTime, 1);
  122. if ($limit !== false) {
  123. $data = array_slice($data, ($queryData['page'] - 1) * $queryData['limit'], $queryData['limit'], false);
  124. }
  125. return array(array(array('count' => count($data))));
  126. }
  127. // Order
  128. if (!empty($queryData['order'])) {
  129. if (is_string($queryData['order'][0])) {
  130. $field = $queryData['order'][0];
  131. $alias = $model->alias;
  132. if (strpos($field, '.') !== false) {
  133. list($alias, $field) = explode('.', $field, 2);
  134. }
  135. if ($alias === $model->alias) {
  136. $sort = 'ASC';
  137. if (strpos($field, ' ') !== false) {
  138. list($field, $sort) = explode(' ', $field, 2);
  139. }
  140. $data = Set::sort($data, '{n}.' . $model->alias . '.' . $field, $sort);
  141. }
  142. }
  143. }
  144. // Limit
  145. if ($limit !== false) {
  146. $data = array_slice($data, ($queryData['page'] - 1) * $queryData['limit'], $queryData['limit'], false);
  147. }
  148. // Filter fields
  149. if (!empty($queryData['fields'])) {
  150. $listOfFields = array();
  151. foreach ((array)$queryData['fields'] as $field) {
  152. if (strpos($field, '.') !== false) {
  153. list($alias, $field) = explode('.', $field, 2);
  154. if ($alias !== $model->alias) {
  155. continue;
  156. }
  157. }
  158. $listOfFields[] = $field;
  159. }
  160. foreach ($data as $id => $record) {
  161. foreach ($record[$model->alias] as $field => $value) {
  162. if (!in_array($field, $listOfFields)) {
  163. unset($data[$id][$model->alias][$field]);
  164. }
  165. }
  166. }
  167. }
  168. $this->_registerLog($model, $queryData, microtime(true) - $startTime, count($data));
  169. $_associations = $model->_associations;
  170. if ($queryData['recursive'] > -1) {
  171. foreach ($_associations as $type) {
  172. foreach ($model->{$type} as $assoc => $assocData) {
  173. $linkModel =& $model->{$assoc};
  174. if ($model->useDbConfig == $linkModel->useDbConfig) {
  175. $db =& $this;
  176. } else {
  177. $db =& ConnectionManager::getDataSource($linkModel->useDbConfig);
  178. }
  179. if (isset($db)) {
  180. if (method_exists($db, 'queryAssociation')) {
  181. $stack = array($assoc);
  182. $db->queryAssociation($model, $linkModel, $type, $assoc, $assocData, $queryData, true, $data, $queryData['recursive'] - 1, $stack);
  183. }
  184. unset($db);
  185. }
  186. }
  187. }
  188. }
  189. if ($model->findQueryType === 'first') {
  190. if (!isset($data[0])) {
  191. $data = array();
  192. } else {
  193. $data = array($data[0]);
  194. }
  195. }
  196. return $data;
  197. }
  198. /**
  199. * Conditions Filter
  200. *
  201. * @param Model $model
  202. * @param string $record
  203. * @param array $conditions
  204. * @param boolean $or
  205. * @return void
  206. */
  207. public function conditionsFilter(&$model, $record, $conditions, $or = false) {
  208. foreach ($conditions as $field => $value) {
  209. $return = null;
  210. if ($value === '') {
  211. continue;
  212. }
  213. if (is_array($value) && in_array(strtoupper($field), array('AND', 'NOT', 'OR'))) {
  214. switch (strtoupper($field)) {
  215. case 'AND':
  216. $return = $this->conditionsFilter($model, $record, $value);
  217. break;
  218. case 'NOT':
  219. $return = !$this->conditionsFilter($model, $record, $value);
  220. break;
  221. case 'OR':
  222. $return = $this->conditionsFilter($model, $record, $value, true);
  223. break;
  224. }
  225. } else {
  226. if (is_array($value)) {
  227. $type = 'IN';
  228. } elseif (preg_match('/^(\w+\.?\w+)\s+(=|!=|LIKE|IN)$/i', $field, $matches)) {
  229. $field = $matches[1];
  230. $type = strtoupper($matches[2]);
  231. } elseif (preg_match('/^(\w+\.?\w+)\s+(=|!=|LIKE|IN)\s+(.*)$/i', $value, $matches)) {
  232. $field = $matches[1];
  233. $type = strtoupper($matches[2]);
  234. $value = $matches[3];
  235. } else {
  236. $type = '=';
  237. }
  238. if (strpos($field, '.') !== false) {
  239. list($alias, $field) = explode('.', $field, 2);
  240. if ($alias != $model->alias) {
  241. continue;
  242. }
  243. }
  244. switch ($type) {
  245. case '=':
  246. $return = (isset($record[$field]) && $record[$field] == $value);
  247. break;
  248. case '!=':
  249. $return = (!isset($record[$field]) || $record[$field] != $value);
  250. break;
  251. case 'LIKE':
  252. $value = preg_replace(array('#(^|[^\\\\])_#', '#(^|[^\\\\])%#'), array('$1.', '$1.*'), $value);
  253. $return = (isset($record[$field]) && preg_match('#^' . $value . '$#i', $record[$field]));
  254. break;
  255. case 'IN':
  256. $items = array();
  257. if (is_array($value)) {
  258. $items = $value;
  259. } elseif (preg_match('/^\(\w+(,\s*\w+)*\)$/', $value)) {
  260. $items = explode(',', trim($value, '()'));
  261. $items = array_map('trim', $items);
  262. }
  263. $return = (isset($record[$field]) && in_array($record[$field], (array)$items));
  264. break;
  265. }
  266. }
  267. if ($return === $or) {
  268. return $or;
  269. }
  270. }
  271. return !$or;
  272. }
  273. /**
  274. * Returns an calculation
  275. *
  276. * @param model $model
  277. * @param string $type Lowercase name type, i.e. 'count' or 'max'
  278. * @param array $params Function parameters (any values must be quoted manually)
  279. * @return string Calculation method
  280. */
  281. public function calculate(&$model, $type, $params = array()) {
  282. return 'COUNT';
  283. }
  284. /**
  285. * Implemented to make the datasource work with Model::find('count').
  286. *
  287. * @return boolean Always false;
  288. */
  289. public function expression() {
  290. return false;
  291. }
  292. /**
  293. * Queries associations. Used to fetch results on recursive models.
  294. *
  295. * @param Model $model Primary Model object
  296. * @param Model $linkModel Linked model that
  297. * @param string $type Association type, one of the model association types ie. hasMany
  298. * @param unknown_type $association
  299. * @param unknown_type $assocData
  300. * @param array $queryData
  301. * @param boolean $external Whether or not the association query is on an external datasource.
  302. * @param array $resultSet Existing results
  303. * @param integer $recursive Number of levels of association
  304. * @param array $stack
  305. */
  306. public function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) {
  307. $assocData = array_merge(array('conditions' => null, 'fields' => null, 'order' => null), $assocData);
  308. if (isset($queryData['conditions'])) {
  309. $assocData['conditions'] = array_merge((array)$queryData['conditions'], (array)$assocData['conditions']);
  310. }
  311. if (isset($queryData['fields'])) {
  312. $assocData['fields'] = array_merge((array)$queryData['fields'], (array)$assocData['fields']);
  313. }
  314. foreach ($resultSet as $id => $result) {
  315. if (!array_key_exists($model->alias, $result)) {
  316. continue;
  317. }
  318. if ($type === 'belongsTo' && array_key_exists($assocData['foreignKey'], $result[$model->alias])) {
  319. $find = $model->{$association}->find('first', array(
  320. 'conditions' => array_merge((array)$assocData['conditions'], array($model->{$association}->primaryKey => $result[$model->alias][$assocData['foreignKey']])),
  321. 'fields' => $assocData['fields'],
  322. 'order' => $assocData['order'],
  323. 'recursive' => $recursive
  324. ));
  325. } elseif (in_array($type, array('hasOne', 'hasMany')) && array_key_exists($model->primaryKey, $result[$model->alias])) {
  326. if ($type === 'hasOne') {
  327. $find = $model->{$association}->find('first', array(
  328. 'conditions' => array_merge((array)$assocData['conditions'], array($association . '.' . $assocData['foreignKey'] => $result[$model->alias][$model->primaryKey])),
  329. 'fields' => $assocData['fields'],
  330. 'order' => $assocData['order'],
  331. 'recursive' => $recursive
  332. ));
  333. } else {
  334. $find = $model->{$association}->find('all', array(
  335. 'conditions' => array_merge((array)$assocData['conditions'], array($association . '.' . $assocData['foreignKey'] => $result[$model->alias][$model->primaryKey])),
  336. 'fields' => $assocData['fields'],
  337. 'order' => $assocData['order'],
  338. 'recursive' => $recursive
  339. ));
  340. $find = array(
  341. $association => Set::extract('{n}.' . $association, $find)
  342. );
  343. }
  344. } elseif ($type === 'hasAndBelongsToMany' && array_key_exists($model->primaryKey, $result[$model->alias])) {
  345. $hABTMModel = ClassRegistry::init($assocData['with']);
  346. $ids = $hABTMModel->find('all', array(
  347. 'fields' => array(
  348. $assocData['with'] . '.' . $assocData['associationForeignKey']
  349. ),
  350. 'conditions' => array(
  351. $assocData['with'] . '.' . $assocData['foreignKey'] => $result[$model->alias][$model->primaryKey]
  352. )
  353. ));
  354. $ids = Set::extract('{n}.' . $assocData['with'] . '.' . $assocData['associationForeignKey'], $ids);
  355. $find = $model->{$association}->find('all', array(
  356. 'conditions' => array_merge((array)$assocData['conditions'], array($association . '.' . $linkModel->primaryKey => $ids)),
  357. 'fields' => $assocData['fields'],
  358. 'order' => $assocData['order'],
  359. 'recursive' => $recursive
  360. ));
  361. $find = array(
  362. $association => Set::extract('{n}.' . $association, $find)
  363. );
  364. }
  365. if (empty($find)) {
  366. $find = array($association => array());
  367. }
  368. $resultSet[$id] = array_merge($find, $resultSet[$id]);
  369. }
  370. }
  371. /**
  372. * Get the query log as an array.
  373. *
  374. * @param boolean $sorted Get the queries sorted by time taken, defaults to false.
  375. * @param boolean $clear Clear after return logs
  376. * @return array Array of queries run as an array
  377. */
  378. public function getLog($sorted = false, $clear = true) {
  379. if ($sorted) {
  380. $log = sortByKey($this->_requestsLog, 'took', 'desc', SORT_NUMERIC);
  381. } else {
  382. $log = $this->_requestsLog;
  383. }
  384. if ($clear) {
  385. $this->_requestsLog = array();
  386. }
  387. return array('log' => $log, 'count' => count($log), 'time' => array_sum(Set::extract('{n}.took', $log)));
  388. }
  389. /**
  390. * Generate a log registry
  391. *
  392. * @param object $model
  393. * @param array $queryData
  394. * @param float $took
  395. * @param integer $numRows
  396. * @return void
  397. */
  398. public function _registerLog(&$model, &$queryData, $took, $numRows) {
  399. if (!Configure::read()) {
  400. return;
  401. }
  402. $this->_requestsLog[] = array(
  403. 'query' => $this->_pseudoSelect($model, $queryData),
  404. 'error' => '',
  405. 'affected' => 0,
  406. 'numRows' => $numRows,
  407. 'took' => round($took, 3)
  408. );
  409. }
  410. /**
  411. * Generate a pseudo select to log
  412. *
  413. * @param object $model Model
  414. * @param array $queryData Query data sended by find
  415. * @return string Pseudo query
  416. */
  417. protected function _pseudoSelect(&$model, &$queryData) {
  418. $out = '(symbolic) SELECT ';
  419. if (empty($queryData['fields'])) {
  420. $out .= '*';
  421. } elseif ($queryData['fields']) {
  422. $out .= 'COUNT(*)';
  423. } else {
  424. $out .= implode(', ', $queryData['fields']);
  425. }
  426. $out .= ' FROM ' . $model->alias;
  427. if (!empty($queryData['conditions'])) {
  428. $out .= ' WHERE';
  429. foreach ($queryData['conditions'] as $id => $condition) {
  430. if (empty($condition)) {
  431. continue;
  432. }
  433. if (is_array($condition)) {
  434. $condition = '(' . implode(', ', $condition) . ')';
  435. if (strpos($id, ' ') === false) {
  436. $id .= ' IN';
  437. }
  438. }
  439. if (is_string($id)) {
  440. if (strpos($id, ' ') !== false) {
  441. $condition = $id . ' ' . $condition;
  442. } else {
  443. $condition = $id . ' = ' . $condition;
  444. }
  445. }
  446. if (preg_match('/^(\w+\.)?\w+ /', $condition, $matches)) {
  447. if (!empty($matches[1]) && substr($matches[1], 0, -1) !== $model->alias) {
  448. continue;
  449. }
  450. }
  451. $out .= ' (' . $condition . ') &&';
  452. }
  453. $out = substr($out, 0, -3);
  454. }
  455. if (!empty($queryData['order'][0])) {
  456. $out .= ' ORDER BY ' . implode(', ', $queryData['order']);
  457. }
  458. if (!empty($queryData['limit'])) {
  459. $out .= ' LIMIT ' . (($queryData['page'] - 1) * $queryData['limit']) . ', ' . $queryData['limit'];
  460. }
  461. return $out;
  462. }
  463. }