Builder.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. <?php namespace Illuminate\Database\Eloquent;
  2. use Closure;
  3. use DateTime;
  4. use Illuminate\Database\Query\Expression;
  5. use Illuminate\Database\Eloquent\Relations\Relation;
  6. use Illuminate\Database\Query\Builder as QueryBuilder;
  7. class Builder {
  8. /**
  9. * The base query builder instance.
  10. *
  11. * @var \Illuminate\Database\Query\Builder
  12. */
  13. protected $query;
  14. /**
  15. * The model being queried.
  16. *
  17. * @var \Illuminate\Database\Eloquent\Model
  18. */
  19. protected $model;
  20. /**
  21. * The relationships that should be eager loaded.
  22. *
  23. * @var array
  24. */
  25. protected $eagerLoad = array();
  26. /**
  27. * The methods that should be returned from query builder.
  28. *
  29. * @var array
  30. */
  31. protected $passthru = array(
  32. 'toSql', 'lists', 'insert', 'insertGetId', 'pluck',
  33. 'count', 'min', 'max', 'avg', 'sum', 'exists',
  34. );
  35. /**
  36. * Create a new Eloquent query builder instance.
  37. *
  38. * @param \Illuminate\Database\Query\Builder $query
  39. * @return void
  40. */
  41. public function __construct(QueryBuilder $query)
  42. {
  43. $this->query = $query;
  44. }
  45. /**
  46. * Find a model by its primary key.
  47. *
  48. * @param mixed $id
  49. * @param array $columns
  50. * @return \Illuminate\Database\Eloquent\Model|static|null
  51. */
  52. public function find($id, $columns = array('*'))
  53. {
  54. $this->query->where($this->model->getKeyName(), '=', $id);
  55. return $this->first($columns);
  56. }
  57. /**
  58. * Find a model by its primary key or throw an exception.
  59. *
  60. * @param mixed $id
  61. * @param array $columns
  62. * @return \Illuminate\Database\Eloquent\Model|static
  63. */
  64. public function findOrFail($id, $columns = array('*'))
  65. {
  66. if ( ! is_null($model = $this->find($id, $columns))) return $model;
  67. throw new ModelNotFoundException;
  68. }
  69. /**
  70. * Execute the query and get the first result.
  71. *
  72. * @param array $columns
  73. * @return \Illuminate\Database\Eloquent\Model|static|null
  74. */
  75. public function first($columns = array('*'))
  76. {
  77. return $this->take(1)->get($columns)->first();
  78. }
  79. /**
  80. * Execute the query and get the first result or throw an exception.
  81. *
  82. * @param array $columns
  83. * @return \Illuminate\Database\Eloquent\Model|static
  84. */
  85. public function firstOrFail($columns = array('*'))
  86. {
  87. if ( ! is_null($model = $this->first($columns))) return $model;
  88. throw new ModelNotFoundException;
  89. }
  90. /**
  91. * Execute the query as a "select" statement.
  92. *
  93. * @param array $columns
  94. * @return \Illuminate\Database\Eloquent\Collection|static[]
  95. */
  96. public function get($columns = array('*'))
  97. {
  98. $models = $this->getModels($columns);
  99. // If we actually found models we will also eager load any relationships that
  100. // have been specified as needing to be eager loaded, which will solve the
  101. // n+1 query issue for the developers to avoid running a lot of queries.
  102. if (count($models) > 0)
  103. {
  104. $models = $this->eagerLoadRelations($models);
  105. }
  106. return $this->model->newCollection($models);
  107. }
  108. /**
  109. * Pluck a single column from the database.
  110. *
  111. * @param string $column
  112. * @return mixed
  113. */
  114. public function pluck($column)
  115. {
  116. $result = $this->first(array($column));
  117. if ($result) return $result->{$column};
  118. }
  119. /**
  120. * Get an array with the values of a given column.
  121. *
  122. * @param string $column
  123. * @param string $key
  124. * @return array
  125. */
  126. public function lists($column, $key = null)
  127. {
  128. $results = $this->query->lists($column, $key);
  129. // If the model has a mutator for the requested column, we will spin through
  130. // the results and mutate the values so that the mutated version of these
  131. // columns are returned as you would expect from these Eloquent models.
  132. if ($this->model->hasGetMutator($column))
  133. {
  134. foreach ($results as $key => &$value)
  135. {
  136. $fill = array($column => $value);
  137. $value = $this->model->newFromBuilder($fill)->$column;
  138. }
  139. }
  140. return $results;
  141. }
  142. /**
  143. * Get a paginator for the "select" statement.
  144. *
  145. * @param int $perPage
  146. * @param array $columns
  147. * @return \Illuminate\Pagination\Paginator
  148. */
  149. public function paginate($perPage = null, $columns = array('*'))
  150. {
  151. $perPage = $perPage ?: $this->model->getPerPage();
  152. $paginator = $this->query->getConnection()->getPaginator();
  153. if (isset($this->query->groups))
  154. {
  155. return $this->groupedPaginate($paginator, $perPage, $columns);
  156. }
  157. else
  158. {
  159. return $this->ungroupedPaginate($paginator, $perPage, $columns);
  160. }
  161. }
  162. /**
  163. * Get a paginator for a grouped statement.
  164. *
  165. * @param \Illuminate\Pagination\Environment $paginator
  166. * @param int $perPage
  167. * @param array $columns
  168. * @return \Illuminate\Pagination\Paginator
  169. */
  170. protected function groupedPaginate($paginator, $perPage, $columns)
  171. {
  172. $results = $this->get($columns)->all();
  173. return $this->query->buildRawPaginator($paginator, $results, $perPage);
  174. }
  175. /**
  176. * Get a paginator for an ungrouped statement.
  177. *
  178. * @param \Illuminate\Pagination\Environment $paginator
  179. * @param int $perPage
  180. * @param array $columns
  181. * @return \Illuminate\Pagination\Paginator
  182. */
  183. protected function ungroupedPaginate($paginator, $perPage, $columns)
  184. {
  185. $total = $this->query->getPaginationCount();
  186. // Once we have the paginator we need to set the limit and offset values for
  187. // the query so we can get the properly paginated items. Once we have an
  188. // array of items we can create the paginator instances for the items.
  189. $page = $paginator->getCurrentPage();
  190. $this->query->forPage($page, $perPage);
  191. return $paginator->make($this->get($columns)->all(), $total, $perPage);
  192. }
  193. /**
  194. * Update a record in the database.
  195. *
  196. * @param array $values
  197. * @return int
  198. */
  199. public function update(array $values)
  200. {
  201. return $this->query->update($this->addUpdatedAtColumn($values));
  202. }
  203. /**
  204. * Increment a column's value by a given amount.
  205. *
  206. * @param string $column
  207. * @param int $amount
  208. * @param array $extra
  209. * @return int
  210. */
  211. public function increment($column, $amount = 1, array $extra = array())
  212. {
  213. $extra = $this->addUpdatedAtColumn($extra);
  214. return $this->query->increment($column, $amount, $extra);
  215. }
  216. /**
  217. * Decrement a column's value by a given amount.
  218. *
  219. * @param string $column
  220. * @param int $amount
  221. * @param array $extra
  222. * @return int
  223. */
  224. public function decrement($column, $amount = 1, array $extra = array())
  225. {
  226. $extra = $this->addUpdatedAtColumn($extra);
  227. return $this->query->decrement($column, $amount, $extra);
  228. }
  229. /**
  230. * Add the "updated at" column to an array of values.
  231. *
  232. * @param array $values
  233. * @return array
  234. */
  235. protected function addUpdatedAtColumn(array $values)
  236. {
  237. if ( ! $this->model->usesTimestamps()) return $values;
  238. $column = $this->model->getUpdatedAtColumn();
  239. return array_add($values, $column, $this->model->freshTimestampString());
  240. }
  241. /**
  242. * Delete a record from the database.
  243. *
  244. * @return int
  245. */
  246. public function delete()
  247. {
  248. if ($this->model->isSoftDeleting())
  249. {
  250. return $this->softDelete();
  251. }
  252. else
  253. {
  254. return $this->query->delete();
  255. }
  256. }
  257. /**
  258. * Soft delete the record in the database.
  259. *
  260. * @return int
  261. */
  262. protected function softDelete()
  263. {
  264. $column = $this->model->getDeletedAtColumn();
  265. return $this->update(array($column => $this->model->freshTimestampString()));
  266. }
  267. /**
  268. * Force a delete on a set of soft deleted models.
  269. *
  270. * @return int
  271. */
  272. public function forceDelete()
  273. {
  274. return $this->query->delete();
  275. }
  276. /**
  277. * Restore the soft-deleted model instances.
  278. *
  279. * @return int
  280. */
  281. public function restore()
  282. {
  283. if ($this->model->isSoftDeleting())
  284. {
  285. $column = $this->model->getDeletedAtColumn();
  286. return $this->update(array($column => null));
  287. }
  288. }
  289. /**
  290. * Include the soft deleted models in the results.
  291. *
  292. * @return \Illuminate\Database\Eloquent\Builder|static
  293. */
  294. public function withTrashed()
  295. {
  296. $column = $this->model->getQualifiedDeletedAtColumn();
  297. foreach ($this->query->wheres as $key => $where)
  298. {
  299. // If the where clause is a soft delete date constraint, we will remove it from
  300. // the query and reset the keys on the wheres. This allows this developer to
  301. // include deleted model in a relationship result set that is lazy loaded.
  302. if ($this->isSoftDeleteConstraint($where, $column))
  303. {
  304. unset($this->query->wheres[$key]);
  305. $this->query->wheres = array_values($this->query->wheres);
  306. }
  307. }
  308. return $this;
  309. }
  310. /**
  311. * Force the result set to only included soft deletes.
  312. *
  313. * @return \Illuminate\Database\Eloquent\Builder|static
  314. */
  315. public function onlyTrashed()
  316. {
  317. $this->withTrashed();
  318. $this->query->whereNotNull($this->model->getQualifiedDeletedAtColumn());
  319. return $this;
  320. }
  321. /**
  322. * Determine if the given where clause is a soft delete constraint.
  323. *
  324. * @param array $where
  325. * @param string $column
  326. * @return bool
  327. */
  328. protected function isSoftDeleteConstraint(array $where, $column)
  329. {
  330. return $where['column'] == $column and $where['type'] == 'Null';
  331. }
  332. /**
  333. * Get the hydrated models without eager loading.
  334. *
  335. * @param array $columns
  336. * @return array|static[]
  337. */
  338. public function getModels($columns = array('*'))
  339. {
  340. // First, we will simply get the raw results from the query builders which we
  341. // can use to populate an array with Eloquent models. We will pass columns
  342. // that should be selected as well, which are typically just everything.
  343. $results = $this->query->get($columns);
  344. $connection = $this->model->getConnectionName();
  345. $models = array();
  346. // Once we have the results, we can spin through them and instantiate a fresh
  347. // model instance for each records we retrieved from the database. We will
  348. // also set the proper connection name for the model after we create it.
  349. foreach ($results as $result)
  350. {
  351. $models[] = $model = $this->model->newFromBuilder($result);
  352. $model->setConnection($connection);
  353. }
  354. return $models;
  355. }
  356. /**
  357. * Eager load the relationships for the models.
  358. *
  359. * @param array $models
  360. * @return array
  361. */
  362. public function eagerLoadRelations(array $models)
  363. {
  364. foreach ($this->eagerLoad as $name => $constraints)
  365. {
  366. // For nested eager loads we'll skip loading them here and they will be set as an
  367. // eager load on the query to retrieve the relation so that they will be eager
  368. // loaded on that query, because that is where they get hydrated as models.
  369. if (strpos($name, '.') === false)
  370. {
  371. $models = $this->loadRelation($models, $name, $constraints);
  372. }
  373. }
  374. return $models;
  375. }
  376. /**
  377. * Eagerly load the relationship on a set of models.
  378. *
  379. * @param array $models
  380. * @param string $name
  381. * @param \Closure $constraints
  382. * @return array
  383. */
  384. protected function loadRelation(array $models, $name, Closure $constraints)
  385. {
  386. // First we will "back up" the existing where conditions on the query so we can
  387. // add our eager constraints. Then we will merge the wheres that were on the
  388. // query back to it in order that any where conditions might be specified.
  389. $relation = $this->getRelation($name);
  390. $relation->addEagerConstraints($models);
  391. call_user_func($constraints, $relation);
  392. $models = $relation->initRelation($models, $name);
  393. // Once we have the results, we just match those back up to their parent models
  394. // using the relationship instance. Then we just return the finished arrays
  395. // of models which have been eagerly hydrated and are readied for return.
  396. $results = $relation->get();
  397. return $relation->match($models, $results, $name);
  398. }
  399. /**
  400. * Get the relation instance for the given relation name.
  401. *
  402. * @param string $relation
  403. * @return \Illuminate\Database\Eloquent\Relations\Relation
  404. */
  405. public function getRelation($relation)
  406. {
  407. $me = $this;
  408. // We want to run a relationship query without any constrains so that we will
  409. // not have to remove these where clauses manually which gets really hacky
  410. // and is error prone while we remove the developer's own where clauses.
  411. $query = Relation::noConstraints(function() use ($me, $relation)
  412. {
  413. return $me->getModel()->$relation();
  414. });
  415. $nested = $this->nestedRelations($relation);
  416. // If there are nested relationships set on the query, we will put those onto
  417. // the query instances so that they can be handled after this relationship
  418. // is loaded. In this way they will all trickle down as they are loaded.
  419. if (count($nested) > 0)
  420. {
  421. $query->getQuery()->with($nested);
  422. }
  423. return $query;
  424. }
  425. /**
  426. * Get the deeply nested relations for a given top-level relation.
  427. *
  428. * @param string $relation
  429. * @return array
  430. */
  431. protected function nestedRelations($relation)
  432. {
  433. $nested = array();
  434. // We are basically looking for any relationships that are nested deeper than
  435. // the given top-level relationship. We will just check for any relations
  436. // that start with the given top relations and adds them to our arrays.
  437. foreach ($this->eagerLoad as $name => $constraints)
  438. {
  439. if ($this->isNested($name, $relation))
  440. {
  441. $nested[substr($name, strlen($relation.'.'))] = $constraints;
  442. }
  443. }
  444. return $nested;
  445. }
  446. /**
  447. * Determine if the relationship is nested.
  448. *
  449. * @param string $name
  450. * @param string $relation
  451. * @return bool
  452. */
  453. protected function isNested($name, $relation)
  454. {
  455. $dots = str_contains($name, '.');
  456. return $dots and starts_with($name, $relation) and $name != $relation;
  457. }
  458. /**
  459. * Add a relationship count condition to the query.
  460. *
  461. * @param string $relation
  462. * @param string $operator
  463. * @param int $count
  464. * @param string $boolean
  465. * @return \Illuminate\Database\Eloquent\Builder|static
  466. */
  467. public function has($relation, $operator = '>=', $count = 1, $boolean = 'and')
  468. {
  469. $instance = $this->model->$relation();
  470. $query = $instance->getRelationCountQuery($instance->getRelated()->newQuery());
  471. $this->query->mergeBindings($query->getQuery());
  472. return $this->where(new Expression('('.$query->toSql().')'), $operator, $count, $boolean);
  473. }
  474. /**
  475. * Add a relationship count condition to the query with an "or".
  476. *
  477. * @param string $relation
  478. * @param string $operator
  479. * @param int $count
  480. * @return \Illuminate\Database\Eloquent\Builder|static
  481. */
  482. public function orHas($relation, $operator = '>=', $count = 1)
  483. {
  484. return $this->has($relation, $operator, $count, 'or');
  485. }
  486. /**
  487. * Set the relationships that should be eager loaded.
  488. *
  489. * @param dynamic $relations
  490. * @return \Illuminate\Database\Eloquent\Builder|static
  491. */
  492. public function with($relations)
  493. {
  494. if (is_string($relations)) $relations = func_get_args();
  495. $eagers = $this->parseRelations($relations);
  496. $this->eagerLoad = array_merge($this->eagerLoad, $eagers);
  497. return $this;
  498. }
  499. /**
  500. * Parse a list of relations into individuals.
  501. *
  502. * @param array $relations
  503. * @return array
  504. */
  505. protected function parseRelations(array $relations)
  506. {
  507. $results = array();
  508. foreach ($relations as $name => $constraints)
  509. {
  510. // If the "relation" value is actually a numeric key, we can assume that no
  511. // constraints have been specified for the eager load and we'll just put
  512. // an empty Closure with the loader so that we can treat all the same.
  513. if (is_numeric($name))
  514. {
  515. $f = function() {};
  516. list($name, $constraints) = array($constraints, $f);
  517. }
  518. // We need to separate out any nested includes. Which allows the developers
  519. // to load deep relationships using "dots" without stating each level of
  520. // the relationship with its own key in the array of eager load names.
  521. $results = $this->parseNested($name, $results);
  522. $results[$name] = $constraints;
  523. }
  524. return $results;
  525. }
  526. /**
  527. * Parse the nested relationships in a relation.
  528. *
  529. * @param string $name
  530. * @param array $results
  531. * @return array
  532. */
  533. protected function parseNested($name, $results)
  534. {
  535. $progress = array();
  536. // If the relation has already been set on the result array, we will not set it
  537. // again, since that would override any constraints that were already placed
  538. // on the relationships. We will only set the ones that are not specified.
  539. foreach (explode('.', $name) as $segment)
  540. {
  541. $progress[] = $segment;
  542. if ( ! isset($results[$last = implode('.', $progress)]))
  543. {
  544. $results[$last] = function() {};
  545. }
  546. }
  547. return $results;
  548. }
  549. /**
  550. * Get the underlying query builder instance.
  551. *
  552. * @return \Illuminate\Database\Query\Builder|static
  553. */
  554. public function getQuery()
  555. {
  556. return $this->query;
  557. }
  558. /**
  559. * Set the underlying query builder instance.
  560. *
  561. * @param \Illuminate\Database\Query\Builder $query
  562. * @return void
  563. */
  564. public function setQuery($query)
  565. {
  566. $this->query = $query;
  567. }
  568. /**
  569. * Get the relationships being eagerly loaded.
  570. *
  571. * @return array
  572. */
  573. public function getEagerLoads()
  574. {
  575. return $this->eagerLoad;
  576. }
  577. /**
  578. * Set the relationships being eagerly loaded.
  579. *
  580. * @param array $eagerLoad
  581. * @return void
  582. */
  583. public function setEagerLoads(array $eagerLoad)
  584. {
  585. $this->eagerLoad = $eagerLoad;
  586. }
  587. /**
  588. * Get the model instance being queried.
  589. *
  590. * @return \Illuminate\Database\Eloquent\Model
  591. */
  592. public function getModel()
  593. {
  594. return $this->model;
  595. }
  596. /**
  597. * Set a model instance for the model being queried.
  598. *
  599. * @param \Illuminate\Database\Eloquent\Model $model
  600. * @return \Illuminate\Database\Eloquent\Builder
  601. */
  602. public function setModel(Model $model)
  603. {
  604. $this->model = $model;
  605. $this->query->from($model->getTable());
  606. return $this;
  607. }
  608. /**
  609. * Dynamically handle calls into the query instance.
  610. *
  611. * @param string $method
  612. * @param array $parameters
  613. * @return mixed
  614. */
  615. public function __call($method, $parameters)
  616. {
  617. if (method_exists($this->model, $scope = 'scope'.ucfirst($method)))
  618. {
  619. array_unshift($parameters, $this);
  620. call_user_func_array(array($this->model, $scope), $parameters);
  621. }
  622. else
  623. {
  624. $result = call_user_func_array(array($this->query, $method), $parameters);
  625. }
  626. return in_array($method, $this->passthru) ? $result : $this;
  627. }
  628. }