query.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. <?php namespace Laravel\Database; use Laravel\Paginator;
  2. class Query {
  3. /**
  4. * The database connection.
  5. *
  6. * @var Connection
  7. */
  8. public $connection;
  9. /**
  10. * The query grammar instance.
  11. *
  12. * @var Grammars\Grammar
  13. */
  14. public $grammar;
  15. /**
  16. * The SELECT clause.
  17. *
  18. * @var array
  19. */
  20. public $selects;
  21. /**
  22. * The aggregating column and function.
  23. *
  24. * @var array
  25. */
  26. public $aggregate;
  27. /**
  28. * Indicates if the query should return distinct results.
  29. *
  30. * @var bool
  31. */
  32. public $distinct = false;
  33. /**
  34. * The table name.
  35. *
  36. * @var string
  37. */
  38. public $from;
  39. /**
  40. * The table joins.
  41. *
  42. * @var array
  43. */
  44. public $joins;
  45. /**
  46. * The WHERE clauses.
  47. *
  48. * @var array
  49. */
  50. public $wheres;
  51. /**
  52. * The ORDER BY clauses.
  53. *
  54. * @var array
  55. */
  56. public $orderings;
  57. /**
  58. * The LIMIT value.
  59. *
  60. * @var int
  61. */
  62. public $limit;
  63. /**
  64. * The OFFSET value.
  65. *
  66. * @var int
  67. */
  68. public $offset;
  69. /**
  70. * The query value bindings.
  71. *
  72. * @var array
  73. */
  74. public $bindings = array();
  75. /**
  76. * Create a new query instance.
  77. *
  78. * @param Connection $connection
  79. * @param Grammars\Grammar $grammar
  80. * @param string $table
  81. * @return void
  82. */
  83. public function __construct(Connection $connection, Grammars\Grammar $grammar, $table)
  84. {
  85. $this->from = $table;
  86. $this->grammar = $grammar;
  87. $this->connection = $connection;
  88. }
  89. /**
  90. * Force the query to return distinct results.
  91. *
  92. * @return Query
  93. */
  94. public function distinct()
  95. {
  96. $this->distinct = true;
  97. return $this;
  98. }
  99. /**
  100. * Add an array of columns to the SELECT clause.
  101. *
  102. * @param array $columns
  103. * @return Query
  104. */
  105. public function select($columns = array('*'))
  106. {
  107. $this->selects = (array) $columns;
  108. return $this;
  109. }
  110. /**
  111. * Add a join clause to the query.
  112. *
  113. * @param string $table
  114. * @param string $column1
  115. * @param string $operator
  116. * @param string $column2
  117. * @param string $type
  118. * @return Query
  119. */
  120. public function join($table, $column1, $operator, $column2, $type = 'INNER')
  121. {
  122. $this->joins[] = compact('type', 'table', 'column1', 'operator', 'column2');
  123. return $this;
  124. }
  125. /**
  126. * Add a left join to the query.
  127. *
  128. * @param string $table
  129. * @param string $column1
  130. * @param string $operator
  131. * @param string $column2
  132. * @return Query
  133. */
  134. public function left_join($table, $column1, $operator, $column2)
  135. {
  136. return $this->join($table, $column1, $operator, $column2, 'LEFT');
  137. }
  138. /**
  139. * Reset the where clause to its initial state. All bindings will be cleared.
  140. *
  141. * @return void
  142. */
  143. public function reset_where()
  144. {
  145. list($this->wheres, $this->bindings) = array(array(), array());
  146. }
  147. /**
  148. * Add a raw where condition to the query.
  149. *
  150. * @param string $where
  151. * @param array $bindings
  152. * @param string $connector
  153. * @return Query
  154. */
  155. public function raw_where($where, $bindings = array(), $connector = 'AND')
  156. {
  157. $this->wheres[] = array('type' => 'raw', 'connector' => $connector, 'sql' => $where);
  158. $this->bindings = array_merge($this->bindings, $bindings);
  159. return $this;
  160. }
  161. /**
  162. * Add a raw or where condition to the query.
  163. *
  164. * @param string $where
  165. * @param array $bindings
  166. * @return Query
  167. */
  168. public function raw_or_where($where, $bindings = array())
  169. {
  170. return $this->raw_where($where, $bindings, 'OR');
  171. }
  172. /**
  173. * Add a where condition to the query.
  174. *
  175. * @param string $column
  176. * @param string $operator
  177. * @param mixed $value
  178. * @param string $connector
  179. * @return Query
  180. */
  181. public function where($column, $operator, $value, $connector = 'AND')
  182. {
  183. $type = 'where';
  184. $this->wheres[] = compact('type', 'column', 'operator', 'value', 'connector');
  185. $this->bindings[] = $value;
  186. return $this;
  187. }
  188. /**
  189. * Add an or where condition to the query.
  190. *
  191. * @param string $column
  192. * @param string $operator
  193. * @param mixed $value
  194. * @return Query
  195. */
  196. public function or_where($column, $operator, $value)
  197. {
  198. return $this->where($column, $operator, $value, 'OR');
  199. }
  200. /**
  201. * Add an or where condition for the primary key to the query.
  202. *
  203. * @param mixed $value
  204. * @return Query
  205. */
  206. public function or_where_id($value)
  207. {
  208. return $this->or_where('id', '=', $value);
  209. }
  210. /**
  211. * Add a where in condition to the query.
  212. *
  213. * @param string $column
  214. * @param array $values
  215. * @param string $connector
  216. * @param bool $not
  217. * @return Query
  218. */
  219. public function where_in($column, $values, $connector = 'AND', $not = false)
  220. {
  221. $type = ($not) ? 'where_not_in' : 'where_in';
  222. $this->wheres[] = compact('type', 'column', 'values', 'connector');
  223. $this->bindings = array_merge($this->bindings, $values);
  224. return $this;
  225. }
  226. /**
  227. * Add an or where in condition to the query.
  228. *
  229. * @param string $column
  230. * @param array $values
  231. * @return Query
  232. */
  233. public function or_where_in($column, $values)
  234. {
  235. return $this->where_in($column, $values, 'OR');
  236. }
  237. /**
  238. * Add a where not in condition to the query.
  239. *
  240. * @param string $column
  241. * @param array $values
  242. * @param string $connector
  243. * @return Query
  244. */
  245. public function where_not_in($column, $values, $connector = 'AND')
  246. {
  247. return $this->where_in($column, $values, $connector, true);
  248. }
  249. /**
  250. * Add an or where not in condition to the query.
  251. *
  252. * @param string $column
  253. * @param array $values
  254. * @return Query
  255. */
  256. public function or_where_not_in($column, $values)
  257. {
  258. return $this->where_not_in($column, $values, 'OR');
  259. }
  260. /**
  261. * Add a where null condition to the query.
  262. *
  263. * @param string $column
  264. * @param string $connector
  265. * @param bool $not
  266. * @return Query
  267. */
  268. public function where_null($column, $connector = 'AND', $not = false)
  269. {
  270. $type = ($not) ? 'where_not_null' : 'where_null';
  271. $this->wheres[] = compact('type', 'column', 'connector');
  272. return $this;
  273. }
  274. /**
  275. * Add an or where null condition to the query.
  276. *
  277. * @param string $column
  278. * @return Query
  279. */
  280. public function or_where_null($column)
  281. {
  282. return $this->where_null($column, 'OR');
  283. }
  284. /**
  285. * Add a where not null condition to the query.
  286. *
  287. * @param string $column
  288. * @param string $connector
  289. * @return Query
  290. */
  291. public function where_not_null($column, $connector = 'AND')
  292. {
  293. return $this->where_null($column, $connector, true);
  294. }
  295. /**
  296. * Add an or where not null condition to the query.
  297. *
  298. * @param string $column
  299. * @return Query
  300. */
  301. public function or_where_not_null($column)
  302. {
  303. return $this->where_not_null($column, 'OR');
  304. }
  305. /**
  306. * Add dynamic where conditions to the query.
  307. *
  308. * Dynamic queries are caught by the __call magic method and are parsed here.
  309. * They provide a convenient, expressive API for building simple conditions.
  310. *
  311. * @param string $method
  312. * @param array $parameters
  313. * @return Query
  314. */
  315. private function dynamic_where($method, $parameters)
  316. {
  317. // Strip the "where_" off of the method.
  318. $finder = substr($method, 6);
  319. // Split the column names from the connectors.
  320. $segments = preg_split('/(_and_|_or_)/i', $finder, -1, PREG_SPLIT_DELIM_CAPTURE);
  321. // The connector variable will determine which connector will be
  322. // used for the condition. We'll change it as we come across new
  323. // connectors in the dynamic method string.
  324. //
  325. // The index variable helps us get the correct parameter value
  326. // for the where condition. We increment it each time we add
  327. // a condition to the query.
  328. $connector = 'AND';
  329. $index = 0;
  330. foreach ($segments as $segment)
  331. {
  332. if ($segment != '_and_' and $segment != '_or_')
  333. {
  334. $this->where($segment, '=', $parameters[$index], $connector);
  335. $index++;
  336. }
  337. else
  338. {
  339. $connector = trim(strtoupper($segment), '_');
  340. }
  341. }
  342. return $this;
  343. }
  344. /**
  345. * Add an ordering to the query.
  346. *
  347. * @param string $column
  348. * @param string $direction
  349. * @return Query
  350. */
  351. public function order_by($column, $direction = 'asc')
  352. {
  353. $this->orderings[] = compact('column', 'direction');
  354. return $this;
  355. }
  356. /**
  357. * Set the query offset.
  358. *
  359. * @param int $value
  360. * @return Query
  361. */
  362. public function skip($value)
  363. {
  364. $this->offset = $value;
  365. return $this;
  366. }
  367. /**
  368. * Set the query limit.
  369. *
  370. * @param int $value
  371. * @return Query
  372. */
  373. public function take($value)
  374. {
  375. $this->limit = $value;
  376. return $this;
  377. }
  378. /**
  379. * Set the query limit and offset for a given page and item per page count.
  380. *
  381. * @param int $page
  382. * @param int $per_page
  383. * @return Query
  384. */
  385. public function for_page($page, $per_page)
  386. {
  387. return $this->skip(($page - 1) * $per_page)->take($per_page);
  388. }
  389. /**
  390. * Find a record by the primary key.
  391. *
  392. * @param int $id
  393. * @param array $columns
  394. * @return object
  395. */
  396. public function find($id, $columns = array('*'))
  397. {
  398. return $this->where('id', '=', $id)->first($columns);
  399. }
  400. /**
  401. * Execute the query as a SELECT statement and return a single column.
  402. *
  403. * @param string $column
  404. * @return mixed
  405. */
  406. public function only($column)
  407. {
  408. $this->select(array($column));
  409. return $this->connection->only($this->grammar->select($this), $this->bindings);
  410. }
  411. /**
  412. * Execute the query as a SELECT statement and return the first result.
  413. *
  414. * If a single column is selected from the database, only the value of that column will be returned.
  415. *
  416. * @param array $columns
  417. * @return mixed
  418. */
  419. public function first($columns = array('*'))
  420. {
  421. $columns = (array) $columns;
  422. return (count($results = $this->take(1)->get($columns)) > 0) ? $results[0] : null;
  423. }
  424. /**
  425. * Execute the query as a SELECT statement.
  426. *
  427. * @param array $columns
  428. * @return array
  429. */
  430. public function get($columns = array('*'))
  431. {
  432. if (is_null($this->selects)) $this->select($columns);
  433. $results = $this->connection->query($this->grammar->select($this), $this->bindings);
  434. // Reset the SELECT clause so more queries can be performed using
  435. // the same instance. This is helpful for getting aggregates and
  436. // then getting actual results.
  437. $this->selects = null;
  438. return $results;
  439. }
  440. /**
  441. * Get an aggregate value.
  442. *
  443. * @param string $aggregate
  444. * @param string $column
  445. * @return mixed
  446. */
  447. private function aggregate($aggregator, $column)
  448. {
  449. $this->aggregate = compact('aggregator', 'column');
  450. $result = $this->connection->only($this->grammar->select($this), $this->bindings);
  451. // Reset the aggregate so more queries can be performed using
  452. // the same instance. This is helpful for getting aggregates
  453. // and then getting actual results.
  454. $this->aggregate = null;
  455. return $result;
  456. }
  457. /**
  458. * Get the paginated query results as a Paginator instance.
  459. *
  460. * @param int $per_page
  461. * @param array $columns
  462. * @return Paginator
  463. */
  464. public function paginate($per_page = 20, $columns = array('*'))
  465. {
  466. // Calculate the current page for the request. The page number
  467. // will be validated and adjusted by the Paginator class,
  468. // so we can assume it is valid.
  469. $page = Paginator::page($total = $this->count(), $per_page);
  470. return Paginator::make($this->for_page($page, $per_page)->get($columns), $total, $per_page);
  471. }
  472. /**
  473. * Insert an array of values into the database table.
  474. *
  475. * @param array $values
  476. * @return bool
  477. */
  478. public function insert($values)
  479. {
  480. // Force every insert to be treated like a batch insert to make creating
  481. // the binding array simpler since we can just spin through the inserted
  482. // rows as if there/ was more than one every time.
  483. if ( ! is_array(reset($values))) $values = array($values);
  484. $bindings = array();
  485. foreach ($values as $value)
  486. {
  487. $bindings = array_merge($bindings, array_values($value));
  488. }
  489. return $this->connection->query($this->grammar->insert($this, $values), $bindings);
  490. }
  491. /**
  492. * Insert an array of values into the database table and
  493. * return the value of the ID column.
  494. *
  495. * @param array $values
  496. * @param string $sequence
  497. * @return int
  498. */
  499. public function insert_get_id($values, $sequence = null)
  500. {
  501. $this->connection->query($this->grammar->insert($this, $values), array_values($values));
  502. return (int) $this->connection->pdo->lastInsertId($sequence);
  503. }
  504. /**
  505. * Increment the value of a column by a given amount.
  506. *
  507. * @param string $column
  508. * @param int $amount
  509. * @return int
  510. */
  511. public function increment($column, $amount = 1)
  512. {
  513. return $this->adjust($column, $amount, ' + ');
  514. }
  515. /**
  516. * Decrement the value of a column by a given amount.
  517. *
  518. * @param string $column
  519. * @param int $amount
  520. * @return int
  521. */
  522. public function decrement($column, $amount = 1)
  523. {
  524. return $this->adjust($column, $amount, ' - ');
  525. }
  526. /**
  527. * Adjust the value of a column up or down by a given amount.
  528. *
  529. * @param string $column
  530. * @param int $amount
  531. * @param string $operator
  532. * @return int
  533. */
  534. protected function adjust($column, $amount, $operator)
  535. {
  536. $value = Manager::raw($this->grammar->wrap($column).$operator.$amount);
  537. return $this->update(array($column => $value));
  538. }
  539. /**
  540. * Update an array of values in the database table.
  541. *
  542. * @param array $values
  543. * @return int
  544. */
  545. public function update($values)
  546. {
  547. $bindings = array_merge(array_values($values), $this->bindings);
  548. return $this->connection->query($this->grammar->update($this, $values), $bindings);
  549. }
  550. /**
  551. * Execute the query as a DELETE statement.
  552. *
  553. * Optionally, an ID may be passed to the method do delete a specific row.
  554. *
  555. * @param int $id
  556. * @return int
  557. */
  558. public function delete($id = null)
  559. {
  560. if ( ! is_null($id)) $this->where('id', '=', $id);
  561. return $this->connection->query($this->grammar->delete($this), $this->bindings);
  562. }
  563. /**
  564. * Magic Method for handling dynamic functions.
  565. *
  566. * This method handles all calls to aggregate functions as well
  567. * as the construction of dynamic where clauses.
  568. */
  569. public function __call($method, $parameters)
  570. {
  571. if (strpos($method, 'where_') === 0)
  572. {
  573. return $this->dynamic_where($method, $parameters, $this);
  574. }
  575. if (in_array($method, array('abs', 'count', 'min', 'max', 'avg', 'sum')))
  576. {
  577. if ($method == 'count')
  578. {
  579. return $this->aggregate(strtoupper($method), '*');
  580. }
  581. else
  582. {
  583. return $this->aggregate(strtoupper($method), $parameters[0]);
  584. }
  585. }
  586. throw new \Exception("Method [$method] is not defined on the Query class.");
  587. }
  588. }