PageRenderTime 56ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Cygnite/Database/ActiveRecord.php

https://github.com/wahidhmca/framework
PHP | 1481 lines | 1087 code | 192 blank | 202 comment | 107 complexity | 75d52ec7872ef2ebac813491bfb36367 MD5 | raw file
  1. <?php
  2. namespace Cygnite\Database;
  3. use PDO;
  4. use Cygnite;
  5. use Exception;
  6. use PDOException;
  7. use Cygnite\Helpers\Inflector;
  8. use ReflectionClass;
  9. use ReflectionObject;
  10. use ReflectionProperty;
  11. use Cygnite\Base\Event;
  12. use Cygnite\Database\Schema;
  13. use Cygnite\Database\Connections;
  14. use Cygnite\Common\Pagination;
  15. use Cygnite\Database\Configurations;
  16. use Cygnite\Database\Exceptions\DatabaseException;
  17. /**
  18. * Cygnite Framework
  19. *
  20. * An open source application development framework for PHP 5.3x or newer
  21. *
  22. * License
  23. *
  24. * This source file is subject to the MIT license that is bundled
  25. * with this package in the file LICENSE.txt.
  26. * http://www.cygniteframework.com/license.txt
  27. * If you did not receive a copy of the license and are unable to
  28. * obtain it through the world-wide-web, please send an email
  29. * to sanjoy@hotmail.com so I can send you a copy immediately.
  30. *
  31. * @Package : Packages
  32. * @Sub Packages : Database
  33. * @Filename : ActiveRecord
  34. * @Description : Active Record to handle database manipulations.
  35. * As read,write,erase,update etc.
  36. * @Author : Sanjoy Dey
  37. * @Copyright : Copyright (c) 2013 - 2014,
  38. * @Link : http://www.cygniteframework.com
  39. * @Since : Version 1.0
  40. * @FileSource
  41. *
  42. */
  43. class ActiveRecord extends Connections
  44. {
  45. public $id;
  46. //Hold your connection object
  47. public $pdo;
  48. public $modelClass;
  49. //set closed property as true is set else false
  50. public $closed;
  51. //Hold all your table fields in attributes
  52. protected $attributes = array();
  53. public $data = array();
  54. //set your pdo statement here
  55. private $_statement;
  56. //hold all your fields name which to select from table
  57. private $_selectColumns;
  58. private $_fromWhere;
  59. private $_columnWhere;
  60. private $_whereType;
  61. private $_limitValue;
  62. private $_offsetValue;
  63. private $_columnName;
  64. private $_orderType;
  65. private $_groupBy;
  66. //set user defined database name into it.
  67. protected $database;
  68. //set user defined table name into it.
  69. protected $tableName;
  70. //set user defined table primary key
  71. protected $primaryKey;
  72. private $index;
  73. // set query builder query into property
  74. private $sqlQuery;
  75. private $debugQuery;
  76. private $distinct;
  77. protected $events = array(
  78. 'beforeCreate',
  79. 'afterCreate',
  80. 'beforeUpdate',
  81. 'afterUpdate',
  82. 'beforeSelect',
  83. 'afterSelect',
  84. 'beforeDelete',
  85. 'afterDelete'
  86. );
  87. const DELETE = 'DELETE';
  88. public $paginationUri;
  89. public $paginator = array();
  90. public $paginationOffset;
  91. public $pageNumber;
  92. private $validFinders = array(
  93. 'first',
  94. 'last',
  95. 'find',
  96. 'findBy',
  97. 'all',
  98. 'findBySql',
  99. 'findByAnd',
  100. 'findByOr',
  101. 'save'
  102. );
  103. /*
  104. * Restrict users to create active records object Directly
  105. * Get the database configurations
  106. *
  107. */
  108. protected function __construct()
  109. {
  110. $model = null;
  111. $model = get_class($this);
  112. if (!empty($this->events)) {
  113. foreach ($this->events as $eventKey => $event) {
  114. Event::instance()->attach($event, '\\'.$model.'@'.$event);
  115. }
  116. }
  117. $this->modelClass = Inflector::instance()->getClassNameFromNamespace($model);
  118. if (!property_exists($model, 'tableName') || is_null($this->tableName)) {
  119. $this->tableName = Inflector::instance()->fromCamelCase($this->modelClass);
  120. }
  121. if (!property_exists($model, 'database')) {
  122. $this->database = $this->getDefaultConnection();
  123. }
  124. if (is_null($this->database)) {
  125. throw new \InvalidArgumentException(
  126. "Please specify database name in your model.".get_called_class()
  127. );
  128. }
  129. //$this->pdo = Connections::getConnection($this->database);
  130. $this->setDatabaseConnection($this->getConnection($this->database));
  131. }
  132. public function setAttributes($attributes = array())
  133. {
  134. foreach ($attributes as $key => $value) {
  135. $this->__set($key, $value);
  136. }
  137. }
  138. /*
  139. * Set your table columns dynamically
  140. * @access public
  141. * @param $key hold your table columns
  142. * @param $value hold your table column values
  143. * @return void
  144. *
  145. */
  146. public function __set($key, $value)
  147. {
  148. $this->attributes[$key] = $value;
  149. }
  150. /*
  151. * Get your table columns dynamically
  152. * @access public
  153. * @param $key
  154. * @return void
  155. *
  156. */
  157. public function __get($key)
  158. {
  159. try {
  160. return isset($this->attributes[$key]) ? $this->attributes[$key] : null;
  161. } catch (\Exception $ex) {
  162. throw new \Exception($ex->getMessage());
  163. }
  164. }
  165. /**
  166. * The finder make use of __callStatic() to invoke
  167. * undefinded static methods dynamically. This magic method is mainly used
  168. * for dynamic finders
  169. *
  170. * @param $method String
  171. * @param $arguments array
  172. * @return object
  173. *
  174. */
  175. public static function __callStatic($method, $arguments)
  176. {
  177. $class = $params = null;
  178. $class = self::getDynamicInstance();
  179. if (substr($method, 0, 6) == 'findBy') {
  180. if ($method == 'findBySql') {
  181. return static::callDynamicMethod(array($class, $method), $arguments);
  182. }
  183. $params = array();
  184. if (strpos($method,'And') !== false) {
  185. $params = static::buildFindersWhereConditions($method, $arguments);
  186. return static::callDynamicMethod(array($class, 'findByAnd'), array($params));
  187. }
  188. if (strpos($method,'Or') !== false) {
  189. $params = static::buildFindersWhereConditions($method, $arguments, 'Or');
  190. return static::callDynamicMethod(array($class, 'findByOr'), array($params));
  191. }
  192. $columnName = Inflector::instance()->fromCamelCase(substr($method, 6));
  193. $params = array();
  194. $condition = (isset($arguments[1])) ? $arguments[1] : '=';
  195. $params = array(
  196. $columnName,
  197. $condition,
  198. $arguments[0],
  199. );
  200. return call_user_func_array(array($class, substr($method, 0, 6)), $params);
  201. }
  202. if ($method == 'first') {
  203. return static::callDynamicMethod(array($class, $method), $arguments);
  204. }
  205. if ($method == 'find') {
  206. return static::callDynamicMethod(array($class, $method), $arguments);
  207. }
  208. if ($method == 'all') {
  209. return static::callDynamicMethod(array($class, $method), $arguments);
  210. }
  211. if ($method == 'last') {
  212. return static::callDynamicMethod(array($class, $method), $arguments);
  213. }
  214. if ($method == 'createLinks') {
  215. $model = get_called_class();
  216. $pagination = null;
  217. $pagination = Pagination::instance(new $model());
  218. return $pagination->{$method}();
  219. }
  220. //Use the power of PDO methods directly via static functions
  221. return call_user_func_array(array(new $class, $method), $arguments);
  222. }
  223. /**
  224. * This method is mainly used for building where conditions as array
  225. * for dynamic finders.
  226. *
  227. *
  228. * @param $method String
  229. * @param $arguments array
  230. * @param $type string
  231. * @throws \Exception
  232. * @return object
  233. *
  234. */
  235. public static function buildFindersWhereConditions($method, $arguments, $type = 'And')
  236. {
  237. $conditions = array();
  238. $conditions = explode($type, str_replace('findBy', '', $method));
  239. if (count($conditions) == count($arguments[0])) {
  240. foreach ($conditions as $key => $value) {
  241. $field = Inflector::instance()->fromCamelCase($value);
  242. $params[$field.' ='] = isset($arguments[0][$key]) ?
  243. trim($arguments[0][$key]) :
  244. '';
  245. }
  246. } else {
  247. throw new Exception("Arguments doesn't matched with number of fields");
  248. }
  249. return $params;
  250. }
  251. public static function callDynamicMethod($callback, $arguments = array())
  252. {
  253. return call_user_func_array($callback, $arguments);
  254. }
  255. private static function getDynamicInstance()
  256. {
  257. $class = $child = $reflector = null;
  258. $class = get_called_class();
  259. if (class_exists($class)) {
  260. $reflector = new ReflectionClass($class);
  261. $child = $reflector->getProperty('primaryKey');
  262. return new $child->class;
  263. }
  264. }
  265. public function buildDynamicQuery()
  266. {
  267. }
  268. public function __isset($key)
  269. {
  270. return isset($this->attributes[$key]);
  271. }
  272. /**
  273. * Call framework defined method based on user input
  274. * $name method name
  275. * $arguments pass arguments to method dynamically
  276. * return mixed
  277. *
  278. */
  279. public function __call($method, $arguments = array())
  280. {
  281. if (in_array($method, $this->validFinders) && $method == 'save') {
  282. if (empty($arguments) && $this->isNew() == true) {
  283. if (method_exists($this, 'insertInto')) {
  284. return call_user_func_array(array($this, 'insertInto'), $arguments);
  285. }
  286. } else {
  287. if (method_exists($this, 'update')) {
  288. if (empty($arguments)) {
  289. $arguments[] = $this->index[$this->primaryKey];
  290. }
  291. return call_user_func_array(array($this,'update'), $arguments);
  292. }
  293. }
  294. }
  295. if (in_array($method, $this->validFinders) && $method == 'first') {
  296. $fetchObject = $this->select('*')
  297. ->orderBy($this->primaryKey)
  298. ->limit(1)
  299. ->findAll();
  300. if ($fetchObject == null) {
  301. return $this->returnEmptyObject();
  302. }
  303. return $fetchObject;
  304. }
  305. if (in_array($method, $this->validFinders) && $method == 'findByAnd') {
  306. return $this->select('*')->where($arguments[0])->findAll();
  307. }
  308. if (in_array($method, $this->validFinders) && $method == 'findByOr') {
  309. return $this->select('*')->where($arguments[0], '', 'OR')->findAll();
  310. }
  311. if (in_array($method, $this->validFinders) && $method == 'all') {
  312. if (isset($arguments[0]['orderBy'])) {
  313. $exp = array();
  314. $exp = explode(' ', $arguments[0]['orderBy']);
  315. $this->_columnName = (isset($exp[0])) ? $exp[0] : '';
  316. $this->_orderType = (isset($exp[1])) ? $exp[1] : '';
  317. } else {
  318. $this->_columnName = 'id';
  319. }
  320. if (isset($arguments[0]['paginate']) || method_exists($this, 'pageLimit')) {
  321. $page = $offset = $start = "";
  322. $offset = $this->perPage; //how many items to show per page
  323. $limit = !isset($arguments[0]['paginate']['limit']) ?
  324. $this->pageLimit() :
  325. $arguments[0]['paginate']['limit'];
  326. $page = ($limit !== '')
  327. ? $limit
  328. : 0;
  329. if ($page) {
  330. $start = ($page - 1) * $offset;//first item to display on this page
  331. } else {
  332. $start = 0; //if no page var is given, set start to 0
  333. }
  334. $this->_limitValue = intval($start);
  335. $this->_offsetValue = intval($offset);
  336. }
  337. return $this->select('*')->findAll();
  338. }
  339. if (in_array($method, $this->validFinders) && $method == 'find') {
  340. $id = array_shift($arguments);
  341. $fetch = $this->select('*')->where($this->primaryKey, $id)
  342. ->orderBy($this->primaryKey,'DESC')
  343. ->findAll();
  344. $this->setId($this->primaryKey, $id);
  345. if ($fetch == null) {
  346. return $this->returnEmptyObject();
  347. }
  348. $this->{$this->primaryKey} = $fetch[0]->{$this->primaryKey};
  349. foreach ($fetch[0]->attributes as $key => $value) {
  350. $this->{$key} = $value;
  351. }
  352. $this->assignPropertiesToModel($this->attributes);
  353. return $this;
  354. }
  355. if (in_array($method, $this->validFinders) && $method == 'findBy') {
  356. $columnName = "";
  357. $params = array();
  358. $params = array(
  359. $arguments[0].' '.$arguments[1] => $arguments[2]
  360. );
  361. $fetch = $this->select('*')->where($params)->findAll();
  362. if ($fetch == null) {
  363. return $this->returnEmptyObject();
  364. }
  365. return $fetch;
  366. }
  367. if (in_array($method, $this->validFinders) && $method == 'last') {
  368. $fetchObject = $this->select('*')
  369. ->orderBy($this->primaryKey, 'DESC')
  370. ->limit(1)
  371. ->findAll();
  372. if ($fetchObject == null) {
  373. return $this->returnEmptyObject();
  374. }
  375. return $fetchObject;
  376. }
  377. /** @var $method TYPE_NAME */
  378. if (in_array($method, $this->validFinders) && $method == 'findBySql') {
  379. $results = array();
  380. $fetchObject = $this->getDatabaseConnection()->prepare(trim($arguments[0]));
  381. $fetchObject->execute();
  382. $results = $this->fetchAs($fetchObject);
  383. return $results;
  384. }
  385. if (!method_exists($this->getDatabaseConnection(), $method)) {
  386. throw new Exception("$method method not exists ");
  387. }
  388. return call_user_func_array(array($this->getDatabaseConnection(), $method), $arguments);
  389. //throw new \Exception("Invalid method $name called ");
  390. }
  391. private function returnEmptyObject()
  392. {
  393. $class = self::getDynamicInstance();
  394. $this->index[$this->primaryKey] = null;
  395. return new $class;
  396. }
  397. public function lastQuery()
  398. {
  399. return $this->debugQuery;
  400. }
  401. public function setPageNumber($number)
  402. {
  403. $this->pageNumber = intval($number);
  404. }
  405. public function getPageNumber()
  406. {
  407. return (isset($this->pageNumber)) ? $this->pageNumber : null;
  408. $this->pageNumber = intval($number);
  409. }
  410. public function getPaginationOffset()
  411. {
  412. return (isset($this->paginationOffset)) ? $this->paginationOffset : null;
  413. }
  414. public function setPaginationOffset($offset)
  415. {
  416. $this->paginationOffset = intval($offset);
  417. }
  418. private function extractConditionsReverse($arr)
  419. {
  420. //$pattern = '/([A-Za-z_]+[A-Za-z_0-9]?)[ ]?(!=|=|<=|<|>=|>|like|clike|slike|not
  421. // |is|in|between|and|or|IN|NOT|BETWEEN|LIKE|AND|OR)/';
  422. $pattern = '/([\w]+)?[\s]?([\!\<\>]?\=|[\<\>]|[cs]{0,1}like|not
  423. |i[sn]|between|and|or)?/i';
  424. $result = array();
  425. foreach ($arr as $key => $value) {
  426. preg_match($pattern, $key, $matches);
  427. $matches[1] = !empty($matches[1]) ? $matches[1] : null;
  428. $matches[2] = !empty($matches[2]) ? $matches[2] : null;
  429. $result []= array($matches[1], $matches[2], $value);
  430. }
  431. return $result;
  432. }
  433. private function setId($key, $value)
  434. {
  435. $this->index[$key] = $value;
  436. }
  437. public function getId($key)
  438. {
  439. return ($this->index[$key] !== null) ? $this->index[$key] : null;
  440. }
  441. protected function assignPropertiesToModel($attributes = array())
  442. {
  443. $model = null;
  444. $model = self::getDynamicInstance();
  445. foreach ($attributes as $key => $value) {
  446. $model->{$key} = $value;
  447. }
  448. }
  449. public function isNew()
  450. {
  451. return ($this->index[$this->primaryKey] == null) ? true : false;
  452. }
  453. private function findByColumn($key, $values = array())
  454. {
  455. //echo $key;
  456. }
  457. /*
  458. * Save data into table
  459. * @access private
  460. * @param $arguments empty array
  461. * @return true
  462. *
  463. */
  464. private function insertInto($arguments = array())
  465. {
  466. $fields = $values = array();
  467. $query = $debugQuery = "";
  468. if (method_exists($this, 'beforeCreate')) {
  469. Event::instance()->trigger('beforeCreate', $this);
  470. }
  471. $insertMethod = null;
  472. $insertMethod = str_replace('_', ' ',
  473. strtoupper(Inflector::instance()->fromCamelCase(__FUNCTION__))
  474. );
  475. foreach (array_keys($this->attributes) as $key) {
  476. $fields[] = "`$key`";
  477. $values[] = "'" .$this->attributes[$key] . "'";
  478. $placeholder[] = substr(str_repeat('?,', count($key)), 0, -1);
  479. }
  480. $fields = implode(',', $fields);
  481. $values = implode(',', $values);
  482. $placeHolders = implode(',', $placeholder);
  483. $query = $insertMethod." `".$this->database."`.`".$this->tableName."`
  484. ($fields) VALUES"." ($placeHolders)".";";
  485. $debugQuery = $insertMethod." `".$this->database."`.`".$this->tableName."`
  486. ($fields) VALUES"." ($values)".";";
  487. try {
  488. //$this->getDatabaseConnection()->quote($string, $parameter_type=null); have to write a method to escape strings
  489. $statement = $this->getDatabaseConnection()->prepare($query);
  490. if ( true == $statement->execute(array_values($this->attributes))) {
  491. $this->{$this->primaryKey} = (int) $this->getDatabaseConnection()->lastInsertId();
  492. if (method_exists($this, 'afterCreate')) {
  493. Event::instance()->trigger('afterCreate', $this);
  494. }
  495. return true;
  496. }
  497. } catch (PDOException $exception) {
  498. throw new \RuntimeException($exception->getMessage()); //echo $exception->getMessage();
  499. }
  500. }
  501. /*
  502. * Update user data into table by key
  503. * @access private
  504. * @param $args update by table fields
  505. * @return boolean
  506. *
  507. */
  508. private function update($args)
  509. {
  510. $query =$debugQuery= $x = "";
  511. $updateBy = $updateValue = null;
  512. $updateMethod = null;
  513. $updateMethod = strtoupper(Inflector::instance()->fromCamelCase(__FUNCTION__));
  514. if (method_exists($this, 'beforeUpdate')) {
  515. Event::instance()->trigger('beforeUpdate', $this);
  516. }
  517. if ((is_array($args) && !empty($args) )) {
  518. $x = array_keys($args);
  519. $updateBy = $x[0];
  520. $updateValue = $args[$x[0]];
  521. } else {
  522. $updateBy = $this->primaryKey;
  523. $updateValue = $args;
  524. }
  525. $query .= $updateMethod." `".$this->database."`.`".$this->tableName."` SET ";
  526. $debugQuery .= $updateMethod." `".$this->database."`.`".$this->tableName."` SET ";
  527. $arrCount = count($this->attributes);
  528. $i = 0;
  529. foreach ($this->attributes as $key => $value) {
  530. $query .= " `".$key."` "."="." '".$value."'"." ";
  531. $debugQuery .= " `".$key."` "."="." '".$value."'"." ";
  532. $query .= ($i < $arrCount-1) ? ',' : '';
  533. $debugQuery .= ($i < $arrCount-1) ? ',' : '';
  534. $i++;
  535. }
  536. $query .=" WHERE ".$updateBy." = :column";
  537. $debugQuery .=" WHERE ".$updateBy." = ".$updateValue;
  538. //$this->debugLastQuery($debugQuery);
  539. try {
  540. $statement = $this->getDatabaseConnection()->prepare($query);
  541. $statement->bindValue(':column', $updateValue);
  542. $statement->execute();
  543. if (method_exists($this, 'afterUpdate')) {
  544. Event::instance()->trigger('afterUpdate', $this);
  545. }
  546. return $statement->rowCount();
  547. } catch (\PDOException $exception) {
  548. throw new Exception($exception->getMessage());
  549. }
  550. }
  551. /**
  552. * Trash method
  553. *
  554. * Delete rows from the table and runs the query
  555. *
  556. * @access public
  557. * @param array $where
  558. * $multiple false
  559. * @param bool $multiple
  560. * @throws \Exception
  561. * @internal param \Cygnite\Database\the $string table to retrieve the results from
  562. * @return object
  563. */
  564. public function trash($where, $multiple = false)
  565. {
  566. $whr = array();
  567. $column = $value = $statement = null;
  568. if (method_exists($this, 'beforeDelete')) {
  569. Event::instance()->trigger('beforeDelete', $this);
  570. }
  571. if (is_array($where) && $multiple == false) {
  572. $whr = array_keys($where);
  573. $column = $whr[0];
  574. $value = $where[$whr[0]];
  575. $condition = "` WHERE `".$column."` = ".$value;
  576. }
  577. if (is_string($where) || is_int($where)) {
  578. $column = $this->primaryKey;
  579. $value = $where;
  580. $condition = "` WHERE `".$column."` = ".$value;
  581. }
  582. $values = array();
  583. if (is_array($where) && $multiple == true) {
  584. $condition = "` WHERE `id` IN (".implode(',', $where).")";
  585. $output = array_map(
  586. function ($val) {
  587. return '?';
  588. },
  589. $where
  590. );
  591. $debugQuery = "` WHERE `id` IN (".implode(',', $output).")";
  592. $sqlQuery = self::DELETE." FROM `".$this->tableName.$condition;
  593. $debugQuery = self::DELETE." FROM `".$this->tableName.$debugQuery;
  594. } else {
  595. $sqlQuery =
  596. self::DELETE." FROM `".$this->tableName."` WHERE `".$column."` = :where";
  597. $debugQuery =
  598. self::DELETE." FROM `".$this->tableName."` WHERE `".$column."` = ".$value;
  599. }
  600. /** @var $exception TYPE_NAME */
  601. try {
  602. $statement = $this->getDatabaseConnection()->prepare($sqlQuery);
  603. if (is_array($values) && empty($values)) {
  604. $statement->bindValue(':where', $value);
  605. }
  606. $affectedRows = null;
  607. $affectedRows = $statement->execute();
  608. if (method_exists($this, 'afterDelete')) {
  609. Event::instance()->trigger('afterDelete', $this);
  610. }
  611. return $affectedRows;
  612. } catch (\PDOException $exception) {
  613. throw new \Exception($exception->getMessage());
  614. }
  615. }
  616. /**
  617. * Find Function to selecting Table columns
  618. *
  619. * Generates the SELECT portion of the query
  620. *
  621. * @access public
  622. * @param $type
  623. * @throws \Exception
  624. * @internal param $string
  625. * @return object
  626. */
  627. public function select($type)
  628. {
  629. //create where condition with and if value is passed as array
  630. if (is_string($type) && !is_null($type)) {
  631. if ($type === 'all' || $type == '*') {
  632. $this->_selectColumns = $this->tableName.'.*';
  633. } else {
  634. $this->_selectColumns = (string) $type; // Need to split the column name and add quotes
  635. }
  636. } else {
  637. throw new Exception("Accepted parameters should be string format.");
  638. }
  639. return $this;
  640. }
  641. /* Where
  642. *
  643. * Generates the WHERE portion of the query. Separates
  644. * multiple calls with AND
  645. * You can also use this method for WHERE IN(),
  646. * OR WHERE etc.
  647. * Example:
  648. * <code>
  649. * $this->where('field_name','value','=');
  650. *
  651. * $conditions = array(
  652. * 'field_name1 LIKE' => '%Sanjoy%',
  653. * ); 'field_name2 LIKE' => 'Dey%',
  654. *
  655. * $this->where($conditions);
  656. *
  657. * $conditions2 = array(
  658. * 'field_name1 LIKE' => '%Sanjoy%',
  659. * ); 'field_name2 =' => 'Dey',
  660. *
  661. * $this->where($conditions2,'OR');
  662. *
  663. *
  664. * $conditions3 = array(
  665. * 'field_name1 IN' => '#"Automatic","Automated","Autoclaves"'
  666. * );
  667. *
  668. * $this->where($conditions2,'OR');
  669. *
  670. * $conditions4 = array(
  671. * 'created_at BETWEEN' => '2012-12-27',
  672. date('Y-m-d'),
  673. * );
  674. *
  675. * $this->where($conditions4);
  676. * </code>
  677. *
  678. * @access public
  679. * @param column name
  680. * @param value
  681. * @return object
  682. */
  683. public function where($columnName, $where = "", $type = null)
  684. {
  685. $resultArray = array();
  686. // Check whether value passed as array or not
  687. if (is_array($columnName)) {
  688. $arrayCount = count($columnName);
  689. $resultArray = $this->extractConditions($columnName);
  690. $arrayCount = count($resultArray);
  691. $i = 0;
  692. $whereValue = $whereCondition= "";
  693. foreach ($resultArray as $row) {
  694. $whereField = $this->tableName.".`".$row['0']."`";
  695. if ($row['0'] === null) {
  696. $whereField = '';
  697. }
  698. $whereCondition = (is_string($row['1'])) ? strtoupper($row['1']) : $row['1'] ;
  699. if (preg_match('/#/', $row['2'])) {
  700. $whereValue = str_replace('#', '(', $row['2']).')';
  701. } else {
  702. $whereValue = (is_string($row['2'])) ? " '".$row['2']."'" : $row['2'] ;
  703. }
  704. $whereType = '';
  705. $this->_fromWhere .= $whereField." ".$whereCondition.$whereValue;
  706. $whereType = ($where == '' && $type !== 'OR') ? ' AND ' : ' '.$type.' ';
  707. $this->_fromWhere .= ($i < $arrayCount-1) ? $whereType : '';
  708. $this->_whereType = '';
  709. $i++;
  710. }
  711. return $this;
  712. }
  713. if (is_string($columnName)) {
  714. $columnName = "`".$columnName."`";
  715. }
  716. $this->_whereType = '=';
  717. $this->_columnWhere = $this->tableName.'.'.$columnName;
  718. $this->_fromWhere = " '".$where."' ";
  719. if (!is_null($type)) {
  720. $this->_whereType = $type;
  721. }
  722. return $this;
  723. }
  724. /*
  725. * Extract user conditions from array
  726. * @access private
  727. * @param $arr array to extract conditions
  728. * @return array
  729. */
  730. private function extractConditions($arr)
  731. {
  732. //$pattern = '/([A-Za-z_]+[A-Za-z_0-9]?)[ ]?(!=|=|<=|<|>=|>|like|clike|slike|not
  733. // |is|in|between|and|or|IN|NOT|BETWEEN|LIKE|AND|OR)/';
  734. $pattern = '/([\w]+)?[\s]?([\!\<\>]?\=|[\<\>]|[cs]{0,1}like|not
  735. |i[sn]|between|and|or)?/i';
  736. $result = array();
  737. foreach ($arr as $key => $value) {
  738. preg_match($pattern, $key, $matches);
  739. $matches[1] = !empty($matches[1]) ? $matches[1] : null;
  740. $matches[2] = !empty($matches[2]) ? $matches[2] : null;
  741. $result []= array($matches[1], $matches[2], $value);
  742. }
  743. return $result;
  744. }
  745. /*
  746. * Get the distinct value of the column
  747. * @access public
  748. * @param $column
  749. * @return $this
  750. *
  751. */
  752. public function distinct($column)
  753. {
  754. $this->distinct = (string) (strtolower(__FUNCTION__).($column));
  755. return $this;
  756. }
  757. /*
  758. * limit function to limit the database query
  759. * @access public
  760. * @param int
  761. * @return object
  762. */
  763. public function limit($limit, $offset = "")
  764. {
  765. if ($limit == ' ' || is_null($limit)) {
  766. throw new \Exception('Empty parameter given to limit clause ');
  767. }
  768. if (empty($offset) && !empty($limit)) {
  769. $this->_limitValue = 0;
  770. $this->_offsetValue = intval($limit);
  771. } else {
  772. $this->_limitValue = intval($limit);
  773. $this->_offsetValue = intval($offset);
  774. }
  775. return $this;
  776. }
  777. /*
  778. * Group By function to group columns based on aggregate functions
  779. * @access public
  780. * @param string
  781. * @return object
  782. */
  783. public function groupBy($column)
  784. {
  785. if (is_null($column)) {
  786. throw new \InvalidArgumentException("Cannot pass null argument to ".__METHOD__);
  787. }
  788. $groupBy = "";
  789. switch ($column) {
  790. case is_array($column):
  791. $i = 0;
  792. $count = count($column);
  793. while ($i < $count) { //Create group by in condition with and if value is passed as array
  794. $groupBy .= '`'.$column[$i].'`';
  795. $groupBy .= ($i < $count-1) ? ',' : '';
  796. $i ++;
  797. }
  798. $this->_groupBy = 'GROUP BY '.$groupBy;
  799. return $this;
  800. break;
  801. default:
  802. $this->_groupBy = 'GROUP BY `'.$column.'` ';//exit;
  803. return $this;
  804. break;
  805. }
  806. }
  807. public function quoteStrings($string)
  808. {
  809. //escape strings
  810. }
  811. /*
  812. * orderBy function to make order for selected query
  813. * @access public
  814. * @param string
  815. * @param string
  816. * @return object
  817. */
  818. public function orderBy($filedName, $orderType = "ASC")
  819. {
  820. if (empty($filedName)) {
  821. throw new \Exception('Empty parameter given to order by clause');
  822. }
  823. if ($this->_columnName === null && $this->_orderType === null) {
  824. $this->_columnName = $filedName;
  825. }
  826. $this->_orderType = $orderType;
  827. return $this;
  828. }
  829. /*
  830. * Convert array results to json encoded format
  831. * @access public
  832. * @return object
  833. */
  834. public function toJson()
  835. {
  836. $this->serialize = 'json';
  837. return $this;
  838. }
  839. /*
  840. * Convert array results to simple xml format
  841. * @access public
  842. * @return object
  843. */
  844. public function toXML()
  845. {
  846. $this->serialize = 'xml';
  847. return $this;
  848. }
  849. /**
  850. * Build and Find all the matching records from database.
  851. * By default its returns class with properties values
  852. * You can simply pass fetchMode into findAll to get various
  853. * format output.
  854. *
  855. * @access public
  856. * @param string $fetchMode
  857. * @throws \Exception
  858. * @internal param string $fetchMode
  859. * @return array or object
  860. */
  861. public function findAll($fetchMode = "")
  862. {
  863. $data = array();
  864. $limit = "";
  865. if (is_null($this->_selectColumns)) {
  866. $this->_selectColumns = '*';
  867. }
  868. $groupBy =(isset($this->_groupBy) && !is_null($this->_groupBy)) ?
  869. $this->_groupBy :
  870. '';
  871. $limit = (isset($this->_limitValue) && isset($this->_offsetValue)) ?
  872. " LIMIT ".$this->_limitValue.",".$this->_offsetValue." " : '';
  873. $orderBy= (isset($this->_columnName) && isset($this->_orderType)) ?
  874. " ORDER BY `".$this->_columnName."` ".$this->_orderType : '';
  875. $this->buildQuery($groupBy, $orderBy, $limit);
  876. try {
  877. $statement = $this->getDatabaseConnection()->prepare($this->sqlQuery);
  878. $this->sqlQuery = null;
  879. $this->setDbStatement($this->database, $statement);
  880. $statement->bindValue(':where', $this->_fromWhere);
  881. $statement->execute();
  882. $data = $this->fetchAs($statement, $fetchMode);
  883. if ($statement->rowCount() > 0) {
  884. return new \ArrayObject($data);
  885. }
  886. } catch (PDOException $ex) {
  887. throw new \Exception("Database exceptions: Invalid query x".$ex->getMessage());
  888. }
  889. }
  890. /*
  891. * fetch data as user defined format
  892. *
  893. * @access private
  894. * @param object $statement
  895. * @param string $fetchMode null
  896. * @return mixed.
  897. */
  898. public function fetchAs($statement, $fetchMode = null)
  899. {
  900. $data = array();
  901. switch ($fetchMode) {
  902. case 'GROUP':
  903. $data = $statement->fetchAll(\PDO::FETCH_GROUP| \PDO::FETCH_ASSOC);
  904. break;
  905. case 'BOTH':
  906. $data = $statement->fetchAll(\PDO::FETCH_BOTH);
  907. break;
  908. case 'JSON':
  909. $data = json_encode($statement->fetchAll(\PDO::FETCH_ASSOC));
  910. break;
  911. case 'OBJ':
  912. $data = $statement->fetchAll(\PDO::FETCH_OBJ);
  913. break;
  914. case 'ASSOC':
  915. $data = $statement->fetchAll(\PDO::FETCH_ASSOC);
  916. break;
  917. case 'COLUMN':
  918. $data = $statement->fetchAll(\PDO::FETCH_COLUMN);
  919. break;
  920. case 'CLASS':
  921. $data = $statement->fetchAll(\PDO::FETCH_CLASS, '\\'.__NAMESPACE__.'\\DataSource');
  922. break;
  923. default:
  924. $data = $statement->fetchAll(\PDO::FETCH_CLASS, get_called_class());
  925. }
  926. return $data;
  927. }
  928. /*
  929. * Get number of rows returned by query
  930. *
  931. * @access public
  932. * @return int.
  933. */
  934. public function rowCount()
  935. {
  936. $statement = $this->getDbStatement($this->database);
  937. return $statement->rowCount();
  938. }
  939. /*
  940. * Build your query internally here
  941. *
  942. * @access private
  943. * @param $groupBy column name
  944. * @param $orderBy sort column
  945. * @param $limit limit your query results
  946. * @return void
  947. */
  948. private function buildQuery($groupBy, $orderBy, $limit)
  949. {
  950. $searchKey = strpos($this->_fromWhere, 'AND');
  951. if (method_exists($this, 'exceptColumns')) {
  952. $ar = $this;
  953. $select = Schema::instance(
  954. $this,
  955. function ($table) use ($ar) {
  956. $table->database = $ar->database;
  957. $table->tableName = $ar->tableName;
  958. return $table->getColumns();
  959. }
  960. );
  961. $columns = $this->query($select->schema)->getAll();
  962. // Get all column name which need to remove from the result set
  963. $exceptColumns = $this->exceptColumns();
  964. foreach ($columns as $key => $value) {
  965. if (!in_array($value->column_name, $exceptColumns)) {
  966. $columnArray[] = $value->column_name;
  967. }
  968. }
  969. $this->_selectColumns = (string) implode (',', $columnArray);
  970. }
  971. if ($searchKey === false) {
  972. ($this->_columnWhere)
  973. ?
  974. $where = ' WHERE '.$this->_columnWhere.' = :where '
  975. : $where = ' ';
  976. $where = (is_null($this->_columnWhere) && is_null($this->_fromWhere))
  977. ? ''
  978. : ' WHERE '.$this->_columnWhere." $this->_whereType ".$this->_fromWhere."";
  979. $this->debugQuery =
  980. "SELECT ".$this->_selectColumns." FROM `".$this->tableName.'`'.$where.
  981. ' '.$groupBy.' '.$orderBy.' '.$limit;
  982. $this->sqlQuery =
  983. "SELECT ".$this->_selectColumns." FROM `".$this->tableName.'` '.$where.
  984. ' '.$groupBy.' '.$orderBy.' '.$limit;
  985. } else {
  986. $where = ($this->_fromWhere !="") ?
  987. " WHERE ".$this->_fromWhere :
  988. '';
  989. $this->debugQuery =
  990. "SELECT ".$this->_selectColumns." FROM `".$this->tableName.'` '.$where.' '.
  991. $groupBy.' '.$orderBy.' '.$limit;
  992. $this->sqlQuery =
  993. "SELECT ".$this->_selectColumns." FROM `".$this->tableName.'` '.$where.' '.
  994. $groupBy.' '.$orderBy.' '.$limit;
  995. }
  996. }
  997. ########
  998. /**
  999. * Build raw queries
  1000. *
  1001. * @access public
  1002. * @param string $sql
  1003. * @param array $attributes
  1004. * @throws \Exception|\PDOException
  1005. * @return object pointer $this
  1006. */
  1007. public function query($sql, $attributes = array())
  1008. {
  1009. try {
  1010. $this->_statement = $this->getDatabaseConnection()->prepare($sql);
  1011. if (!empty($attributes)) {
  1012. $this->_statement->execute($attributes);
  1013. } else {
  1014. $this->_statement->execute();
  1015. }
  1016. } catch (\PDOException $e) {
  1017. throw new Exception($e->getMessage());
  1018. }
  1019. return $this;
  1020. }
  1021. /*
  1022. * Execute user raw queries
  1023. *
  1024. * @access public
  1025. * @return array results
  1026. */
  1027. public function execute()
  1028. {
  1029. return $this->_statement->execute();
  1030. }
  1031. /*
  1032. * Find single row
  1033. *
  1034. * @access public
  1035. * @return array results
  1036. */
  1037. public function fetch()
  1038. {
  1039. return $this->_statement->fetch();
  1040. }
  1041. /**
  1042. * get all rows of table
  1043. *
  1044. * @access public
  1045. * @internal param \Cygnite\Database\fetch $fetchModel type
  1046. * @return array results
  1047. */
  1048. public function getAll()
  1049. {
  1050. return $this->_statement->fetchAll(\PDO::FETCH_CLASS, get_called_class());
  1051. }
  1052. /**
  1053. * @param string $req : the query on which link the values
  1054. * @param array $array : associative array containing the values ??to bind
  1055. * @param array|bool $typeArray : associative array with the desired value for its
  1056. * corresponding key in $array
  1057. * @link http://us2.php.net/manual/en/pdostatement.bindvalue.php#104939
  1058. */
  1059. public function bindArrayValue($req, $array, $typeArray = false)
  1060. {
  1061. if (is_object($req) && ($req instanceof \PDOStatement)) {
  1062. foreach ($array as $key => $value) {
  1063. if ($typeArray) {
  1064. $req->bindValue(":$key", $value, $typeArray[$key]);
  1065. } else {
  1066. if (is_int($value)) {
  1067. $param = \PDO::PARAM_INT;
  1068. } elseif (is_bool($value)) {
  1069. $param = \PDO::PARAM_BOOL;
  1070. } elseif (is_null($value)) {
  1071. $param = \PDO::PARAM_NULL;
  1072. } elseif (is_string($value)) {
  1073. $param = \PDO::PARAM_STR;
  1074. } else {
  1075. $param = false;
  1076. }
  1077. if ($param) {
  1078. $req->bindValue(":$key", $value, $param);
  1079. }
  1080. }
  1081. }
  1082. }
  1083. }
  1084. public function setDebug($sql)
  1085. {
  1086. $this->data[] = $sql;
  1087. }
  1088. protected function debugLastQuery($sql)
  1089. {
  1090. //echo $sql;
  1091. }
  1092. public function setDatabaseConnection($connection)
  1093. {
  1094. $this->pdo = $connection;
  1095. }
  1096. /**
  1097. * Get Database Connection
  1098. *
  1099. * @return null|object
  1100. */
  1101. public function getDatabaseConnection()
  1102. {
  1103. return is_object($this->pdo) ? $this->pdo : null;
  1104. }
  1105. /*
  1106. * Flush results after data retrieving process
  1107. * It will unset all existing properties and close reader in order to make new selection process
  1108. *
  1109. */
  1110. public function flush()
  1111. {
  1112. if ($this->isClosed() == false):
  1113. $this->close();
  1114. $this->closed = false;
  1115. unset($this->_selectColumns);
  1116. unset($this->_fromWhere);
  1117. unset($this->_columnWhere);
  1118. unset($this->_columnWhere_in);
  1119. unset($this->_limitValue);
  1120. unset($this->_columnName);
  1121. unset($this->_offsetValue);
  1122. unset($this->_orderType);
  1123. endif;
  1124. }
  1125. private function setDbStatement($key, $value)
  1126. {
  1127. $this->data[$key] = $value;
  1128. }
  1129. public function getDbStatement($key)
  1130. {
  1131. return (isset($this->data[$key])) ? $this->data[$key] : null;
  1132. }
  1133. /**
  1134. * Closes the reader.
  1135. * This frees up the resources allocated for executing this SQL statement.
  1136. * Read attempts after this method call are unpredictable.
  1137. */
  1138. public function close()
  1139. {
  1140. $statement = null;
  1141. $statement = $this->getDbStatement($this->database);
  1142. $statement->closeCursor();
  1143. $this->closed = true;
  1144. }
  1145. /**
  1146. * whether the reader is closed or not.
  1147. * @return boolean whether the reader is closed or not.
  1148. */
  1149. private function isClosed()
  1150. {
  1151. return $this->closed;
  1152. }
  1153. /*public function getModelProperties()
  1154. {
  1155. echo $this->tableName;
  1156. echo $this->database;
  1157. echo $this->primaryKey;
  1158. var_dump($this->columns);
  1159. }*/
  1160. public function explainQuery()
  1161. {
  1162. $sql = $explain = "";
  1163. $sql = 'EXPLAIN EXTENDED '.$this->sqlQuery;
  1164. $explain = $this->getDatabaseConnection()->query($sql)->fetchAll(\PDO::FETCH_ASSOC);
  1165. $html = "";
  1166. $html .= "<html> <head><title>Explain Query</title>
  1167. <style type='text/css'>
  1168. #contetainer { font-family:Lucida Grande,Verdana,Sans-serif;
  1169. font-size:12px;padding: 20px 20px 12px 20px;margin:40px; background:#fff;
  1170. border:1px solid #D3640F; }
  1171. h2 { color: #990000; font-size: 15px;font-weight: normal;margin: 5px 5px 5px 13px;}
  1172. p { margin:6px; padding: 9px; }
  1173. </style>
  1174. </head><body>
  1175. <div id='contetainer'>
  1176. <table >
  1177. <th>ID</th>
  1178. <th>Query Type</th>
  1179. <th>Table</th>
  1180. <th>Type</th>
  1181. <th>Possible Keys</th>
  1182. <th>Key</th>
  1183. <th>Key Length</th>
  1184. <th>Ref</th>
  1185. <th>Rows</th>
  1186. <th>Filtered</th>
  1187. <th>Extra</th>
  1188. <tr>
  1189. <td> ".$explain[0]['id']."</td>
  1190. <td> ".$explain[0]['select_type']."</td>
  1191. <td> ".$explain[0]['table']."</td>
  1192. <td> ".$explain[0]['type']."</td>
  1193. <td> ".$explain[0]['possible_keys']."</td>
  1194. <td> ".$explain[0]['key']."</td>
  1195. <td> ".$explain[0]['key_len']."</td>
  1196. <td> ".$explain[0]['ref']."</td>
  1197. <td> ".$explain[0]['rows']."</td>
  1198. <td> ".$explain[0]['filtered']."</td>
  1199. <td> ".$explain[0]['Extra']."</td></tr></table></div></body></html>";
  1200. unset($explain);
  1201. return $html;
  1202. }
  1203. public function setPageLimit($number = null)
  1204. {
  1205. if (is_null($number)) {
  1206. $number = $this->setPageLimit();
  1207. }
  1208. $pagination = Pagination::instance();
  1209. $pagination->setPerPage($number);
  1210. }
  1211. public function __destruct()
  1212. {
  1213. unset($this->attributes);
  1214. unset($this->data);
  1215. }
  1216. }