PageRenderTime 61ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/Gongo/Db/Mapper.php

https://bitbucket.org/no22/gongodb
PHP | 597 lines | 539 code | 58 blank | 0 comment | 77 complexity | b8a4f6f1437fef68918b2b23373a16f9 MD5 | raw file
  1. <?php
  2. class Gongo_Db_Mapper
  3. {
  4. protected $db;
  5. protected $table;
  6. protected $queryWriter;
  7. protected $pk = 'id';
  8. protected $entityClass = 'Gongo_Bean';
  9. protected $namedScopes = array();
  10. protected $autoPopulate = true;
  11. protected $createdDateColumn = 'created';
  12. protected $modifiedDateColumn = 'modified';
  13. protected $quote = '`';
  14. public $relation = array();
  15. protected $defaultTableAlias = 't';
  16. protected $joinMapper = null;
  17. protected $currentArgs = array();
  18. protected $argsCount = array();
  19. protected $strict = null;
  20. protected $tableAlias = array();
  21. static function joinHandler($self, $keys, $q = null, $inner = false)
  22. {
  23. $q = is_null($q) ? $self->query() : $q ;
  24. $fromTable = $self->identifier($self->tableName());
  25. $fromAlias = $self->identifier($self->defaultTableAlias());
  26. $from = $q->getQuery('from');
  27. if (empty($from)) {
  28. $q->from($fromTable . " AS {$fromAlias}");
  29. }
  30. foreach ($keys as $key => $obj) {
  31. if (is_int($key)) {
  32. $relMapper = $self->getRelationMapper($obj);
  33. $key = $obj;
  34. } else {
  35. if (is_string($obj)) {
  36. $obj = $self->getRelationMapperInstance($obj);
  37. }
  38. $relMapper = $obj;
  39. }
  40. if ($relMapper) {
  41. $joinTable = $self->identifier($relMapper->tableName());
  42. $joinAlias = $self->identifier($key);
  43. $pk = $self->pkId($relMapper);
  44. $fkey = $self->identifier($self->foreignKey($key));
  45. if ($inner) {
  46. $q->innerJoin("{$joinTable} AS {$joinAlias} ON {$fromAlias}.{$fkey} = {$joinAlias}.{$pk}");
  47. } else {
  48. $q->join("{$joinTable} AS {$joinAlias} ON {$fromAlias}.{$fkey} = {$joinAlias}.{$pk}");
  49. }
  50. }
  51. }
  52. return $q;
  53. }
  54. function __construct($db = null, $table = null, $pk = null, $namedScopes = null, $queryWriter = null)
  55. {
  56. if (!is_null($db)) $this->db($db);
  57. if (!is_null($table)) $this->table($table);
  58. if (!is_null($pk)) $this->primaryKey($pk);
  59. $inheritedNamedScopes = $this->inheritNamedScopes();
  60. if (!is_null($namedScopes)) {
  61. $inheritedNamedScopes = array_merge($inheritedNamedScopes, $namedScopes);
  62. }
  63. $this->namedScopes($inheritedNamedScopes);
  64. $queryWriter = is_null($queryWriter) ? Gongo_Locator::get('Gongo_Db_QueryWriter') : $queryWriter ;
  65. $this->queryWriter($queryWriter);
  66. $this->setQueryWriterDefaultTableName();
  67. }
  68. public function inheritNamedScopes($sClass = null)
  69. {
  70. $sClass = is_null($sClass) ? get_class($this) : $sClass ;
  71. $aVars = get_class_vars($sClass);
  72. $aNamedScopes = isset($aVars['namedScopes']) ? $aVars['namedScopes'] : array() ;
  73. $sParent = get_parent_class($sClass);
  74. if (!$sParent) return $aNamedScopes;
  75. $aParentNamedScopes = $this->inheritNamedScopes($sParent);
  76. return array_merge($aParentNamedScopes, $aNamedScopes);
  77. }
  78. function setQueryWriterDefaultTableName()
  79. {
  80. $db = $this->db();
  81. if (!is_null($db) && !$this->table()) {
  82. $this->queryWriter()->defaultTable($this->tableName());
  83. }
  84. }
  85. function db($value = null)
  86. {
  87. if (is_null($value)) return $this->db;
  88. $this->db = $value;
  89. $this->setQueryWriterDefaultTableName();
  90. return $this;
  91. }
  92. function table($value = null)
  93. {
  94. if (is_null($value)) return $this->table;
  95. $this->table = $value;
  96. $this->setQueryWriterDefaultTableName();
  97. return $this;
  98. }
  99. function tableAlias($value = null)
  100. {
  101. if (is_null($value)) return $this->tableAlias;
  102. $this->tableAlias = $value;
  103. return $this;
  104. }
  105. function tableName()
  106. {
  107. return $this->db()->tableName($this->table());
  108. }
  109. function primaryKey($value = null)
  110. {
  111. if (is_null($value)) return $this->pk;
  112. $this->pk = $value;
  113. return $this;
  114. }
  115. function entityClass($value = null)
  116. {
  117. if (is_null($value)) return $this->entityClass;
  118. $this->entityClass = $value;
  119. return $this;
  120. }
  121. function namedScopes($value = null)
  122. {
  123. if (is_null($value)) return $this->namedScopes;
  124. $this->namedScopes = $value;
  125. return $this;
  126. }
  127. function queryWriter($value = null)
  128. {
  129. if (is_null($value)) return $this->queryWriter;
  130. $this->queryWriter = $value;
  131. return $this;
  132. }
  133. function autoPopulate($value = null)
  134. {
  135. if (is_null($value)) return $this->autoPopulate;
  136. $this->autoPopulate = $value;
  137. return $this;
  138. }
  139. function createdDateColumn($value =null)
  140. {
  141. if (is_null($value)) return $this->createdDateColumn;
  142. $this->createdDateColumn = $value;
  143. return $this;
  144. }
  145. function modifiedDateColumn($value = null)
  146. {
  147. if (is_null($value)) return $this->modifiedDateColumn;
  148. $this->modifiedDateColumn = $value;
  149. return $this;
  150. }
  151. function relation($value = null)
  152. {
  153. if (is_null($value)) return $this->relation;
  154. $this->relation = $value;
  155. return $this;
  156. }
  157. function addRelation($value)
  158. {
  159. $this->relation[] = $value;
  160. return $this;
  161. }
  162. function currentDateTime()
  163. {
  164. return date('Y-m-d H:i:s');
  165. }
  166. function joinMapper($value = null)
  167. {
  168. if (is_null($value)) return $this->joinMapper;
  169. $this->joinMapper = $value;
  170. return $this;
  171. }
  172. function strict($value = null)
  173. {
  174. if (is_null($value)) return $this->strict;
  175. $this->strict = $value;
  176. return $this;
  177. }
  178. function defaultTableAlias($value = null)
  179. {
  180. if (is_null($value)) return $this->defaultTableAlias;
  181. $this->defaultTableAlias = $value;
  182. return $this;
  183. }
  184. function finder($fields = null, $inner = false) { return $this->query($fields, $inner); }
  185. function q($fields = null, $inner = false) { return $this->query($fields, $inner); }
  186. function select($fields = null, $inner = false) { return $this->query($fields, $inner); }
  187. function query($fields = null, $inner = false, $q = null)
  188. {
  189. $q = is_null($q) ? Gongo_Locator::get('Gongo_Db_QueryBuilder', $this) : $q ;
  190. return $this->_prepareFields($q, $fields, $inner);
  191. }
  192. function setFromTable($query)
  193. {
  194. if (!isset($query['from'])) {
  195. $tableName = $this->tableName();
  196. if ($tableName) {
  197. $query['from'] = $tableName;
  198. }
  199. }
  200. return $query;
  201. }
  202. function setSelectColumn($query)
  203. {
  204. if (!isset($query['select'])) {
  205. $query['select'] = '*';
  206. }
  207. return $query;
  208. }
  209. function bean($ary = array(), $entityClass = null)
  210. {
  211. $entityClass = is_null($entityClass) ? $this->entityClass() : $entityClass ;
  212. return Gongo_Locator::get($entityClass, $ary);
  213. }
  214. function _replaceTableAliasCallback($m)
  215. {
  216. $tableName = isset($this->tableAlias[$m[1]]) ? $this->tableAlias[$m[1]] : $m[1] ;
  217. return $this->db()->tablePrefix() . $tableName;
  218. }
  219. function _replaceTableAlias($str)
  220. {
  221. if (!$this->tableAlias) return preg_replace('/\[(.*?)\]/', $this->db()->tablePrefix() . '$1', $str);
  222. return preg_replace_callback(
  223. '/\[(.*?)\]/', array($this, '_replaceTableAliasCallback'), $str
  224. );
  225. }
  226. function _replaceTableName($m)
  227. {
  228. $table = $this->_replaceTableAlias($m[1]);
  229. return $table . $m[2];
  230. }
  231. function replaceTableName($sql)
  232. {
  233. if ($this->db()->tablePrefix() === '' || (strpos($sql, '"') !== false && strpos($sql, "'") !== false)) {
  234. if (strpos($sql, '[') === false) return $sql;
  235. return $this->_replaceTableAlias($sql);
  236. }
  237. return preg_replace_callback(
  238. '/([^\'"]*)(\'(?:[^\'\\\\]|\\\\\')*?\'|"(?:[^"\\\\]|\\\\")*?"|$)/',
  239. array($this, '_replaceTableName'), $sql
  240. );
  241. }
  242. function _replaceParams($m)
  243. {
  244. $key = $m[1];
  245. if (!isset($this->argsCount[$key])) {
  246. $this->argsCount[$key] = 1;
  247. if (isset($this->currentArgs[$key . '#'])) {
  248. $this->currentArgs[$key] = $this->currentArgs[$key . '#'][0];
  249. }
  250. return $m[0];
  251. }
  252. $c = $this->argsCount[$key]++;
  253. $arg = null;
  254. if (isset($this->currentArgs[$key . '#'])) {
  255. $arg = $this->currentArgs[$key . '#'][$c];
  256. } else if (isset($this->currentArgs[$key])) {
  257. $arg = $this->currentArgs[$key];
  258. }
  259. $name = $key . '___' . $c;
  260. $this->currentArgs[$name] = $arg;
  261. return $name;
  262. }
  263. function _replaceRepeatedParams($m)
  264. {
  265. $sql = preg_replace_callback(
  266. '/(:\w+)/', array($this, '_replaceParams'), $m[1]
  267. );
  268. return $sql . $m[2];
  269. }
  270. function replaceRepeatedParams($sql, $args)
  271. {
  272. $this->currentArgs = $args;
  273. $this->argsCount = array();
  274. $sql = preg_replace_callback(
  275. '/([^\'"]*)(\'(?:[^\'\\\\]|\\\\\')*?\'|"(?:[^"\\\\]|\\\\")*?"|$)/',
  276. array($this, '_replaceRepeatedParams'), $sql
  277. );
  278. foreach ($this->currentArgs as $k => $v) {
  279. if (strpos($k, '#', strlen($k) - 1) !== false) unset($this->currentArgs[$k]);
  280. }
  281. $unusedArgs = array_diff_key($args, $this->argsCount);
  282. $currentArgs = array_diff_key($this->currentArgs, $unusedArgs);
  283. return array($sql, $currentArgs);
  284. }
  285. function _prepareFields($q, $fields, $inner = false)
  286. {
  287. if (is_null($fields)) return $q;
  288. $join = array();
  289. $select = array();
  290. foreach ($fields as $k => $v) {
  291. $distinct = false;
  292. if (is_int($k)) {
  293. $pos = stripos($v, ' AS ');
  294. if ($pos !== false) {
  295. $k = trim(substr($v, $pos+4));
  296. $v = trim(substr($v, 0, $pos));
  297. }
  298. }
  299. $pos = stripos($v, 'DISTINCT ');
  300. if ($pos !== false) {
  301. $v = trim(substr($v, $pos+9));
  302. $distinct = true;
  303. }
  304. if (strpos($v, '.') === false) $v = $this->defaultTableAlias . "." . $v;
  305. list($table, $col) = explode('.', $v);
  306. if ($table !== $this->defaultTableAlias && !in_array($table, $join)) {
  307. $join[] = $table;
  308. }
  309. $column = ($distinct ? 'DISTINCT ' : '') . $this->identifier($table) . '.' . $this->identifier($col);
  310. if (!is_int($k)) $column .= ' AS ' . $this->identifier($k);
  311. $select[] = $column;
  312. }
  313. $joinMapper = $this->joinMapper();
  314. if (is_null($joinMapper)) {
  315. $q = $this->join($join, $q, $inner);
  316. } else {
  317. $q = $joinMapper->join($join, $q, $inner);
  318. }
  319. $q->select(implode(', ', $select));
  320. return $q;
  321. }
  322. function _prepareQuery($q, $query)
  323. {
  324. if (isset($query['fields']) && !empty($query['fields'])) {
  325. $this->_prepareFields($q, $query['fields'], false);
  326. }
  327. if (isset($query['ifields']) && !empty($query['ifields'])) {
  328. $this->_prepareFields($q, $query['ifields'], true);
  329. }
  330. }
  331. function _sql($query, $args = null, $boundParams = array(), $select = false)
  332. {
  333. if ($select) {
  334. $sql = $this->replaceTableName($this->queryWriter()->buildSelectQuery($query, $this->namedScopes()));
  335. } else {
  336. $sql = $this->replaceTableName($this->queryWriter()->build($query, $this->namedScopes()));
  337. }
  338. $args = $this->queryWriter()->params($args, $query, $boundParams);
  339. return $this->replaceRepeatedParams($sql, $args);
  340. }
  341. function _all($query, $args = null, $boundParams = array(), $strict = null)
  342. {
  343. $entityClass = isset($query['entityclass']) ? $query['entityclass'] : null ;
  344. $prototype = $this->bean(array(), $entityClass);
  345. return $this->_iter($query, $args, $boundParams, $strict)->map(array($prototype, '___'));
  346. }
  347. function _iter($query, $args = null, $boundParams = array(), $strict = null)
  348. {
  349. $strict = is_null($strict) ? $this->strict() : $strict ;
  350. $query = $this->setSelectColumn($query);
  351. $query = $this->setFromTable($query);
  352. list($sql, $args) = $this->_sql($query, $args, $boundParams, true);
  353. return $this->db()->iter($sql, $args, $strict);
  354. }
  355. function _row($query, $args = null, $boundParams = array(), $strict = null)
  356. {
  357. $strict = is_null($strict) ? $this->strict() : $strict ;
  358. $query = $this->setSelectColumn($query);
  359. $query = $this->setFromTable($query);
  360. list($sql, $args) = $this->_sql($query, $args, $boundParams, true);
  361. return $this->db()->row($sql, $args, $strict);
  362. }
  363. function _first($query, $args = null, $boundParams = array(), $strict = null)
  364. {
  365. $result = $this->_row($query, $args, $boundParams, $strict);
  366. if ($result) {
  367. $entityClass = isset($query['entityclass']) ? $query['entityclass'] : null ;
  368. return $this->bean($result, $entityClass);
  369. }
  370. return null;
  371. }
  372. function _count($query, $args = null, $boundParams = array(), $strict = null)
  373. {
  374. $strict = is_null($strict) ? $this->strict() : $strict ;
  375. $query = $this->setFromTable($query);
  376. if (isset($query['count'])) {
  377. if (is_array($query['count'])) {
  378. foreach ($query['count'] as $k => $v) {
  379. $query[$k] = $v;
  380. }
  381. } else {
  382. $query['select'] = $query['count'];
  383. }
  384. } else {
  385. $query['select'] = "count(*) AS count";
  386. }
  387. list($sql, $args) = $this->_sql($query, $args, $boundParams, true);
  388. $bean = $this->db()->first($sql, $args, $strict);
  389. return $bean ? (int) $bean->count : null ;
  390. }
  391. function _exec($query, $args = null, $returnRowCount = false, $boundParams = array(), $strict = null)
  392. {
  393. $strict = is_null($strict) ? $this->strict() : $strict ;
  394. list($sql, $args) = $this->_sql($query, $args, $boundParams, false);
  395. return $this->db()->exec($sql, $args, $returnRowCount, $strict);
  396. }
  397. function lastInsertId()
  398. {
  399. return (int) $this->db()->lastInsertId($this->primaryKey());
  400. }
  401. function identifier($name)
  402. {
  403. if ($name === '*') return $name;
  404. $q = $this->quote;
  405. return $q . str_replace($q, $q.$q, $name) . $q;
  406. }
  407. protected function makeColumnLabel($bean, $eq = false, $ignore = null)
  408. {
  409. $col = array();
  410. $param = array();
  411. $var = array();
  412. foreach ($bean as $k => $v) {
  413. if (is_null($ignore) || !in_array($k, $ignore)) {
  414. $id = $this->identifier($k);
  415. $col[] = $id . ($eq ? "=:{$k}" : '');
  416. $var[] = ':' . $k;
  417. }
  418. $param[':' . $k] = $v;
  419. }
  420. return array($col, $param, $var);
  421. }
  422. function insert($bean, $q = null)
  423. {
  424. if ($this->autoPopulate()) {
  425. $current = $this->currentDateTime();
  426. $bean->{$this->createdDateColumn()} = $current;
  427. $bean->{$this->modifiedDateColumn()} = $current;
  428. }
  429. $q = is_null($q) ? $this->query() : $q ;
  430. list($col, $param, $var) = $this->makeColumnLabel($bean, false, $q->ignoreKeys());
  431. $result =
  432. $q->insert()->into($this->tableName() . ' (' . implode(',', $col) .')')
  433. ->values('(' . implode(',', $var) .')')->exec($param);
  434. if ($result) {
  435. $bean->{$this->primaryKey()} = $this->lastInsertId();
  436. }
  437. return $result;
  438. }
  439. function update($bean, $q = null, $returnRowCount = false)
  440. {
  441. if ($this->autoPopulate()) {
  442. $current = $this->currentDateTime();
  443. $bean->{$this->modifiedDateColumn()} = $current;
  444. }
  445. $q = is_null($q) ? $this->query() : $q ;
  446. $set = array();
  447. $param = array();
  448. $pk = $this->primaryKey();
  449. $ignoreKeys = array($pk);
  450. $ignoreKeys = array_merge($ignoreKeys, $q->ignoreKeys());
  451. list($set, $param, $var) = $this->makeColumnLabel($bean, true, $ignoreKeys);
  452. $pkid = $this->identifier($pk);
  453. return
  454. $q->update($this->tableName())->set(implode(',', $set))
  455. ->where("{$pkid} = :{$pk}")->rowCount($returnRowCount)->exec($param);
  456. }
  457. function save($bean, $q = null)
  458. {
  459. $pk = $this->primaryKey();
  460. if ($bean->{$pk}) {
  461. return $this->update($bean, $q);
  462. }
  463. return $this->insert($bean, $q);
  464. }
  465. function delete($id, $q = null, $returnRowCount = false)
  466. {
  467. if (is_int($id) || is_numeric($id) || is_string($id)) {
  468. $pk = $this->primaryKey();
  469. $q = is_null($q) ? $this->query() : $q ;
  470. $pkid = $this->identifier($pk);
  471. return
  472. $q->delete()->from($this->tableName())->where("{$pkid} = :{$pk}")
  473. ->rowCount($returnRowCount)->exec(array(":{$pk}" => $id));
  474. }
  475. $q = is_null($q) ? $this->query() : $q ;
  476. list($set, $param, $var) = $this->makeColumnLabel($id, true, $q->ignoreKeys());
  477. return
  478. $q->delete()->from($this->tableName())->where($set)
  479. ->rowCount($returnRowCount)->exec($param);
  480. }
  481. function get($id = null, $q = null, $empty = false)
  482. {
  483. if (!$id) return $this->bean();
  484. if (is_int($id) || is_numeric($id) || is_string($id)) {
  485. $pk = $this->primaryKey();
  486. $q = is_null($q) ? $this->query() : $q ;
  487. $pkid = $this->identifier($pk);
  488. $bean =
  489. $q->select('*')->from($this->tableName())
  490. ->where("{$pkid} = :{$pk}")->first(array(":{$pk}" => $id));
  491. if (!$empty) return $bean;
  492. return $bean ? $bean : $this->bean() ;
  493. }
  494. $q = is_null($q) ? $this->query() : $q ;
  495. list($set, $param, $var) = $this->makeColumnLabel($id, true, $q->ignoreKeys());
  496. return $q->from($this->tableName())->where($set)->first($param);
  497. }
  498. function getRelationMapper($key)
  499. {
  500. if (!isset($this->relation[$key])) return null;
  501. $mapper = $this->relation[$key];
  502. if (!is_string($mapper)) return $mapper;
  503. $this->relation[$key] = Gongo_Locator::get($mapper, $this->db());
  504. return $this->relation[$key];
  505. }
  506. function getRelationMapperInstance($class)
  507. {
  508. return Gongo_Locator::get($class, $this->db());
  509. }
  510. function foreignKey($key)
  511. {
  512. return $key . '_id';
  513. }
  514. function pkId($relMapper)
  515. {
  516. return $this->identifier($relMapper->primaryKey());
  517. }
  518. function join($keys, $q = null, $inner = false)
  519. {
  520. return self::joinHandler($this, $keys, $q, $inner);
  521. }
  522. function beginTransaction()
  523. {
  524. return $this->db()->beginTransaction();
  525. }
  526. function commit()
  527. {
  528. return $this->db()->commit();
  529. }
  530. function rollBack()
  531. {
  532. return $this->db()->rollBack();
  533. }
  534. function pdo()
  535. {
  536. return $this->db()->pdo();
  537. }
  538. }