Builder.php 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
  1. <?php
  2. namespace Illuminate\Database\Eloquent;
  3. use Closure;
  4. use BadMethodCallException;
  5. use Illuminate\Support\Arr;
  6. use Illuminate\Support\Str;
  7. use Illuminate\Pagination\Paginator;
  8. use Illuminate\Contracts\Support\Arrayable;
  9. use Illuminate\Database\Concerns\BuildsQueries;
  10. use Illuminate\Database\Eloquent\Relations\Relation;
  11. use Illuminate\Database\Query\Builder as QueryBuilder;
  12. /**
  13. * @mixin \Illuminate\Database\Query\Builder
  14. */
  15. class Builder
  16. {
  17. use BuildsQueries, Concerns\QueriesRelationships;
  18. /**
  19. * The base query builder instance.
  20. *
  21. * @var \Illuminate\Database\Query\Builder
  22. */
  23. protected $query;
  24. /**
  25. * The model being queried.
  26. *
  27. * @var \Illuminate\Database\Eloquent\Model
  28. */
  29. protected $model;
  30. /**
  31. * The relationships that should be eager loaded.
  32. *
  33. * @var array
  34. */
  35. protected $eagerLoad = [];
  36. /**
  37. * All of the globally registered builder macros.
  38. *
  39. * @var array
  40. */
  41. protected static $macros = [];
  42. /**
  43. * All of the locally registered builder macros.
  44. *
  45. * @var array
  46. */
  47. protected $localMacros = [];
  48. /**
  49. * A replacement for the typical delete function.
  50. *
  51. * @var \Closure
  52. */
  53. protected $onDelete;
  54. /**
  55. * The methods that should be returned from query builder.
  56. *
  57. * @var array
  58. */
  59. protected $passthru = [
  60. 'insert', 'insertGetId', 'getBindings', 'toSql',
  61. 'exists', 'doesntExist', 'count', 'min', 'max', 'avg', 'sum', 'getConnection',
  62. ];
  63. /**
  64. * Applied global scopes.
  65. *
  66. * @var array
  67. */
  68. protected $scopes = [];
  69. /**
  70. * Removed global scopes.
  71. *
  72. * @var array
  73. */
  74. protected $removedScopes = [];
  75. /**
  76. * Create a new Eloquent query builder instance.
  77. *
  78. * @param \Illuminate\Database\Query\Builder $query
  79. * @return void
  80. */
  81. public function __construct(QueryBuilder $query)
  82. {
  83. $this->query = $query;
  84. }
  85. /**
  86. * Create and return an un-saved model instance.
  87. *
  88. * @param array $attributes
  89. * @return \Illuminate\Database\Eloquent\Model
  90. */
  91. public function make(array $attributes = [])
  92. {
  93. return $this->newModelInstance($attributes);
  94. }
  95. /**
  96. * Register a new global scope.
  97. *
  98. * @param string $identifier
  99. * @param \Illuminate\Database\Eloquent\Scope|\Closure $scope
  100. * @return $this
  101. */
  102. public function withGlobalScope($identifier, $scope)
  103. {
  104. $this->scopes[$identifier] = $scope;
  105. if (method_exists($scope, 'extend')) {
  106. $scope->extend($this);
  107. }
  108. return $this;
  109. }
  110. /**
  111. * Remove a registered global scope.
  112. *
  113. * @param \Illuminate\Database\Eloquent\Scope|string $scope
  114. * @return $this
  115. */
  116. public function withoutGlobalScope($scope)
  117. {
  118. if (! is_string($scope)) {
  119. $scope = get_class($scope);
  120. }
  121. unset($this->scopes[$scope]);
  122. $this->removedScopes[] = $scope;
  123. return $this;
  124. }
  125. /**
  126. * Remove all or passed registered global scopes.
  127. *
  128. * @param array|null $scopes
  129. * @return $this
  130. */
  131. public function withoutGlobalScopes(array $scopes = null)
  132. {
  133. if (is_array($scopes)) {
  134. foreach ($scopes as $scope) {
  135. $this->withoutGlobalScope($scope);
  136. }
  137. } else {
  138. $this->scopes = [];
  139. }
  140. return $this;
  141. }
  142. /**
  143. * Get an array of global scopes that were removed from the query.
  144. *
  145. * @return array
  146. */
  147. public function removedScopes()
  148. {
  149. return $this->removedScopes;
  150. }
  151. /**
  152. * Add a where clause on the primary key to the query.
  153. *
  154. * @param mixed $id
  155. * @return $this
  156. */
  157. public function whereKey($id)
  158. {
  159. if (is_array($id) || $id instanceof Arrayable) {
  160. $this->query->whereIn($this->model->getQualifiedKeyName(), $id);
  161. return $this;
  162. }
  163. return $this->where($this->model->getQualifiedKeyName(), '=', $id);
  164. }
  165. /**
  166. * Add a where clause on the primary key to the query.
  167. *
  168. * @param mixed $id
  169. * @return $this
  170. */
  171. public function whereKeyNot($id)
  172. {
  173. if (is_array($id) || $id instanceof Arrayable) {
  174. $this->query->whereNotIn($this->model->getQualifiedKeyName(), $id);
  175. return $this;
  176. }
  177. return $this->where($this->model->getQualifiedKeyName(), '!=', $id);
  178. }
  179. /**
  180. * Add a basic where clause to the query.
  181. *
  182. * @param string|array|\Closure $column
  183. * @param string $operator
  184. * @param mixed $value
  185. * @param string $boolean
  186. * @return $this
  187. */
  188. public function where($column, $operator = null, $value = null, $boolean = 'and')
  189. {
  190. if ($column instanceof Closure) {
  191. $query = $this->model->newQueryWithoutScopes();
  192. $column($query);
  193. $this->query->addNestedWhereQuery($query->getQuery(), $boolean);
  194. } else {
  195. $this->query->where(...func_get_args());
  196. }
  197. return $this;
  198. }
  199. /**
  200. * Add an "or where" clause to the query.
  201. *
  202. * @param \Closure|array|string $column
  203. * @param string $operator
  204. * @param mixed $value
  205. * @return \Illuminate\Database\Eloquent\Builder|static
  206. */
  207. public function orWhere($column, $operator = null, $value = null)
  208. {
  209. list($value, $operator) = $this->query->prepareValueAndOperator(
  210. $value, $operator, func_num_args() == 2
  211. );
  212. return $this->where($column, $operator, $value, 'or');
  213. }
  214. /**
  215. * Create a collection of models from plain arrays.
  216. *
  217. * @param array $items
  218. * @return \Illuminate\Database\Eloquent\Collection
  219. */
  220. public function hydrate(array $items)
  221. {
  222. $instance = $this->newModelInstance();
  223. return $instance->newCollection(array_map(function ($item) use ($instance) {
  224. return $instance->newFromBuilder($item);
  225. }, $items));
  226. }
  227. /**
  228. * Create a collection of models from a raw query.
  229. *
  230. * @param string $query
  231. * @param array $bindings
  232. * @return \Illuminate\Database\Eloquent\Collection
  233. */
  234. public function fromQuery($query, $bindings = [])
  235. {
  236. return $this->hydrate(
  237. $this->query->getConnection()->select($query, $bindings)
  238. );
  239. }
  240. /**
  241. * Find a model by its primary key.
  242. *
  243. * @param mixed $id
  244. * @param array $columns
  245. * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|null
  246. */
  247. public function find($id, $columns = ['*'])
  248. {
  249. if (is_array($id) || $id instanceof Arrayable) {
  250. return $this->findMany($id, $columns);
  251. }
  252. return $this->whereKey($id)->first($columns);
  253. }
  254. /**
  255. * Find multiple models by their primary keys.
  256. *
  257. * @param \Illuminate\Contracts\Support\Arrayable|array $ids
  258. * @param array $columns
  259. * @return \Illuminate\Database\Eloquent\Collection
  260. */
  261. public function findMany($ids, $columns = ['*'])
  262. {
  263. if (empty($ids)) {
  264. return $this->model->newCollection();
  265. }
  266. return $this->whereKey($ids)->get($columns);
  267. }
  268. /**
  269. * Find a model by its primary key or throw an exception.
  270. *
  271. * @param mixed $id
  272. * @param array $columns
  273. * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection
  274. *
  275. * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
  276. */
  277. public function findOrFail($id, $columns = ['*'])
  278. {
  279. $result = $this->find($id, $columns);
  280. if (is_array($id)) {
  281. if (count($result) == count(array_unique($id))) {
  282. return $result;
  283. }
  284. } elseif (! is_null($result)) {
  285. return $result;
  286. }
  287. throw (new ModelNotFoundException)->setModel(
  288. get_class($this->model), $id
  289. );
  290. }
  291. /**
  292. * Find a model by its primary key or return fresh model instance.
  293. *
  294. * @param mixed $id
  295. * @param array $columns
  296. * @return \Illuminate\Database\Eloquent\Model
  297. */
  298. public function findOrNew($id, $columns = ['*'])
  299. {
  300. if (! is_null($model = $this->find($id, $columns))) {
  301. return $model;
  302. }
  303. return $this->newModelInstance();
  304. }
  305. /**
  306. * Get the first record matching the attributes or instantiate it.
  307. *
  308. * @param array $attributes
  309. * @param array $values
  310. * @return \Illuminate\Database\Eloquent\Model
  311. */
  312. public function firstOrNew(array $attributes, array $values = [])
  313. {
  314. if (! is_null($instance = $this->where($attributes)->first())) {
  315. return $instance;
  316. }
  317. return $this->newModelInstance($attributes + $values);
  318. }
  319. /**
  320. * Get the first record matching the attributes or create it.
  321. *
  322. * @param array $attributes
  323. * @param array $values
  324. * @return \Illuminate\Database\Eloquent\Model
  325. */
  326. public function firstOrCreate(array $attributes, array $values = [])
  327. {
  328. if (! is_null($instance = $this->where($attributes)->first())) {
  329. return $instance;
  330. }
  331. return tap($this->newModelInstance($attributes + $values), function ($instance) {
  332. $instance->save();
  333. });
  334. }
  335. /**
  336. * Create or update a record matching the attributes, and fill it with values.
  337. *
  338. * @param array $attributes
  339. * @param array $values
  340. * @return \Illuminate\Database\Eloquent\Model
  341. */
  342. public function updateOrCreate(array $attributes, array $values = [])
  343. {
  344. return tap($this->firstOrNew($attributes), function ($instance) use ($values) {
  345. $instance->fill($values)->save();
  346. });
  347. }
  348. /**
  349. * Execute the query and get the first result or throw an exception.
  350. *
  351. * @param array $columns
  352. * @return \Illuminate\Database\Eloquent\Model|static
  353. *
  354. * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
  355. */
  356. public function firstOrFail($columns = ['*'])
  357. {
  358. if (! is_null($model = $this->first($columns))) {
  359. return $model;
  360. }
  361. throw (new ModelNotFoundException)->setModel(get_class($this->model));
  362. }
  363. /**
  364. * Execute the query and get the first result or call a callback.
  365. *
  366. * @param \Closure|array $columns
  367. * @param \Closure|null $callback
  368. * @return \Illuminate\Database\Eloquent\Model|static|mixed
  369. */
  370. public function firstOr($columns = ['*'], Closure $callback = null)
  371. {
  372. if ($columns instanceof Closure) {
  373. $callback = $columns;
  374. $columns = ['*'];
  375. }
  376. if (! is_null($model = $this->first($columns))) {
  377. return $model;
  378. }
  379. return call_user_func($callback);
  380. }
  381. /**
  382. * Get a single column's value from the first result of a query.
  383. *
  384. * @param string $column
  385. * @return mixed
  386. */
  387. public function value($column)
  388. {
  389. if ($result = $this->first([$column])) {
  390. return $result->{$column};
  391. }
  392. }
  393. /**
  394. * Execute the query as a "select" statement.
  395. *
  396. * @param array $columns
  397. * @return \Illuminate\Database\Eloquent\Collection|static[]
  398. */
  399. public function get($columns = ['*'])
  400. {
  401. $builder = $this->applyScopes();
  402. // If we actually found models we will also eager load any relationships that
  403. // have been specified as needing to be eager loaded, which will solve the
  404. // n+1 query issue for the developers to avoid running a lot of queries.
  405. if (count($models = $builder->getModels($columns)) > 0) {
  406. $models = $builder->eagerLoadRelations($models);
  407. }
  408. return $builder->getModel()->newCollection($models);
  409. }
  410. /**
  411. * Get the hydrated models without eager loading.
  412. *
  413. * @param array $columns
  414. * @return \Illuminate\Database\Eloquent\Model[]
  415. */
  416. public function getModels($columns = ['*'])
  417. {
  418. return $this->model->hydrate(
  419. $this->query->get($columns)->all()
  420. )->all();
  421. }
  422. /**
  423. * Eager load the relationships for the models.
  424. *
  425. * @param array $models
  426. * @return array
  427. */
  428. public function eagerLoadRelations(array $models)
  429. {
  430. foreach ($this->eagerLoad as $name => $constraints) {
  431. // For nested eager loads we'll skip loading them here and they will be set as an
  432. // eager load on the query to retrieve the relation so that they will be eager
  433. // loaded on that query, because that is where they get hydrated as models.
  434. if (strpos($name, '.') === false) {
  435. $models = $this->eagerLoadRelation($models, $name, $constraints);
  436. }
  437. }
  438. return $models;
  439. }
  440. /**
  441. * Eagerly load the relationship on a set of models.
  442. *
  443. * @param array $models
  444. * @param string $name
  445. * @param \Closure $constraints
  446. * @return array
  447. */
  448. protected function eagerLoadRelation(array $models, $name, Closure $constraints)
  449. {
  450. // First we will "back up" the existing where conditions on the query so we can
  451. // add our eager constraints. Then we will merge the wheres that were on the
  452. // query back to it in order that any where conditions might be specified.
  453. $relation = $this->getRelation($name);
  454. $relation->addEagerConstraints($models);
  455. $constraints($relation);
  456. // Once we have the results, we just match those back up to their parent models
  457. // using the relationship instance. Then we just return the finished arrays
  458. // of models which have been eagerly hydrated and are readied for return.
  459. return $relation->match(
  460. $relation->initRelation($models, $name),
  461. $relation->getEager(), $name
  462. );
  463. }
  464. /**
  465. * Get the relation instance for the given relation name.
  466. *
  467. * @param string $name
  468. * @return \Illuminate\Database\Eloquent\Relations\Relation
  469. */
  470. public function getRelation($name)
  471. {
  472. // We want to run a relationship query without any constrains so that we will
  473. // not have to remove these where clauses manually which gets really hacky
  474. // and error prone. We don't want constraints because we add eager ones.
  475. $relation = Relation::noConstraints(function () use ($name) {
  476. try {
  477. return $this->getModel()->{$name}();
  478. } catch (BadMethodCallException $e) {
  479. throw RelationNotFoundException::make($this->getModel(), $name);
  480. }
  481. });
  482. $nested = $this->relationsNestedUnder($name);
  483. // If there are nested relationships set on the query, we will put those onto
  484. // the query instances so that they can be handled after this relationship
  485. // is loaded. In this way they will all trickle down as they are loaded.
  486. if (count($nested) > 0) {
  487. $relation->getQuery()->with($nested);
  488. }
  489. return $relation;
  490. }
  491. /**
  492. * Get the deeply nested relations for a given top-level relation.
  493. *
  494. * @param string $relation
  495. * @return array
  496. */
  497. protected function relationsNestedUnder($relation)
  498. {
  499. $nested = [];
  500. // We are basically looking for any relationships that are nested deeper than
  501. // the given top-level relationship. We will just check for any relations
  502. // that start with the given top relations and adds them to our arrays.
  503. foreach ($this->eagerLoad as $name => $constraints) {
  504. if ($this->isNestedUnder($relation, $name)) {
  505. $nested[substr($name, strlen($relation.'.'))] = $constraints;
  506. }
  507. }
  508. return $nested;
  509. }
  510. /**
  511. * Determine if the relationship is nested.
  512. *
  513. * @param string $relation
  514. * @param string $name
  515. * @return bool
  516. */
  517. protected function isNestedUnder($relation, $name)
  518. {
  519. return Str::contains($name, '.') && Str::startsWith($name, $relation.'.');
  520. }
  521. /**
  522. * Get a generator for the given query.
  523. *
  524. * @return \Generator
  525. */
  526. public function cursor()
  527. {
  528. foreach ($this->applyScopes()->query->cursor() as $record) {
  529. yield $this->model->newFromBuilder($record);
  530. }
  531. }
  532. /**
  533. * Chunk the results of a query by comparing numeric IDs.
  534. *
  535. * @param int $count
  536. * @param callable $callback
  537. * @param string $column
  538. * @param string|null $alias
  539. * @return bool
  540. */
  541. public function chunkById($count, callable $callback, $column = null, $alias = null)
  542. {
  543. $column = is_null($column) ? $this->getModel()->getKeyName() : $column;
  544. $alias = is_null($alias) ? $column : $alias;
  545. $lastId = 0;
  546. do {
  547. $clone = clone $this;
  548. // We'll execute the query for the given page and get the results. If there are
  549. // no results we can just break and return from here. When there are results
  550. // we will call the callback with the current chunk of these results here.
  551. $results = $clone->forPageAfterId($count, $lastId, $column)->get();
  552. $countResults = $results->count();
  553. if ($countResults == 0) {
  554. break;
  555. }
  556. // On each chunk result set, we will pass them to the callback and then let the
  557. // developer take care of everything within the callback, which allows us to
  558. // keep the memory low for spinning through large result sets for working.
  559. if ($callback($results) === false) {
  560. return false;
  561. }
  562. $lastId = $results->last()->{$alias};
  563. unset($results);
  564. } while ($countResults == $count);
  565. return true;
  566. }
  567. /**
  568. * Add a generic "order by" clause if the query doesn't already have one.
  569. *
  570. * @return void
  571. */
  572. protected function enforceOrderBy()
  573. {
  574. if (empty($this->query->orders) && empty($this->query->unionOrders)) {
  575. $this->orderBy($this->model->getQualifiedKeyName(), 'asc');
  576. }
  577. }
  578. /**
  579. * Get an array with the values of a given column.
  580. *
  581. * @param string $column
  582. * @param string|null $key
  583. * @return \Illuminate\Support\Collection
  584. */
  585. public function pluck($column, $key = null)
  586. {
  587. $results = $this->toBase()->pluck($column, $key);
  588. // If the model has a mutator for the requested column, we will spin through
  589. // the results and mutate the values so that the mutated version of these
  590. // columns are returned as you would expect from these Eloquent models.
  591. if (! $this->model->hasGetMutator($column) &&
  592. ! $this->model->hasCast($column) &&
  593. ! in_array($column, $this->model->getDates())) {
  594. return $results;
  595. }
  596. return $results->map(function ($value) use ($column) {
  597. return $this->model->newFromBuilder([$column => $value])->{$column};
  598. });
  599. }
  600. /**
  601. * Paginate the given query.
  602. *
  603. * @param int $perPage
  604. * @param array $columns
  605. * @param string $pageName
  606. * @param int|null $page
  607. * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
  608. *
  609. * @throws \InvalidArgumentException
  610. */
  611. public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
  612. {
  613. $page = $page ?: Paginator::resolveCurrentPage($pageName);
  614. $perPage = $perPage ?: $this->model->getPerPage();
  615. $results = ($total = $this->toBase()->getCountForPagination())
  616. ? $this->forPage($page, $perPage)->get($columns)
  617. : $this->model->newCollection();
  618. return $this->paginator($results, $total, $perPage, $page, [
  619. 'path' => Paginator::resolveCurrentPath(),
  620. 'pageName' => $pageName,
  621. ]);
  622. }
  623. /**
  624. * Paginate the given query into a simple paginator.
  625. *
  626. * @param int $perPage
  627. * @param array $columns
  628. * @param string $pageName
  629. * @param int|null $page
  630. * @return \Illuminate\Contracts\Pagination\Paginator
  631. */
  632. public function simplePaginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
  633. {
  634. $page = $page ?: Paginator::resolveCurrentPage($pageName);
  635. $perPage = $perPage ?: $this->model->getPerPage();
  636. // Next we will set the limit and offset for this query so that when we get the
  637. // results we get the proper section of results. Then, we'll create the full
  638. // paginator instances for these results with the given page and per page.
  639. $this->skip(($page - 1) * $perPage)->take($perPage + 1);
  640. return $this->simplePaginator($this->get($columns), $perPage, $page, [
  641. 'path' => Paginator::resolveCurrentPath(),
  642. 'pageName' => $pageName,
  643. ]);
  644. }
  645. /**
  646. * Save a new model and return the instance.
  647. *
  648. * @param array $attributes
  649. * @return \Illuminate\Database\Eloquent\Model|$this
  650. */
  651. public function create(array $attributes = [])
  652. {
  653. return tap($this->newModelInstance($attributes), function ($instance) {
  654. $instance->save();
  655. });
  656. }
  657. /**
  658. * Save a new model and return the instance. Allow mass-assignment.
  659. *
  660. * @param array $attributes
  661. * @return \Illuminate\Database\Eloquent\Model|$this
  662. */
  663. public function forceCreate(array $attributes)
  664. {
  665. return $this->model->unguarded(function () use ($attributes) {
  666. return $this->newModelInstance()->create($attributes);
  667. });
  668. }
  669. /**
  670. * Update a record in the database.
  671. *
  672. * @param array $values
  673. * @return int
  674. */
  675. public function update(array $values)
  676. {
  677. return $this->toBase()->update($this->addUpdatedAtColumn($values));
  678. }
  679. /**
  680. * Increment a column's value by a given amount.
  681. *
  682. * @param string $column
  683. * @param int $amount
  684. * @param array $extra
  685. * @return int
  686. */
  687. public function increment($column, $amount = 1, array $extra = [])
  688. {
  689. return $this->toBase()->increment(
  690. $column, $amount, $this->addUpdatedAtColumn($extra)
  691. );
  692. }
  693. /**
  694. * Decrement a column's value by a given amount.
  695. *
  696. * @param string $column
  697. * @param int $amount
  698. * @param array $extra
  699. * @return int
  700. */
  701. public function decrement($column, $amount = 1, array $extra = [])
  702. {
  703. return $this->toBase()->decrement(
  704. $column, $amount, $this->addUpdatedAtColumn($extra)
  705. );
  706. }
  707. /**
  708. * Add the "updated at" column to an array of values.
  709. *
  710. * @param array $values
  711. * @return array
  712. */
  713. protected function addUpdatedAtColumn(array $values)
  714. {
  715. if (! $this->model->usesTimestamps()) {
  716. return $values;
  717. }
  718. return Arr::add(
  719. $values, $this->model->getUpdatedAtColumn(),
  720. $this->model->freshTimestampString()
  721. );
  722. }
  723. /**
  724. * Delete a record from the database.
  725. *
  726. * @return mixed
  727. */
  728. public function delete()
  729. {
  730. if (isset($this->onDelete)) {
  731. return call_user_func($this->onDelete, $this);
  732. }
  733. return $this->toBase()->delete();
  734. }
  735. /**
  736. * Run the default delete function on the builder.
  737. *
  738. * Since we do not apply scopes here, the row will actually be deleted.
  739. *
  740. * @return mixed
  741. */
  742. public function forceDelete()
  743. {
  744. return $this->query->delete();
  745. }
  746. /**
  747. * Register a replacement for the default delete function.
  748. *
  749. * @param \Closure $callback
  750. * @return void
  751. */
  752. public function onDelete(Closure $callback)
  753. {
  754. $this->onDelete = $callback;
  755. }
  756. /**
  757. * Call the given local model scopes.
  758. *
  759. * @param array $scopes
  760. * @return mixed
  761. */
  762. public function scopes(array $scopes)
  763. {
  764. $builder = $this;
  765. foreach ($scopes as $scope => $parameters) {
  766. // If the scope key is an integer, then the scope was passed as the value and
  767. // the parameter list is empty, so we will format the scope name and these
  768. // parameters here. Then, we'll be ready to call the scope on the model.
  769. if (is_int($scope)) {
  770. list($scope, $parameters) = [$parameters, []];
  771. }
  772. // Next we'll pass the scope callback to the callScope method which will take
  773. // care of grouping the "wheres" properly so the logical order doesn't get
  774. // messed up when adding scopes. Then we'll return back out the builder.
  775. $builder = $builder->callScope(
  776. [$this->model, 'scope'.ucfirst($scope)],
  777. (array) $parameters
  778. );
  779. }
  780. return $builder;
  781. }
  782. /**
  783. * Apply the scopes to the Eloquent builder instance and return it.
  784. *
  785. * @return \Illuminate\Database\Eloquent\Builder|static
  786. */
  787. public function applyScopes()
  788. {
  789. if (! $this->scopes) {
  790. return $this;
  791. }
  792. $builder = clone $this;
  793. foreach ($this->scopes as $identifier => $scope) {
  794. if (! isset($builder->scopes[$identifier])) {
  795. continue;
  796. }
  797. $builder->callScope(function (Builder $builder) use ($scope) {
  798. // If the scope is a Closure we will just go ahead and call the scope with the
  799. // builder instance. The "callScope" method will properly group the clauses
  800. // that are added to this query so "where" clauses maintain proper logic.
  801. if ($scope instanceof Closure) {
  802. $scope($builder);
  803. }
  804. // If the scope is a scope object, we will call the apply method on this scope
  805. // passing in the builder and the model instance. After we run all of these
  806. // scopes we will return back the builder instance to the outside caller.
  807. if ($scope instanceof Scope) {
  808. $scope->apply($builder, $this->getModel());
  809. }
  810. });
  811. }
  812. return $builder;
  813. }
  814. /**
  815. * Apply the given scope on the current builder instance.
  816. *
  817. * @param callable $scope
  818. * @param array $parameters
  819. * @return mixed
  820. */
  821. protected function callScope(callable $scope, $parameters = [])
  822. {
  823. array_unshift($parameters, $this);
  824. $query = $this->getQuery();
  825. // We will keep track of how many wheres are on the query before running the
  826. // scope so that we can properly group the added scope constraints in the
  827. // query as their own isolated nested where statement and avoid issues.
  828. $originalWhereCount = is_null($query->wheres)
  829. ? 0 : count($query->wheres);
  830. $result = $scope(...array_values($parameters)) ?? $this;
  831. if (count((array) $query->wheres) > $originalWhereCount) {
  832. $this->addNewWheresWithinGroup($query, $originalWhereCount);
  833. }
  834. return $result;
  835. }
  836. /**
  837. * Nest where conditions by slicing them at the given where count.
  838. *
  839. * @param \Illuminate\Database\Query\Builder $query
  840. * @param int $originalWhereCount
  841. * @return void
  842. */
  843. protected function addNewWheresWithinGroup(QueryBuilder $query, $originalWhereCount)
  844. {
  845. // Here, we totally remove all of the where clauses since we are going to
  846. // rebuild them as nested queries by slicing the groups of wheres into
  847. // their own sections. This is to prevent any confusing logic order.
  848. $allWheres = $query->wheres;
  849. $query->wheres = [];
  850. $this->groupWhereSliceForScope(
  851. $query, array_slice($allWheres, 0, $originalWhereCount)
  852. );
  853. $this->groupWhereSliceForScope(
  854. $query, array_slice($allWheres, $originalWhereCount)
  855. );
  856. }
  857. /**
  858. * Slice where conditions at the given offset and add them to the query as a nested condition.
  859. *
  860. * @param \Illuminate\Database\Query\Builder $query
  861. * @param array $whereSlice
  862. * @return void
  863. */
  864. protected function groupWhereSliceForScope(QueryBuilder $query, $whereSlice)
  865. {
  866. $whereBooleans = collect($whereSlice)->pluck('boolean');
  867. // Here we'll check if the given subset of where clauses contains any "or"
  868. // booleans and in this case create a nested where expression. That way
  869. // we don't add any unnecessary nesting thus keeping the query clean.
  870. if ($whereBooleans->contains('or')) {
  871. $query->wheres[] = $this->createNestedWhere(
  872. $whereSlice, $whereBooleans->first()
  873. );
  874. } else {
  875. $query->wheres = array_merge($query->wheres, $whereSlice);
  876. }
  877. }
  878. /**
  879. * Create a where array with nested where conditions.
  880. *
  881. * @param array $whereSlice
  882. * @param string $boolean
  883. * @return array
  884. */
  885. protected function createNestedWhere($whereSlice, $boolean = 'and')
  886. {
  887. $whereGroup = $this->getQuery()->forNestedWhere();
  888. $whereGroup->wheres = $whereSlice;
  889. return ['type' => 'Nested', 'query' => $whereGroup, 'boolean' => $boolean];
  890. }
  891. /**
  892. * Set the relationships that should be eager loaded.
  893. *
  894. * @param mixed $relations
  895. * @return $this
  896. */
  897. public function with($relations)
  898. {
  899. $eagerLoad = $this->parseWithRelations(is_string($relations) ? func_get_args() : $relations);
  900. $this->eagerLoad = array_merge($this->eagerLoad, $eagerLoad);
  901. return $this;
  902. }
  903. /**
  904. * Prevent the specified relations from being eager loaded.
  905. *
  906. * @param mixed $relations
  907. * @return $this
  908. */
  909. public function without($relations)
  910. {
  911. $this->eagerLoad = array_diff_key($this->eagerLoad, array_flip(
  912. is_string($relations) ? func_get_args() : $relations
  913. ));
  914. return $this;
  915. }
  916. /**
  917. * Create a new instance of the model being queried.
  918. *
  919. * @param array $attributes
  920. * @return \Illuminate\Database\Eloquent\Model
  921. */
  922. public function newModelInstance($attributes = [])
  923. {
  924. return $this->model->newInstance($attributes)->setConnection(
  925. $this->query->getConnection()->getName()
  926. );
  927. }
  928. /**
  929. * Parse a list of relations into individuals.
  930. *
  931. * @param array $relations
  932. * @return array
  933. */
  934. protected function parseWithRelations(array $relations)
  935. {
  936. $results = [];
  937. foreach ($relations as $name => $constraints) {
  938. // If the "relation" value is actually a numeric key, we can assume that no
  939. // constraints have been specified for the eager load and we'll just put
  940. // an empty Closure with the loader so that we can treat all the same.
  941. if (is_numeric($name)) {
  942. $name = $constraints;
  943. list($name, $constraints) = Str::contains($name, ':')
  944. ? $this->createSelectWithConstraint($name)
  945. : [$name, function () {
  946. //
  947. }];
  948. }
  949. // We need to separate out any nested includes. Which allows the developers
  950. // to load deep relationships using "dots" without stating each level of
  951. // the relationship with its own key in the array of eager load names.
  952. $results = $this->addNestedWiths($name, $results);
  953. $results[$name] = $constraints;
  954. }
  955. return $results;
  956. }
  957. /**
  958. * Create a constraint to select the given columns for the relation.
  959. *
  960. * @param string $name
  961. * @return array
  962. */
  963. protected function createSelectWithConstraint($name)
  964. {
  965. return [explode(':', $name)[0], function ($query) use ($name) {
  966. $query->select(explode(',', explode(':', $name)[1]));
  967. }];
  968. }
  969. /**
  970. * Parse the nested relationships in a relation.
  971. *
  972. * @param string $name
  973. * @param array $results
  974. * @return array
  975. */
  976. protected function addNestedWiths($name, $results)
  977. {
  978. $progress = [];
  979. // If the relation has already been set on the result array, we will not set it
  980. // again, since that would override any constraints that were already placed
  981. // on the relationships. We will only set the ones that are not specified.
  982. foreach (explode('.', $name) as $segment) {
  983. $progress[] = $segment;
  984. if (! isset($results[$last = implode('.', $progress)])) {
  985. $results[$last] = function () {
  986. //
  987. };
  988. }
  989. }
  990. return $results;
  991. }
  992. /**
  993. * Get the underlying query builder instance.
  994. *
  995. * @return \Illuminate\Database\Query\Builder
  996. */
  997. public function getQuery()
  998. {
  999. return $this->query;
  1000. }
  1001. /**
  1002. * Set the underlying query builder instance.
  1003. *
  1004. * @param \Illuminate\Database\Query\Builder $query
  1005. * @return $this
  1006. */
  1007. public function setQuery($query)
  1008. {
  1009. $this->query = $query;
  1010. return $this;
  1011. }
  1012. /**
  1013. * Get a base query builder instance.
  1014. *
  1015. * @return \Illuminate\Database\Query\Builder
  1016. */
  1017. public function toBase()
  1018. {
  1019. return $this->applyScopes()->getQuery();
  1020. }
  1021. /**
  1022. * Get the relationships being eagerly loaded.
  1023. *
  1024. * @return array
  1025. */
  1026. public function getEagerLoads()
  1027. {
  1028. return $this->eagerLoad;
  1029. }
  1030. /**
  1031. * Set the relationships being eagerly loaded.
  1032. *
  1033. * @param array $eagerLoad
  1034. * @return $this
  1035. */
  1036. public function setEagerLoads(array $eagerLoad)
  1037. {
  1038. $this->eagerLoad = $eagerLoad;
  1039. return $this;
  1040. }
  1041. /**
  1042. * Get the model instance being queried.
  1043. *
  1044. * @return \Illuminate\Database\Eloquent\Model
  1045. */
  1046. public function getModel()
  1047. {
  1048. return $this->model;
  1049. }
  1050. /**
  1051. * Set a model instance for the model being queried.
  1052. *
  1053. * @param \Illuminate\Database\Eloquent\Model $model
  1054. * @return $this
  1055. */
  1056. public function setModel(Model $model)
  1057. {
  1058. $this->model = $model;
  1059. $this->query->from($model->getTable());
  1060. return $this;
  1061. }
  1062. /**
  1063. * Qualify the given column name by the model's table.
  1064. *
  1065. * @param string $column
  1066. * @return string
  1067. */
  1068. public function qualifyColumn($column)
  1069. {
  1070. return $this->model->qualifyColumn($column);
  1071. }
  1072. /**
  1073. * Get the given macro by name.
  1074. *
  1075. * @param string $name
  1076. * @return \Closure
  1077. */
  1078. public function getMacro($name)
  1079. {
  1080. return Arr::get($this->localMacros, $name);
  1081. }
  1082. /**
  1083. * Dynamically handle calls into the query instance.
  1084. *
  1085. * @param string $method
  1086. * @param array $parameters
  1087. * @return mixed
  1088. */
  1089. public function __call($method, $parameters)
  1090. {
  1091. if ($method === 'macro') {
  1092. $this->localMacros[$parameters[0]] = $parameters[1];
  1093. return;
  1094. }
  1095. if (isset($this->localMacros[$method])) {
  1096. array_unshift($parameters, $this);
  1097. return $this->localMacros[$method](...$parameters);
  1098. }
  1099. if (isset(static::$macros[$method])) {
  1100. if (static::$macros[$method] instanceof Closure) {
  1101. return call_user_func_array(static::$macros[$method]->bindTo($this, static::class), $parameters);
  1102. }
  1103. return call_user_func_array(static::$macros[$method], $parameters);
  1104. }
  1105. if (method_exists($this->model, $scope = 'scope'.ucfirst($method))) {
  1106. return $this->callScope([$this->model, $scope], $parameters);
  1107. }
  1108. if (in_array($method, $this->passthru)) {
  1109. return $this->toBase()->{$method}(...$parameters);
  1110. }
  1111. $this->query->{$method}(...$parameters);
  1112. return $this;
  1113. }
  1114. /**
  1115. * Dynamically handle calls into the query instance.
  1116. *
  1117. * @param string $method
  1118. * @param array $parameters
  1119. * @return mixed
  1120. *
  1121. * @throws \BadMethodCallException
  1122. */
  1123. public static function __callStatic($method, $parameters)
  1124. {
  1125. if ($method === 'macro') {
  1126. static::$macros[$parameters[0]] = $parameters[1];
  1127. return;
  1128. }
  1129. if (! isset(static::$macros[$method])) {
  1130. throw new BadMethodCallException("Method {$method} does not exist.");
  1131. }
  1132. if (static::$macros[$method] instanceof Closure) {
  1133. return call_user_func_array(Closure::bind(static::$macros[$method], null, static::class), $parameters);
  1134. }
  1135. return call_user_func_array(static::$macros[$method], $parameters);
  1136. }
  1137. /**
  1138. * Force a clone of the underlying query builder when cloning.
  1139. *
  1140. * @return void
  1141. */
  1142. public function __clone()
  1143. {
  1144. $this->query = clone $this->query;
  1145. }
  1146. }