PageRenderTime 63ms CodeModel.GetById 23ms RepoModel.GetById 2ms app.codeStats 1ms

/laravel/database/query.php

https://github.com/kulafihi/laravel
PHP | 868 lines | 313 code | 123 blank | 432 comment | 15 complexity | cfe61319d888fde37ca40177a8036012 MD5 | raw file
Possible License(s): MIT
  1. <?php namespace Laravel\Database;
  2. use Closure;
  3. use Laravel\Database;
  4. use Paginator;
  5. use Laravel\Database\Query\Grammars\Postgres;
  6. use Laravel\Database\Query\Grammars\SQLServer;
  7. class Query {
  8. /**
  9. * The database connection.
  10. *
  11. * @var Connection
  12. */
  13. public $connection;
  14. /**
  15. * The query grammar instance.
  16. *
  17. * @var Query\Grammars\Grammar
  18. */
  19. public $grammar;
  20. /**
  21. * The SELECT clause.
  22. *
  23. * @var array
  24. */
  25. public $selects;
  26. /**
  27. * The aggregating column and function.
  28. *
  29. * @var array
  30. */
  31. public $aggregate;
  32. /**
  33. * Indicates if the query should return distinct results.
  34. *
  35. * @var bool
  36. */
  37. public $distinct = false;
  38. /**
  39. * The table name.
  40. *
  41. * @var string
  42. */
  43. public $from;
  44. /**
  45. * The table joins.
  46. *
  47. * @var array
  48. */
  49. public $joins;
  50. /**
  51. * The WHERE clauses.
  52. *
  53. * @var array
  54. */
  55. public $wheres;
  56. /**
  57. * The GROUP BY clauses.
  58. *
  59. * @var array
  60. */
  61. public $groupings;
  62. /**
  63. * The HAVING clauses.
  64. *
  65. * @var array
  66. */
  67. public $havings;
  68. /**
  69. * The ORDER BY clauses.
  70. *
  71. * @var array
  72. */
  73. public $orderings;
  74. /**
  75. * The LIMIT value.
  76. *
  77. * @var int
  78. */
  79. public $limit;
  80. /**
  81. * The OFFSET value.
  82. *
  83. * @var int
  84. */
  85. public $offset;
  86. /**
  87. * The query value bindings.
  88. *
  89. * @var array
  90. */
  91. public $bindings = array();
  92. /**
  93. * Create a new query instance.
  94. *
  95. * @param Connection $connection
  96. * @param Grammar $grammar
  97. * @param string $table
  98. * @return void
  99. */
  100. public function __construct(Connection $connection, Query\Grammars\Grammar $grammar, $table)
  101. {
  102. $this->from = $table;
  103. $this->grammar = $grammar;
  104. $this->connection = $connection;
  105. }
  106. /**
  107. * Force the query to return distinct results.
  108. *
  109. * @return Query
  110. */
  111. public function distinct()
  112. {
  113. $this->distinct = true;
  114. return $this;
  115. }
  116. /**
  117. * Add an array of columns to the SELECT clause.
  118. *
  119. * @param array $columns
  120. * @return Query
  121. */
  122. public function select($columns = array('*'))
  123. {
  124. $this->selects = is_array($columns) ? $columns : array($columns);
  125. return $this;
  126. }
  127. /**
  128. * Add a join clause to the query.
  129. *
  130. * @param string $table
  131. * @param string $column1
  132. * @param string $operator
  133. * @param string $column2
  134. * @param string $type
  135. * @return Query
  136. */
  137. public function join($table, $column1, $operator = null, $column2 = null, $type = 'INNER')
  138. {
  139. // If the "column" is really an instance of a Closure, the developer is
  140. // trying to create a join with a complex "ON" clause. So, we will add
  141. // the join, and then call the Closure with the join.
  142. if ($column1 instanceof Closure)
  143. {
  144. $this->joins[] = new Query\Join($type, $table);
  145. call_user_func($column1, end($this->joins));
  146. }
  147. // If the column is just a string, we can assume that the join just
  148. // has a simple on clause, and we'll create the join instance and
  149. // add the clause automatically for the developer.
  150. else
  151. {
  152. $join = new Query\Join($type, $table);
  153. $join->on($column1, $operator, $column2);
  154. $this->joins[] = $join;
  155. }
  156. return $this;
  157. }
  158. /**
  159. * Add a left join to the query.
  160. *
  161. * @param string $table
  162. * @param string $column1
  163. * @param string $operator
  164. * @param string $column2
  165. * @return Query
  166. */
  167. public function left_join($table, $column1, $operator = null, $column2 = null)
  168. {
  169. return $this->join($table, $column1, $operator, $column2, 'LEFT');
  170. }
  171. /**
  172. * Reset the where clause to its initial state.
  173. *
  174. * @return void
  175. */
  176. public function reset_where()
  177. {
  178. list($this->wheres, $this->bindings) = array(array(), array());
  179. }
  180. /**
  181. * Add a raw where condition to the query.
  182. *
  183. * @param string $where
  184. * @param array $bindings
  185. * @param string $connector
  186. * @return Query
  187. */
  188. public function raw_where($where, $bindings = array(), $connector = 'AND')
  189. {
  190. $this->wheres[] = array('type' => 'where_raw', 'connector' => $connector, 'sql' => $where);
  191. $this->bindings = array_merge($this->bindings, $bindings);
  192. return $this;
  193. }
  194. /**
  195. * Add a raw or where condition to the query.
  196. *
  197. * @param string $where
  198. * @param array $bindings
  199. * @return Query
  200. */
  201. public function raw_or_where($where, $bindings = array())
  202. {
  203. return $this->raw_where($where, $bindings, 'OR');
  204. }
  205. /**
  206. * Add a where condition to the query.
  207. *
  208. * @param string $column
  209. * @param string $operator
  210. * @param mixed $value
  211. * @param string $connector
  212. * @return Query
  213. */
  214. public function where($column, $operator = null, $value = null, $connector = 'AND')
  215. {
  216. // If a Closure is passed into the method, it means a nested where
  217. // clause is being initiated, so we will take a different course
  218. // of action than when the statement is just a simple where.
  219. if ($column instanceof Closure)
  220. {
  221. return $this->where_nested($column, $connector);
  222. }
  223. $type = 'where';
  224. $this->wheres[] = compact('type', 'column', 'operator', 'value', 'connector');
  225. $this->bindings[] = $value;
  226. return $this;
  227. }
  228. /**
  229. * Add an or where condition to the query.
  230. *
  231. * @param string $column
  232. * @param string $operator
  233. * @param mixed $value
  234. * @return Query
  235. */
  236. public function or_where($column, $operator = null, $value = null)
  237. {
  238. return $this->where($column, $operator, $value, 'OR');
  239. }
  240. /**
  241. * Add an or where condition for the primary key to the query.
  242. *
  243. * @param mixed $value
  244. * @return Query
  245. */
  246. public function or_where_id($value)
  247. {
  248. return $this->or_where('id', '=', $value);
  249. }
  250. /**
  251. * Add a where in condition to the query.
  252. *
  253. * @param string $column
  254. * @param array $values
  255. * @param string $connector
  256. * @param bool $not
  257. * @return Query
  258. */
  259. public function where_in($column, $values, $connector = 'AND', $not = false)
  260. {
  261. $type = ($not) ? 'where_not_in' : 'where_in';
  262. $this->wheres[] = compact('type', 'column', 'values', 'connector');
  263. $this->bindings = array_merge($this->bindings, $values);
  264. return $this;
  265. }
  266. /**
  267. * Add an or where in condition to the query.
  268. *
  269. * @param string $column
  270. * @param array $values
  271. * @return Query
  272. */
  273. public function or_where_in($column, $values)
  274. {
  275. return $this->where_in($column, $values, 'OR');
  276. }
  277. /**
  278. * Add a where not in condition to the query.
  279. *
  280. * @param string $column
  281. * @param array $values
  282. * @param string $connector
  283. * @return Query
  284. */
  285. public function where_not_in($column, $values, $connector = 'AND')
  286. {
  287. return $this->where_in($column, $values, $connector, true);
  288. }
  289. /**
  290. * Add an or where not in condition to the query.
  291. *
  292. * @param string $column
  293. * @param array $values
  294. * @return Query
  295. */
  296. public function or_where_not_in($column, $values)
  297. {
  298. return $this->where_not_in($column, $values, 'OR');
  299. }
  300. /**
  301. * Add a where null condition to the query.
  302. *
  303. * @param string $column
  304. * @param string $connector
  305. * @param bool $not
  306. * @return Query
  307. */
  308. public function where_null($column, $connector = 'AND', $not = false)
  309. {
  310. $type = ($not) ? 'where_not_null' : 'where_null';
  311. $this->wheres[] = compact('type', 'column', 'connector');
  312. return $this;
  313. }
  314. /**
  315. * Add an or where null condition to the query.
  316. *
  317. * @param string $column
  318. * @return Query
  319. */
  320. public function or_where_null($column)
  321. {
  322. return $this->where_null($column, 'OR');
  323. }
  324. /**
  325. * Add a where not null condition to the query.
  326. *
  327. * @param string $column
  328. * @param string $connector
  329. * @return Query
  330. */
  331. public function where_not_null($column, $connector = 'AND')
  332. {
  333. return $this->where_null($column, $connector, true);
  334. }
  335. /**
  336. * Add an or where not null condition to the query.
  337. *
  338. * @param string $column
  339. * @return Query
  340. */
  341. public function or_where_not_null($column)
  342. {
  343. return $this->where_not_null($column, 'OR');
  344. }
  345. /**
  346. * Add nested constraints to the query.
  347. *
  348. * @param Closure $callback
  349. * @param string $connector
  350. * @return Query
  351. */
  352. public function where_nested($callback, $connector = 'AND')
  353. {
  354. call_user_func($callback, $this);
  355. return $this;
  356. }
  357. /**
  358. * Add dynamic where conditions to the query.
  359. *
  360. * @param string $method
  361. * @param array $parameters
  362. * @return Query
  363. */
  364. private function dynamic_where($method, $parameters)
  365. {
  366. $finder = substr($method, 6);
  367. $flags = PREG_SPLIT_DELIM_CAPTURE;
  368. $segments = preg_split('/(_and_|_or_)/i', $finder, -1, $flags);
  369. // The connector variable will determine which connector will be used
  370. // for the condition. We'll change it as we come across new boolean
  371. // connectors in the dynamic method string.
  372. //
  373. // The index variable helps us get the correct parameter value for
  374. // the where condition. We increment it each time we add another
  375. // condition to the query's where clause.
  376. $connector = 'AND';
  377. $index = 0;
  378. foreach ($segments as $segment)
  379. {
  380. // If the segment is not a boolean connector, we can assume it is
  381. // a column name, and we'll add it to the query as a new constraint
  382. // of the query's where clause and keep iterating the segments.
  383. if ($segment != '_and_' and $segment != '_or_')
  384. {
  385. $this->where($segment, '=', $parameters[$index], $connector);
  386. $index++;
  387. }
  388. // Otherwise, we will store the connector so we know how the next
  389. // where clause we find in the query should be connected to the
  390. // previous one and will add it when we find the next one.
  391. else
  392. {
  393. $connector = trim(strtoupper($segment), '_');
  394. }
  395. }
  396. return $this;
  397. }
  398. /**
  399. * Add a grouping to the query.
  400. *
  401. * @param string $column
  402. * @return Query
  403. */
  404. public function group_by($column)
  405. {
  406. $this->groupings[] = $column;
  407. return $this;
  408. }
  409. /**
  410. * Add a having to the query.
  411. *
  412. * @param string $column
  413. * @param string $operator
  414. * @param mixed $value
  415. * @return Query
  416. */
  417. public function having($column, $operator, $value)
  418. {
  419. $this->havings[] = compact('column', 'operator', 'value');
  420. $this->bindings[] = $value;
  421. return $this;
  422. }
  423. /**
  424. * Add an ordering to the query.
  425. *
  426. * @param string $column
  427. * @param string $direction
  428. * @return Query
  429. */
  430. public function order_by($column, $direction = 'asc')
  431. {
  432. $this->orderings[] = compact('column', 'direction');
  433. return $this;
  434. }
  435. /**
  436. * Set the query offset.
  437. *
  438. * @param int $value
  439. * @return Query
  440. */
  441. public function skip($value)
  442. {
  443. $this->offset = $value;
  444. return $this;
  445. }
  446. /**
  447. * Set the query limit.
  448. *
  449. * @param int $value
  450. * @return Query
  451. */
  452. public function take($value)
  453. {
  454. $this->limit = $value;
  455. return $this;
  456. }
  457. /**
  458. * Set the query limit and offset for a given page.
  459. *
  460. * @param int $page
  461. * @param int $per_page
  462. * @return Query
  463. */
  464. public function for_page($page, $per_page)
  465. {
  466. return $this->skip(($page - 1) * $per_page)->take($per_page);
  467. }
  468. /**
  469. * Find a record by the primary key.
  470. *
  471. * @param int $id
  472. * @param array $columns
  473. * @return object
  474. */
  475. public function find($id, $columns = array('*'))
  476. {
  477. return $this->where('id', '=', $id)->first($columns);
  478. }
  479. /**
  480. * Execute the query as a SELECT statement and return a single column.
  481. *
  482. * @param string $column
  483. * @return mixed
  484. */
  485. public function only($column)
  486. {
  487. $sql = $this->grammar->select($this->select(array($column)));
  488. return $this->connection->only($sql, $this->bindings);
  489. }
  490. /**
  491. * Execute the query as a SELECT statement and return the first result.
  492. *
  493. * @param array $columns
  494. * @return mixed
  495. */
  496. public function first($columns = array('*'))
  497. {
  498. $columns = (array) $columns;
  499. // Since we only need the first result, we'll go ahead and set the
  500. // limit clause to 1, since this will be much faster than getting
  501. // all of the rows and then only returning the first.
  502. $results = $this->take(1)->get($columns);
  503. return (count($results) > 0) ? $results[0] : null;
  504. }
  505. /**
  506. * Get an array with the values of a given column.
  507. *
  508. * @param string $column
  509. * @param string $key
  510. * @return array
  511. */
  512. public function lists($column, $key = null)
  513. {
  514. $columns = (is_null($key)) ? array($column) : array($column, $key);
  515. $results = $this->get($columns);
  516. // First we will get the array of values for the requested column.
  517. // Of course, this array will simply have numeric keys. After we
  518. // have this array we will determine if we need to key the array
  519. // by another column from the result set.
  520. $values = array_map(function($row) use ($column)
  521. {
  522. return $row->$column;
  523. }, $results);
  524. // If a key was provided, we will extract an array of keys and
  525. // set the keys on the array of values using the array_combine
  526. // function provided by PHP, which should give us the proper
  527. // array form to return from the method.
  528. if ( ! is_null($key))
  529. {
  530. return array_combine(array_map(function($row) use ($key)
  531. {
  532. return $row->$key;
  533. }, $results), $values);
  534. }
  535. return $values;
  536. }
  537. /**
  538. * Execute the query as a SELECT statement.
  539. *
  540. * @param array $columns
  541. * @return array
  542. */
  543. public function get($columns = array('*'))
  544. {
  545. if (is_null($this->selects)) $this->select($columns);
  546. $sql = $this->grammar->select($this);
  547. $results = $this->connection->query($sql, $this->bindings);
  548. // If the query has an offset and we are using the SQL Server grammar,
  549. // we need to spin through the results and remove the "rownum" from
  550. // each of the objects since there is no "offset".
  551. if ($this->offset > 0 and $this->grammar instanceof SQLServer)
  552. {
  553. array_walk($results, function($result)
  554. {
  555. unset($result->rownum);
  556. });
  557. }
  558. // Reset the SELECT clause so more queries can be performed using
  559. // the same instance. This is helpful for getting aggregates and
  560. // then getting actual results from the query.
  561. $this->selects = null;
  562. return $results;
  563. }
  564. /**
  565. * Get an aggregate value.
  566. *
  567. * @param string $aggregator
  568. * @param array $columns
  569. * @return mixed
  570. */
  571. public function aggregate($aggregator, $columns)
  572. {
  573. // We'll set the aggregate value so the grammar does not try to compile
  574. // a SELECT clause on the query. If an aggregator is present, its own
  575. // grammar function will be used to build the SQL syntax.
  576. $this->aggregate = compact('aggregator', 'columns');
  577. $sql = $this->grammar->select($this);
  578. $result = $this->connection->only($sql, $this->bindings);
  579. // Reset the aggregate so more queries can be performed using the same
  580. // instance. This is helpful for getting aggregates and then getting
  581. // actual results from the query such as during paging.
  582. $this->aggregate = null;
  583. return $result;
  584. }
  585. /**
  586. * Get the paginated query results as a Paginator instance.
  587. *
  588. * @param int $per_page
  589. * @param array $columns
  590. * @return Paginator
  591. */
  592. public function paginate($per_page = 20, $columns = array('*'))
  593. {
  594. // Because some database engines may throw errors if we leave orderings
  595. // on the query when retrieving the total number of records, we'll drop
  596. // all of the orderings and put them back on the query.
  597. list($orderings, $this->orderings) = array($this->orderings, null);
  598. $total = $this->count(reset($columns));
  599. $page = Paginator::page($total, $per_page);
  600. $this->orderings = $orderings;
  601. // Now we're ready to get the actual pagination results from the table
  602. // using the for_page and get methods. The "for_page" method provides
  603. // a convenient way to set the paging limit and offset.
  604. $results = $this->for_page($page, $per_page)->get($columns);
  605. return Paginator::make($results, $total, $per_page);
  606. }
  607. /**
  608. * Insert an array of values into the database table.
  609. *
  610. * @param array $values
  611. * @return bool
  612. */
  613. public function insert($values)
  614. {
  615. // Force every insert to be treated like a batch insert to make creating
  616. // the binding array simpler since we can just spin through the inserted
  617. // rows as if there was more than one every time.
  618. if ( ! is_array(reset($values))) $values = array($values);
  619. $bindings = array();
  620. // We need to merge the insert values into the array of the query
  621. // bindings so that they will be bound to the PDO statement when it
  622. // is executed by the database connection.
  623. foreach ($values as $value)
  624. {
  625. $bindings = array_merge($bindings, array_values($value));
  626. }
  627. $sql = $this->grammar->insert($this, $values);
  628. return $this->connection->query($sql, $bindings);
  629. }
  630. /**
  631. * Insert an array of values into the database table and return the ID.
  632. *
  633. * @param array $values
  634. * @param string $column
  635. * @return int
  636. */
  637. public function insert_get_id($values, $column = 'id')
  638. {
  639. $sql = $this->grammar->insert_get_id($this, $values, $column);
  640. $result = $this->connection->query($sql, array_values($values));
  641. if ($this->grammar instanceof Postgres)
  642. {
  643. return (int) $result[0]->$column;
  644. }
  645. else
  646. {
  647. return (int) $this->connection->pdo->lastInsertId();
  648. }
  649. }
  650. /**
  651. * Increment the value of a column by a given amount.
  652. *
  653. * @param string $column
  654. * @param int $amount
  655. * @return int
  656. */
  657. public function increment($column, $amount = 1)
  658. {
  659. return $this->adjust($column, $amount, ' + ');
  660. }
  661. /**
  662. * Decrement the value of a column by a given amount.
  663. *
  664. * @param string $column
  665. * @param int $amount
  666. * @return int
  667. */
  668. public function decrement($column, $amount = 1)
  669. {
  670. return $this->adjust($column, $amount, ' - ');
  671. }
  672. /**
  673. * Adjust the value of a column up or down by a given amount.
  674. *
  675. * @param string $column
  676. * @param int $amount
  677. * @param string $operator
  678. * @return int
  679. */
  680. protected function adjust($column, $amount, $operator)
  681. {
  682. $wrapped = $this->grammar->wrap($column);
  683. // To make the adjustment to the column, we'll wrap the expression in an
  684. // Expression instance, which forces the adjustment to be injected into
  685. // the query as a string instead of bound.
  686. $value = Database::raw($wrapped.$operator.$amount);
  687. return $this->update(array($column => $value));
  688. }
  689. /**
  690. * Update an array of values in the database table.
  691. *
  692. * @param array $values
  693. * @return int
  694. */
  695. public function update($values)
  696. {
  697. // For update statements, we need to merge the bindings such that the update
  698. // values occur before the where bindings in the array since the sets will
  699. // precede any of the where clauses in the SQL syntax that is generated.
  700. $bindings = array_merge(array_values($values), $this->bindings);
  701. $sql = $this->grammar->update($this, $values);
  702. return $this->connection->query($sql, $bindings);
  703. }
  704. /**
  705. * Execute the query as a DELETE statement.
  706. *
  707. * Optionally, an ID may be passed to the method to delete a specific row.
  708. *
  709. * @param int $id
  710. * @return int
  711. */
  712. public function delete($id = null)
  713. {
  714. // If an ID is given to the method, we'll set the where clause to
  715. // match on the value of the ID. This allows the developer to
  716. // quickly delete a row by its primary key value.
  717. if ( ! is_null($id))
  718. {
  719. $this->where('id', '=', $id);
  720. }
  721. $sql = $this->grammar->delete($this);
  722. return $this->connection->query($sql, $this->bindings);
  723. }
  724. /**
  725. * Magic Method for handling dynamic functions.
  726. *
  727. * This method handles calls to aggregates as well as dynamic where clauses.
  728. */
  729. public function __call($method, $parameters)
  730. {
  731. if (strpos($method, 'where_') === 0)
  732. {
  733. return $this->dynamic_where($method, $parameters, $this);
  734. }
  735. // All of the aggregate methods are handled by a single method, so we'll
  736. // catch them all here and then pass them off to the aggregate method
  737. // instead of creating methods for each one of them.
  738. if (in_array($method, array('count', 'min', 'max', 'avg', 'sum')))
  739. {
  740. if (count($parameters) == 0) $parameters[0] = '*';
  741. return $this->aggregate(strtoupper($method), (array) $parameters[0]);
  742. }
  743. throw new \Exception("Method [$method] is not defined on the Query class.");
  744. }
  745. }