Builder.php 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106
  1. <?php
  2. namespace Illuminate\Database\Eloquent;
  3. use BadMethodCallException;
  4. use Closure;
  5. use Exception;
  6. use Illuminate\Contracts\Database\Eloquent\Builder as BuilderContract;
  7. use Illuminate\Contracts\Database\Query\Expression;
  8. use Illuminate\Contracts\Support\Arrayable;
  9. use Illuminate\Database\Concerns\BuildsQueries;
  10. use Illuminate\Database\Eloquent\Concerns\QueriesRelationships;
  11. use Illuminate\Database\Eloquent\Relations\BelongsToMany;
  12. use Illuminate\Database\Eloquent\Relations\Relation;
  13. use Illuminate\Database\Query\Builder as QueryBuilder;
  14. use Illuminate\Database\RecordsNotFoundException;
  15. use Illuminate\Database\UniqueConstraintViolationException;
  16. use Illuminate\Pagination\Paginator;
  17. use Illuminate\Support\Arr;
  18. use Illuminate\Support\Str;
  19. use Illuminate\Support\Traits\ForwardsCalls;
  20. use ReflectionClass;
  21. use ReflectionMethod;
  22. /**
  23. * @property-read HigherOrderBuilderProxy $orWhere
  24. * @property-read HigherOrderBuilderProxy $whereNot
  25. * @property-read HigherOrderBuilderProxy $orWhereNot
  26. *
  27. * @mixin \Illuminate\Database\Query\Builder
  28. */
  29. class Builder implements BuilderContract
  30. {
  31. use BuildsQueries, ForwardsCalls, QueriesRelationships {
  32. BuildsQueries::sole as baseSole;
  33. }
  34. /**
  35. * The base query builder instance.
  36. *
  37. * @var \Illuminate\Database\Query\Builder
  38. */
  39. protected $query;
  40. /**
  41. * The model being queried.
  42. *
  43. * @var \Illuminate\Database\Eloquent\Model
  44. */
  45. protected $model;
  46. /**
  47. * The relationships that should be eager loaded.
  48. *
  49. * @var array
  50. */
  51. protected $eagerLoad = [];
  52. /**
  53. * All of the globally registered builder macros.
  54. *
  55. * @var array
  56. */
  57. protected static $macros = [];
  58. /**
  59. * All of the locally registered builder macros.
  60. *
  61. * @var array
  62. */
  63. protected $localMacros = [];
  64. /**
  65. * A replacement for the typical delete function.
  66. *
  67. * @var \Closure
  68. */
  69. protected $onDelete;
  70. /**
  71. * The properties that should be returned from query builder.
  72. *
  73. * @var string[]
  74. */
  75. protected $propertyPassthru = [
  76. 'from',
  77. ];
  78. /**
  79. * The methods that should be returned from query builder.
  80. *
  81. * @var string[]
  82. */
  83. protected $passthru = [
  84. 'aggregate',
  85. 'average',
  86. 'avg',
  87. 'count',
  88. 'dd',
  89. 'ddrawsql',
  90. 'doesntexist',
  91. 'doesntexistor',
  92. 'dump',
  93. 'dumprawsql',
  94. 'exists',
  95. 'existsor',
  96. 'explain',
  97. 'getbindings',
  98. 'getconnection',
  99. 'getgrammar',
  100. 'getrawbindings',
  101. 'implode',
  102. 'insert',
  103. 'insertgetid',
  104. 'insertorignore',
  105. 'insertusing',
  106. 'insertorignoreusing',
  107. 'max',
  108. 'min',
  109. 'raw',
  110. 'rawvalue',
  111. 'sum',
  112. 'tosql',
  113. 'torawsql',
  114. ];
  115. /**
  116. * Applied global scopes.
  117. *
  118. * @var array
  119. */
  120. protected $scopes = [];
  121. /**
  122. * Removed global scopes.
  123. *
  124. * @var array
  125. */
  126. protected $removedScopes = [];
  127. /**
  128. * The callbacks that should be invoked after retrieving data from the database.
  129. *
  130. * @var array
  131. */
  132. protected $afterQueryCallbacks = [];
  133. /**
  134. * Create a new Eloquent query builder instance.
  135. *
  136. * @param \Illuminate\Database\Query\Builder $query
  137. * @return void
  138. */
  139. public function __construct(QueryBuilder $query)
  140. {
  141. $this->query = $query;
  142. }
  143. /**
  144. * Create and return an un-saved model instance.
  145. *
  146. * @param array $attributes
  147. * @return \Illuminate\Database\Eloquent\Model|static
  148. */
  149. public function make(array $attributes = [])
  150. {
  151. return $this->newModelInstance($attributes);
  152. }
  153. /**
  154. * Register a new global scope.
  155. *
  156. * @param string $identifier
  157. * @param \Illuminate\Database\Eloquent\Scope|\Closure $scope
  158. * @return $this
  159. */
  160. public function withGlobalScope($identifier, $scope)
  161. {
  162. $this->scopes[$identifier] = $scope;
  163. if (method_exists($scope, 'extend')) {
  164. $scope->extend($this);
  165. }
  166. return $this;
  167. }
  168. /**
  169. * Remove a registered global scope.
  170. *
  171. * @param \Illuminate\Database\Eloquent\Scope|string $scope
  172. * @return $this
  173. */
  174. public function withoutGlobalScope($scope)
  175. {
  176. if (! is_string($scope)) {
  177. $scope = get_class($scope);
  178. }
  179. unset($this->scopes[$scope]);
  180. $this->removedScopes[] = $scope;
  181. return $this;
  182. }
  183. /**
  184. * Remove all or passed registered global scopes.
  185. *
  186. * @param array|null $scopes
  187. * @return $this
  188. */
  189. public function withoutGlobalScopes(?array $scopes = null)
  190. {
  191. if (! is_array($scopes)) {
  192. $scopes = array_keys($this->scopes);
  193. }
  194. foreach ($scopes as $scope) {
  195. $this->withoutGlobalScope($scope);
  196. }
  197. return $this;
  198. }
  199. /**
  200. * Get an array of global scopes that were removed from the query.
  201. *
  202. * @return array
  203. */
  204. public function removedScopes()
  205. {
  206. return $this->removedScopes;
  207. }
  208. /**
  209. * Add a where clause on the primary key to the query.
  210. *
  211. * @param mixed $id
  212. * @return $this
  213. */
  214. public function whereKey($id)
  215. {
  216. if ($id instanceof Model) {
  217. $id = $id->getKey();
  218. }
  219. if (is_array($id) || $id instanceof Arrayable) {
  220. if (in_array($this->model->getKeyType(), ['int', 'integer'])) {
  221. $this->query->whereIntegerInRaw($this->model->getQualifiedKeyName(), $id);
  222. } else {
  223. $this->query->whereIn($this->model->getQualifiedKeyName(), $id);
  224. }
  225. return $this;
  226. }
  227. if ($id !== null && $this->model->getKeyType() === 'string') {
  228. $id = (string) $id;
  229. }
  230. return $this->where($this->model->getQualifiedKeyName(), '=', $id);
  231. }
  232. /**
  233. * Add a where clause on the primary key to the query.
  234. *
  235. * @param mixed $id
  236. * @return $this
  237. */
  238. public function whereKeyNot($id)
  239. {
  240. if ($id instanceof Model) {
  241. $id = $id->getKey();
  242. }
  243. if (is_array($id) || $id instanceof Arrayable) {
  244. if (in_array($this->model->getKeyType(), ['int', 'integer'])) {
  245. $this->query->whereIntegerNotInRaw($this->model->getQualifiedKeyName(), $id);
  246. } else {
  247. $this->query->whereNotIn($this->model->getQualifiedKeyName(), $id);
  248. }
  249. return $this;
  250. }
  251. if ($id !== null && $this->model->getKeyType() === 'string') {
  252. $id = (string) $id;
  253. }
  254. return $this->where($this->model->getQualifiedKeyName(), '!=', $id);
  255. }
  256. /**
  257. * Add a basic where clause to the query.
  258. *
  259. * @param \Closure|string|array|\Illuminate\Contracts\Database\Query\Expression $column
  260. * @param mixed $operator
  261. * @param mixed $value
  262. * @param string $boolean
  263. * @return $this
  264. */
  265. public function where($column, $operator = null, $value = null, $boolean = 'and')
  266. {
  267. if ($column instanceof Closure && is_null($operator)) {
  268. $column($query = $this->model->newQueryWithoutRelationships());
  269. $this->query->addNestedWhereQuery($query->getQuery(), $boolean);
  270. } else {
  271. $this->query->where(...func_get_args());
  272. }
  273. return $this;
  274. }
  275. /**
  276. * Add a basic where clause to the query, and return the first result.
  277. *
  278. * @param \Closure|string|array|\Illuminate\Contracts\Database\Query\Expression $column
  279. * @param mixed $operator
  280. * @param mixed $value
  281. * @param string $boolean
  282. * @return \Illuminate\Database\Eloquent\Model|static|null
  283. */
  284. public function firstWhere($column, $operator = null, $value = null, $boolean = 'and')
  285. {
  286. return $this->where(...func_get_args())->first();
  287. }
  288. /**
  289. * Add an "or where" clause to the query.
  290. *
  291. * @param \Closure|array|string|\Illuminate\Contracts\Database\Query\Expression $column
  292. * @param mixed $operator
  293. * @param mixed $value
  294. * @return $this
  295. */
  296. public function orWhere($column, $operator = null, $value = null)
  297. {
  298. [$value, $operator] = $this->query->prepareValueAndOperator(
  299. $value, $operator, func_num_args() === 2
  300. );
  301. return $this->where($column, $operator, $value, 'or');
  302. }
  303. /**
  304. * Add a basic "where not" clause to the query.
  305. *
  306. * @param \Closure|string|array|\Illuminate\Contracts\Database\Query\Expression $column
  307. * @param mixed $operator
  308. * @param mixed $value
  309. * @param string $boolean
  310. * @return $this
  311. */
  312. public function whereNot($column, $operator = null, $value = null, $boolean = 'and')
  313. {
  314. return $this->where($column, $operator, $value, $boolean.' not');
  315. }
  316. /**
  317. * Add an "or where not" clause to the query.
  318. *
  319. * @param \Closure|array|string|\Illuminate\Contracts\Database\Query\Expression $column
  320. * @param mixed $operator
  321. * @param mixed $value
  322. * @return $this
  323. */
  324. public function orWhereNot($column, $operator = null, $value = null)
  325. {
  326. return $this->whereNot($column, $operator, $value, 'or');
  327. }
  328. /**
  329. * Add an "order by" clause for a timestamp to the query.
  330. *
  331. * @param string|\Illuminate\Contracts\Database\Query\Expression $column
  332. * @return $this
  333. */
  334. public function latest($column = null)
  335. {
  336. if (is_null($column)) {
  337. $column = $this->model->getCreatedAtColumn() ?? 'created_at';
  338. }
  339. $this->query->latest($column);
  340. return $this;
  341. }
  342. /**
  343. * Add an "order by" clause for a timestamp to the query.
  344. *
  345. * @param string|\Illuminate\Contracts\Database\Query\Expression $column
  346. * @return $this
  347. */
  348. public function oldest($column = null)
  349. {
  350. if (is_null($column)) {
  351. $column = $this->model->getCreatedAtColumn() ?? 'created_at';
  352. }
  353. $this->query->oldest($column);
  354. return $this;
  355. }
  356. /**
  357. * Create a collection of models from plain arrays.
  358. *
  359. * @param array $items
  360. * @return \Illuminate\Database\Eloquent\Collection
  361. */
  362. public function hydrate(array $items)
  363. {
  364. $instance = $this->newModelInstance();
  365. return $instance->newCollection(array_map(function ($item) use ($items, $instance) {
  366. $model = $instance->newFromBuilder($item);
  367. if (count($items) > 1) {
  368. $model->preventsLazyLoading = Model::preventsLazyLoading();
  369. }
  370. return $model;
  371. }, $items));
  372. }
  373. /**
  374. * Create a collection of models from a raw query.
  375. *
  376. * @param string $query
  377. * @param array $bindings
  378. * @return \Illuminate\Database\Eloquent\Collection
  379. */
  380. public function fromQuery($query, $bindings = [])
  381. {
  382. return $this->hydrate(
  383. $this->query->getConnection()->select($query, $bindings)
  384. );
  385. }
  386. /**
  387. * Find a model by its primary key.
  388. *
  389. * @param mixed $id
  390. * @param array|string $columns
  391. * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|null
  392. */
  393. public function find($id, $columns = ['*'])
  394. {
  395. if (is_array($id) || $id instanceof Arrayable) {
  396. return $this->findMany($id, $columns);
  397. }
  398. return $this->whereKey($id)->first($columns);
  399. }
  400. /**
  401. * Find multiple models by their primary keys.
  402. *
  403. * @param \Illuminate\Contracts\Support\Arrayable|array $ids
  404. * @param array|string $columns
  405. * @return \Illuminate\Database\Eloquent\Collection
  406. */
  407. public function findMany($ids, $columns = ['*'])
  408. {
  409. $ids = $ids instanceof Arrayable ? $ids->toArray() : $ids;
  410. if (empty($ids)) {
  411. return $this->model->newCollection();
  412. }
  413. return $this->whereKey($ids)->get($columns);
  414. }
  415. /**
  416. * Find a model by its primary key or throw an exception.
  417. *
  418. * @param mixed $id
  419. * @param array|string $columns
  420. * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static|static[]
  421. *
  422. * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
  423. */
  424. public function findOrFail($id, $columns = ['*'])
  425. {
  426. $result = $this->find($id, $columns);
  427. $id = $id instanceof Arrayable ? $id->toArray() : $id;
  428. if (is_array($id)) {
  429. if (count($result) !== count(array_unique($id))) {
  430. throw (new ModelNotFoundException)->setModel(
  431. get_class($this->model), array_diff($id, $result->modelKeys())
  432. );
  433. }
  434. return $result;
  435. }
  436. if (is_null($result)) {
  437. throw (new ModelNotFoundException)->setModel(
  438. get_class($this->model), $id
  439. );
  440. }
  441. return $result;
  442. }
  443. /**
  444. * Find a model by its primary key or return fresh model instance.
  445. *
  446. * @param mixed $id
  447. * @param array|string $columns
  448. * @return \Illuminate\Database\Eloquent\Model|static
  449. */
  450. public function findOrNew($id, $columns = ['*'])
  451. {
  452. if (! is_null($model = $this->find($id, $columns))) {
  453. return $model;
  454. }
  455. return $this->newModelInstance();
  456. }
  457. /**
  458. * Find a model by its primary key or call a callback.
  459. *
  460. * @param mixed $id
  461. * @param \Closure|array|string $columns
  462. * @param \Closure|null $callback
  463. * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|mixed
  464. */
  465. public function findOr($id, $columns = ['*'], ?Closure $callback = null)
  466. {
  467. if ($columns instanceof Closure) {
  468. $callback = $columns;
  469. $columns = ['*'];
  470. }
  471. if (! is_null($model = $this->find($id, $columns))) {
  472. return $model;
  473. }
  474. return $callback();
  475. }
  476. /**
  477. * Get the first record matching the attributes or instantiate it.
  478. *
  479. * @param array $attributes
  480. * @param array $values
  481. * @return \Illuminate\Database\Eloquent\Model|static
  482. */
  483. public function firstOrNew(array $attributes = [], array $values = [])
  484. {
  485. if (! is_null($instance = $this->where($attributes)->first())) {
  486. return $instance;
  487. }
  488. return $this->newModelInstance(array_merge($attributes, $values));
  489. }
  490. /**
  491. * Get the first record matching the attributes. If the record is not found, create it.
  492. *
  493. * @param array $attributes
  494. * @param array $values
  495. * @return \Illuminate\Database\Eloquent\Model|static
  496. */
  497. public function firstOrCreate(array $attributes = [], array $values = [])
  498. {
  499. if (! is_null($instance = (clone $this)->where($attributes)->first())) {
  500. return $instance;
  501. }
  502. return $this->createOrFirst($attributes, $values);
  503. }
  504. /**
  505. * Attempt to create the record. If a unique constraint violation occurs, attempt to find the matching record.
  506. *
  507. * @param array $attributes
  508. * @param array $values
  509. * @return \Illuminate\Database\Eloquent\Model|static
  510. */
  511. public function createOrFirst(array $attributes = [], array $values = [])
  512. {
  513. try {
  514. return $this->withSavepointIfNeeded(fn () => $this->create(array_merge($attributes, $values)));
  515. } catch (UniqueConstraintViolationException $e) {
  516. return $this->useWritePdo()->where($attributes)->first() ?? throw $e;
  517. }
  518. }
  519. /**
  520. * Create or update a record matching the attributes, and fill it with values.
  521. *
  522. * @param array $attributes
  523. * @param array $values
  524. * @return \Illuminate\Database\Eloquent\Model|static
  525. */
  526. public function updateOrCreate(array $attributes, array $values = [])
  527. {
  528. return tap($this->firstOrCreate($attributes, $values), function ($instance) use ($values) {
  529. if (! $instance->wasRecentlyCreated) {
  530. $instance->fill($values)->save();
  531. }
  532. });
  533. }
  534. /**
  535. * Execute the query and get the first result or throw an exception.
  536. *
  537. * @param array|string $columns
  538. * @return \Illuminate\Database\Eloquent\Model|static
  539. *
  540. * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
  541. */
  542. public function firstOrFail($columns = ['*'])
  543. {
  544. if (! is_null($model = $this->first($columns))) {
  545. return $model;
  546. }
  547. throw (new ModelNotFoundException)->setModel(get_class($this->model));
  548. }
  549. /**
  550. * Execute the query and get the first result or call a callback.
  551. *
  552. * @param \Closure|array|string $columns
  553. * @param \Closure|null $callback
  554. * @return \Illuminate\Database\Eloquent\Model|static|mixed
  555. */
  556. public function firstOr($columns = ['*'], ?Closure $callback = null)
  557. {
  558. if ($columns instanceof Closure) {
  559. $callback = $columns;
  560. $columns = ['*'];
  561. }
  562. if (! is_null($model = $this->first($columns))) {
  563. return $model;
  564. }
  565. return $callback();
  566. }
  567. /**
  568. * Execute the query and get the first result if it's the sole matching record.
  569. *
  570. * @param array|string $columns
  571. * @return \Illuminate\Database\Eloquent\Model
  572. *
  573. * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
  574. * @throws \Illuminate\Database\MultipleRecordsFoundException
  575. */
  576. public function sole($columns = ['*'])
  577. {
  578. try {
  579. return $this->baseSole($columns);
  580. } catch (RecordsNotFoundException) {
  581. throw (new ModelNotFoundException)->setModel(get_class($this->model));
  582. }
  583. }
  584. /**
  585. * Get a single column's value from the first result of a query.
  586. *
  587. * @param string|\Illuminate\Contracts\Database\Query\Expression $column
  588. * @return mixed
  589. */
  590. public function value($column)
  591. {
  592. if ($result = $this->first([$column])) {
  593. $column = $column instanceof Expression ? $column->getValue($this->getGrammar()) : $column;
  594. return $result->{Str::afterLast($column, '.')};
  595. }
  596. }
  597. /**
  598. * Get a single column's value from the first result of a query if it's the sole matching record.
  599. *
  600. * @param string|\Illuminate\Contracts\Database\Query\Expression $column
  601. * @return mixed
  602. *
  603. * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
  604. * @throws \Illuminate\Database\MultipleRecordsFoundException
  605. */
  606. public function soleValue($column)
  607. {
  608. $column = $column instanceof Expression ? $column->getValue($this->getGrammar()) : $column;
  609. return $this->sole([$column])->{Str::afterLast($column, '.')};
  610. }
  611. /**
  612. * Get a single column's value from the first result of the query or throw an exception.
  613. *
  614. * @param string|\Illuminate\Contracts\Database\Query\Expression $column
  615. * @return mixed
  616. *
  617. * @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
  618. */
  619. public function valueOrFail($column)
  620. {
  621. $column = $column instanceof Expression ? $column->getValue($this->getGrammar()) : $column;
  622. return $this->firstOrFail([$column])->{Str::afterLast($column, '.')};
  623. }
  624. /**
  625. * Execute the query as a "select" statement.
  626. *
  627. * @param array|string $columns
  628. * @return \Illuminate\Database\Eloquent\Collection|static[]
  629. */
  630. public function get($columns = ['*'])
  631. {
  632. $builder = $this->applyScopes();
  633. // If we actually found models we will also eager load any relationships that
  634. // have been specified as needing to be eager loaded, which will solve the
  635. // n+1 query issue for the developers to avoid running a lot of queries.
  636. if (count($models = $builder->getModels($columns)) > 0) {
  637. $models = $builder->eagerLoadRelations($models);
  638. }
  639. return $this->applyAfterQueryCallbacks(
  640. $builder->getModel()->newCollection($models)
  641. );
  642. }
  643. /**
  644. * Get the hydrated models without eager loading.
  645. *
  646. * @param array|string $columns
  647. * @return \Illuminate\Database\Eloquent\Model[]|static[]
  648. */
  649. public function getModels($columns = ['*'])
  650. {
  651. return $this->model->hydrate(
  652. $this->query->get($columns)->all()
  653. )->all();
  654. }
  655. /**
  656. * Eager load the relationships for the models.
  657. *
  658. * @param array $models
  659. * @return array
  660. */
  661. public function eagerLoadRelations(array $models)
  662. {
  663. foreach ($this->eagerLoad as $name => $constraints) {
  664. // For nested eager loads we'll skip loading them here and they will be set as an
  665. // eager load on the query to retrieve the relation so that they will be eager
  666. // loaded on that query, because that is where they get hydrated as models.
  667. if (! str_contains($name, '.')) {
  668. $models = $this->eagerLoadRelation($models, $name, $constraints);
  669. }
  670. }
  671. return $models;
  672. }
  673. /**
  674. * Eagerly load the relationship on a set of models.
  675. *
  676. * @param array $models
  677. * @param string $name
  678. * @param \Closure $constraints
  679. * @return array
  680. */
  681. protected function eagerLoadRelation(array $models, $name, Closure $constraints)
  682. {
  683. // First we will "back up" the existing where conditions on the query so we can
  684. // add our eager constraints. Then we will merge the wheres that were on the
  685. // query back to it in order that any where conditions might be specified.
  686. $relation = $this->getRelation($name);
  687. $relation->addEagerConstraints($models);
  688. $constraints($relation);
  689. // Once we have the results, we just match those back up to their parent models
  690. // using the relationship instance. Then we just return the finished arrays
  691. // of models which have been eagerly hydrated and are readied for return.
  692. return $relation->match(
  693. $relation->initRelation($models, $name),
  694. $relation->getEager(), $name
  695. );
  696. }
  697. /**
  698. * Get the relation instance for the given relation name.
  699. *
  700. * @param string $name
  701. * @return \Illuminate\Database\Eloquent\Relations\Relation
  702. */
  703. public function getRelation($name)
  704. {
  705. // We want to run a relationship query without any constrains so that we will
  706. // not have to remove these where clauses manually which gets really hacky
  707. // and error prone. We don't want constraints because we add eager ones.
  708. $relation = Relation::noConstraints(function () use ($name) {
  709. try {
  710. return $this->getModel()->newInstance()->$name();
  711. } catch (BadMethodCallException) {
  712. throw RelationNotFoundException::make($this->getModel(), $name);
  713. }
  714. });
  715. $nested = $this->relationsNestedUnder($name);
  716. // If there are nested relationships set on the query, we will put those onto
  717. // the query instances so that they can be handled after this relationship
  718. // is loaded. In this way they will all trickle down as they are loaded.
  719. if (count($nested) > 0) {
  720. $relation->getQuery()->with($nested);
  721. }
  722. return $relation;
  723. }
  724. /**
  725. * Get the deeply nested relations for a given top-level relation.
  726. *
  727. * @param string $relation
  728. * @return array
  729. */
  730. protected function relationsNestedUnder($relation)
  731. {
  732. $nested = [];
  733. // We are basically looking for any relationships that are nested deeper than
  734. // the given top-level relationship. We will just check for any relations
  735. // that start with the given top relations and adds them to our arrays.
  736. foreach ($this->eagerLoad as $name => $constraints) {
  737. if ($this->isNestedUnder($relation, $name)) {
  738. $nested[substr($name, strlen($relation.'.'))] = $constraints;
  739. }
  740. }
  741. return $nested;
  742. }
  743. /**
  744. * Determine if the relationship is nested.
  745. *
  746. * @param string $relation
  747. * @param string $name
  748. * @return bool
  749. */
  750. protected function isNestedUnder($relation, $name)
  751. {
  752. return str_contains($name, '.') && str_starts_with($name, $relation.'.');
  753. }
  754. /**
  755. * Register a closure to be invoked after the query is executed.
  756. *
  757. * @param \Closure $callback
  758. * @return $this
  759. */
  760. public function afterQuery(Closure $callback)
  761. {
  762. $this->afterQueryCallbacks[] = $callback;
  763. return $this;
  764. }
  765. /**
  766. * Invoke the "after query" modification callbacks.
  767. *
  768. * @param mixed $result
  769. * @return mixed
  770. */
  771. public function applyAfterQueryCallbacks($result)
  772. {
  773. foreach ($this->afterQueryCallbacks as $afterQueryCallback) {
  774. $result = $afterQueryCallback($result) ?: $result;
  775. }
  776. return $result;
  777. }
  778. /**
  779. * Get a lazy collection for the given query.
  780. *
  781. * @return \Illuminate\Support\LazyCollection
  782. */
  783. public function cursor()
  784. {
  785. return $this->applyScopes()->query->cursor()->map(function ($record) {
  786. $model = $this->newModelInstance()->newFromBuilder($record);
  787. return $this->applyAfterQueryCallbacks($this->newModelInstance()->newCollection([$model]))->first();
  788. })->reject(fn ($model) => is_null($model));
  789. }
  790. /**
  791. * Add a generic "order by" clause if the query doesn't already have one.
  792. *
  793. * @return void
  794. */
  795. protected function enforceOrderBy()
  796. {
  797. if (empty($this->query->orders) && empty($this->query->unionOrders)) {
  798. $this->orderBy($this->model->getQualifiedKeyName(), 'asc');
  799. }
  800. }
  801. /**
  802. * Get a collection with the values of a given column.
  803. *
  804. * @param string|\Illuminate\Contracts\Database\Query\Expression $column
  805. * @param string|null $key
  806. * @return \Illuminate\Support\Collection
  807. */
  808. public function pluck($column, $key = null)
  809. {
  810. $results = $this->toBase()->pluck($column, $key);
  811. $column = $column instanceof Expression ? $column->getValue($this->getGrammar()) : $column;
  812. $column = Str::after($column, "{$this->model->getTable()}.");
  813. // If the model has a mutator for the requested column, we will spin through
  814. // the results and mutate the values so that the mutated version of these
  815. // columns are returned as you would expect from these Eloquent models.
  816. if (! $this->model->hasGetMutator($column) &&
  817. ! $this->model->hasCast($column) &&
  818. ! in_array($column, $this->model->getDates())) {
  819. return $results;
  820. }
  821. return $this->applyAfterQueryCallbacks(
  822. $results->map(function ($value) use ($column) {
  823. return $this->model->newFromBuilder([$column => $value])->{$column};
  824. })
  825. );
  826. }
  827. /**
  828. * Paginate the given query.
  829. *
  830. * @param int|null|\Closure $perPage
  831. * @param array|string $columns
  832. * @param string $pageName
  833. * @param int|null $page
  834. * @param \Closure|int|null $total
  835. * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
  836. *
  837. * @throws \InvalidArgumentException
  838. */
  839. public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null, $total = null)
  840. {
  841. $page = $page ?: Paginator::resolveCurrentPage($pageName);
  842. $total = value($total) ?? $this->toBase()->getCountForPagination();
  843. $perPage = ($perPage instanceof Closure
  844. ? $perPage($total)
  845. : $perPage
  846. ) ?: $this->model->getPerPage();
  847. $results = $total
  848. ? $this->forPage($page, $perPage)->get($columns)
  849. : $this->model->newCollection();
  850. return $this->paginator($results, $total, $perPage, $page, [
  851. 'path' => Paginator::resolveCurrentPath(),
  852. 'pageName' => $pageName,
  853. ]);
  854. }
  855. /**
  856. * Paginate the given query into a simple paginator.
  857. *
  858. * @param int|null $perPage
  859. * @param array|string $columns
  860. * @param string $pageName
  861. * @param int|null $page
  862. * @return \Illuminate\Contracts\Pagination\Paginator
  863. */
  864. public function simplePaginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
  865. {
  866. $page = $page ?: Paginator::resolveCurrentPage($pageName);
  867. $perPage = $perPage ?: $this->model->getPerPage();
  868. // Next we will set the limit and offset for this query so that when we get the
  869. // results we get the proper section of results. Then, we'll create the full
  870. // paginator instances for these results with the given page and per page.
  871. $this->skip(($page - 1) * $perPage)->take($perPage + 1);
  872. return $this->simplePaginator($this->get($columns), $perPage, $page, [
  873. 'path' => Paginator::resolveCurrentPath(),
  874. 'pageName' => $pageName,
  875. ]);
  876. }
  877. /**
  878. * Paginate the given query into a cursor paginator.
  879. *
  880. * @param int|null $perPage
  881. * @param array|string $columns
  882. * @param string $cursorName
  883. * @param \Illuminate\Pagination\Cursor|string|null $cursor
  884. * @return \Illuminate\Contracts\Pagination\CursorPaginator
  885. */
  886. public function cursorPaginate($perPage = null, $columns = ['*'], $cursorName = 'cursor', $cursor = null)
  887. {
  888. $perPage = $perPage ?: $this->model->getPerPage();
  889. return $this->paginateUsingCursor($perPage, $columns, $cursorName, $cursor);
  890. }
  891. /**
  892. * Ensure the proper order by required for cursor pagination.
  893. *
  894. * @param bool $shouldReverse
  895. * @return \Illuminate\Support\Collection
  896. */
  897. protected function ensureOrderForCursorPagination($shouldReverse = false)
  898. {
  899. if (empty($this->query->orders) && empty($this->query->unionOrders)) {
  900. $this->enforceOrderBy();
  901. }
  902. $reverseDirection = function ($order) {
  903. if (! isset($order['direction'])) {
  904. return $order;
  905. }
  906. $order['direction'] = $order['direction'] === 'asc' ? 'desc' : 'asc';
  907. return $order;
  908. };
  909. if ($shouldReverse) {
  910. $this->query->orders = collect($this->query->orders)->map($reverseDirection)->toArray();
  911. $this->query->unionOrders = collect($this->query->unionOrders)->map($reverseDirection)->toArray();
  912. }
  913. $orders = ! empty($this->query->unionOrders) ? $this->query->unionOrders : $this->query->orders;
  914. return collect($orders)
  915. ->filter(fn ($order) => Arr::has($order, 'direction'))
  916. ->values();
  917. }
  918. /**
  919. * Save a new model and return the instance.
  920. *
  921. * @param array $attributes
  922. * @return \Illuminate\Database\Eloquent\Model|$this
  923. */
  924. public function create(array $attributes = [])
  925. {
  926. return tap($this->newModelInstance($attributes), function ($instance) {
  927. $instance->save();
  928. });
  929. }
  930. /**
  931. * Save a new model and return the instance. Allow mass-assignment.
  932. *
  933. * @param array $attributes
  934. * @return \Illuminate\Database\Eloquent\Model|$this
  935. */
  936. public function forceCreate(array $attributes)
  937. {
  938. return $this->model->unguarded(function () use ($attributes) {
  939. return $this->newModelInstance()->create($attributes);
  940. });
  941. }
  942. /**
  943. * Save a new model instance with mass assignment without raising model events.
  944. *
  945. * @param array $attributes
  946. * @return \Illuminate\Database\Eloquent\Model|$this
  947. */
  948. public function forceCreateQuietly(array $attributes = [])
  949. {
  950. return Model::withoutEvents(fn () => $this->forceCreate($attributes));
  951. }
  952. /**
  953. * Update records in the database.
  954. *
  955. * @param array $values
  956. * @return int
  957. */
  958. public function update(array $values)
  959. {
  960. return $this->toBase()->update($this->addUpdatedAtColumn($values));
  961. }
  962. /**
  963. * Insert new records or update the existing ones.
  964. *
  965. * @param array $values
  966. * @param array|string $uniqueBy
  967. * @param array|null $update
  968. * @return int
  969. */
  970. public function upsert(array $values, $uniqueBy, $update = null)
  971. {
  972. if (empty($values)) {
  973. return 0;
  974. }
  975. if (! is_array(reset($values))) {
  976. $values = [$values];
  977. }
  978. if (is_null($update)) {
  979. $update = array_keys(reset($values));
  980. }
  981. return $this->toBase()->upsert(
  982. $this->addTimestampsToUpsertValues($this->addUniqueIdsToUpsertValues($values)),
  983. $uniqueBy,
  984. $this->addUpdatedAtToUpsertColumns($update)
  985. );
  986. }
  987. /**
  988. * Update the column's update timestamp.
  989. *
  990. * @param string|null $column
  991. * @return int|false
  992. */
  993. public function touch($column = null)
  994. {
  995. $time = $this->model->freshTimestamp();
  996. if ($column) {
  997. return $this->toBase()->update([$column => $time]);
  998. }
  999. $column = $this->model->getUpdatedAtColumn();
  1000. if (! $this->model->usesTimestamps() || is_null($column)) {
  1001. return false;
  1002. }
  1003. return $this->toBase()->update([$column => $time]);
  1004. }
  1005. /**
  1006. * Increment a column's value by a given amount.
  1007. *
  1008. * @param string|\Illuminate\Contracts\Database\Query\Expression $column
  1009. * @param float|int $amount
  1010. * @param array $extra
  1011. * @return int
  1012. */
  1013. public function increment($column, $amount = 1, array $extra = [])
  1014. {
  1015. return $this->toBase()->increment(
  1016. $column, $amount, $this->addUpdatedAtColumn($extra)
  1017. );
  1018. }
  1019. /**
  1020. * Decrement a column's value by a given amount.
  1021. *
  1022. * @param string|\Illuminate\Contracts\Database\Query\Expression $column
  1023. * @param float|int $amount
  1024. * @param array $extra
  1025. * @return int
  1026. */
  1027. public function decrement($column, $amount = 1, array $extra = [])
  1028. {
  1029. return $this->toBase()->decrement(
  1030. $column, $amount, $this->addUpdatedAtColumn($extra)
  1031. );
  1032. }
  1033. /**
  1034. * Add the "updated at" column to an array of values.
  1035. *
  1036. * @param array $values
  1037. * @return array
  1038. */
  1039. protected function addUpdatedAtColumn(array $values)
  1040. {
  1041. if (! $this->model->usesTimestamps() ||
  1042. is_null($this->model->getUpdatedAtColumn())) {
  1043. return $values;
  1044. }
  1045. $column = $this->model->getUpdatedAtColumn();
  1046. if (! array_key_exists($column, $values)) {
  1047. $timestamp = $this->model->freshTimestampString();
  1048. if (
  1049. $this->model->hasSetMutator($column)
  1050. || $this->model->hasAttributeSetMutator($column)
  1051. || $this->model->hasCast($column)
  1052. ) {
  1053. $timestamp = $this->model->newInstance()
  1054. ->forceFill([$column => $timestamp])
  1055. ->getAttributes()[$column] ?? $timestamp;
  1056. }
  1057. $values = array_merge([$column => $timestamp], $values);
  1058. }
  1059. $segments = preg_split('/\s+as\s+/i', $this->query->from);
  1060. $qualifiedColumn = end($segments).'.'.$column;
  1061. $values[$qualifiedColumn] = Arr::get($values, $qualifiedColumn, $values[$column]);
  1062. unset($values[$column]);
  1063. return $values;
  1064. }
  1065. /**
  1066. * Add unique IDs to the inserted values.
  1067. *
  1068. * @param array $values
  1069. * @return array
  1070. */
  1071. protected function addUniqueIdsToUpsertValues(array $values)
  1072. {
  1073. if (! $this->model->usesUniqueIds()) {
  1074. return $values;
  1075. }
  1076. foreach ($this->model->uniqueIds() as $uniqueIdAttribute) {
  1077. foreach ($values as &$row) {
  1078. if (! array_key_exists($uniqueIdAttribute, $row)) {
  1079. $row = array_merge([$uniqueIdAttribute => $this->model->newUniqueId()], $row);
  1080. }
  1081. }
  1082. }
  1083. return $values;
  1084. }
  1085. /**
  1086. * Add timestamps to the inserted values.
  1087. *
  1088. * @param array $values
  1089. * @return array
  1090. */
  1091. protected function addTimestampsToUpsertValues(array $values)
  1092. {
  1093. if (! $this->model->usesTimestamps()) {
  1094. return $values;
  1095. }
  1096. $timestamp = $this->model->freshTimestampString();
  1097. $columns = array_filter([
  1098. $this->model->getCreatedAtColumn(),
  1099. $this->model->getUpdatedAtColumn(),
  1100. ]);
  1101. foreach ($columns as $column) {
  1102. foreach ($values as &$row) {
  1103. $row = array_merge([$column => $timestamp], $row);
  1104. }
  1105. }
  1106. return $values;
  1107. }
  1108. /**
  1109. * Add the "updated at" column to the updated columns.
  1110. *
  1111. * @param array $update
  1112. * @return array
  1113. */
  1114. protected function addUpdatedAtToUpsertColumns(array $update)
  1115. {
  1116. if (! $this->model->usesTimestamps()) {
  1117. return $update;
  1118. }
  1119. $column = $this->model->getUpdatedAtColumn();
  1120. if (! is_null($column) &&
  1121. ! array_key_exists($column, $update) &&
  1122. ! in_array($column, $update)) {
  1123. $update[] = $column;
  1124. }
  1125. return $update;
  1126. }
  1127. /**
  1128. * Delete records from the database.
  1129. *
  1130. * @return mixed
  1131. */
  1132. public function delete()
  1133. {
  1134. if (isset($this->onDelete)) {
  1135. return call_user_func($this->onDelete, $this);
  1136. }
  1137. return $this->toBase()->delete();
  1138. }
  1139. /**
  1140. * Run the default delete function on the builder.
  1141. *
  1142. * Since we do not apply scopes here, the row will actually be deleted.
  1143. *
  1144. * @return mixed
  1145. */
  1146. public function forceDelete()
  1147. {
  1148. return $this->query->delete();
  1149. }
  1150. /**
  1151. * Register a replacement for the default delete function.
  1152. *
  1153. * @param \Closure $callback
  1154. * @return void
  1155. */
  1156. public function onDelete(Closure $callback)
  1157. {
  1158. $this->onDelete = $callback;
  1159. }
  1160. /**
  1161. * Determine if the given model has a scope.
  1162. *
  1163. * @param string $scope
  1164. * @return bool
  1165. */
  1166. public function hasNamedScope($scope)
  1167. {
  1168. return $this->model && $this->model->hasNamedScope($scope);
  1169. }
  1170. /**
  1171. * Call the given local model scopes.
  1172. *
  1173. * @param array|string $scopes
  1174. * @return static|mixed
  1175. */
  1176. public function scopes($scopes)
  1177. {
  1178. $builder = $this;
  1179. foreach (Arr::wrap($scopes) as $scope => $parameters) {
  1180. // If the scope key is an integer, then the scope was passed as the value and
  1181. // the parameter list is empty, so we will format the scope name and these
  1182. // parameters here. Then, we'll be ready to call the scope on the model.
  1183. if (is_int($scope)) {
  1184. [$scope, $parameters] = [$parameters, []];
  1185. }
  1186. // Next we'll pass the scope callback to the callScope method which will take
  1187. // care of grouping the "wheres" properly so the logical order doesn't get
  1188. // messed up when adding scopes. Then we'll return back out the builder.
  1189. $builder = $builder->callNamedScope(
  1190. $scope, Arr::wrap($parameters)
  1191. );
  1192. }
  1193. return $builder;
  1194. }
  1195. /**
  1196. * Apply the scopes to the Eloquent builder instance and return it.
  1197. *
  1198. * @return static
  1199. */
  1200. public function applyScopes()
  1201. {
  1202. if (! $this->scopes) {
  1203. return $this;
  1204. }
  1205. $builder = clone $this;
  1206. foreach ($this->scopes as $identifier => $scope) {
  1207. if (! isset($builder->scopes[$identifier])) {
  1208. continue;
  1209. }
  1210. $builder->callScope(function (self $builder) use ($scope) {
  1211. // If the scope is a Closure we will just go ahead and call the scope with the
  1212. // builder instance. The "callScope" method will properly group the clauses
  1213. // that are added to this query so "where" clauses maintain proper logic.
  1214. if ($scope instanceof Closure) {
  1215. $scope($builder);
  1216. }
  1217. // If the scope is a scope object, we will call the apply method on this scope
  1218. // passing in the builder and the model instance. After we run all of these
  1219. // scopes we will return back the builder instance to the outside caller.
  1220. if ($scope instanceof Scope) {
  1221. $scope->apply($builder, $this->getModel());
  1222. }
  1223. });
  1224. }
  1225. return $builder;
  1226. }
  1227. /**
  1228. * Apply the given scope on the current builder instance.
  1229. *
  1230. * @param callable $scope
  1231. * @param array $parameters
  1232. * @return mixed
  1233. */
  1234. protected function callScope(callable $scope, array $parameters = [])
  1235. {
  1236. array_unshift($parameters, $this);
  1237. $query = $this->getQuery();
  1238. // We will keep track of how many wheres are on the query before running the
  1239. // scope so that we can properly group the added scope constraints in the
  1240. // query as their own isolated nested where statement and avoid issues.
  1241. $originalWhereCount = is_null($query->wheres)
  1242. ? 0 : count($query->wheres);
  1243. $result = $scope(...$parameters) ?? $this;
  1244. if (count((array) $query->wheres) > $originalWhereCount) {
  1245. $this->addNewWheresWithinGroup($query, $originalWhereCount);
  1246. }
  1247. return $result;
  1248. }
  1249. /**
  1250. * Apply the given named scope on the current builder instance.
  1251. *
  1252. * @param string $scope
  1253. * @param array $parameters
  1254. * @return mixed
  1255. */
  1256. protected function callNamedScope($scope, array $parameters = [])
  1257. {
  1258. return $this->callScope(function (...$parameters) use ($scope) {
  1259. return $this->model->callNamedScope($scope, $parameters);
  1260. }, $parameters);
  1261. }
  1262. /**
  1263. * Nest where conditions by slicing them at the given where count.
  1264. *
  1265. * @param \Illuminate\Database\Query\Builder $query
  1266. * @param int $originalWhereCount
  1267. * @return void
  1268. */
  1269. protected function addNewWheresWithinGroup(QueryBuilder $query, $originalWhereCount)
  1270. {
  1271. // Here, we totally remove all of the where clauses since we are going to
  1272. // rebuild them as nested queries by slicing the groups of wheres into
  1273. // their own sections. This is to prevent any confusing logic order.
  1274. $allWheres = $query->wheres;
  1275. $query->wheres = [];
  1276. $this->groupWhereSliceForScope(
  1277. $query, array_slice($allWheres, 0, $originalWhereCount)
  1278. );
  1279. $this->groupWhereSliceForScope(
  1280. $query, array_slice($allWheres, $originalWhereCount)
  1281. );
  1282. }
  1283. /**
  1284. * Slice where conditions at the given offset and add them to the query as a nested condition.
  1285. *
  1286. * @param \Illuminate\Database\Query\Builder $query
  1287. * @param array $whereSlice
  1288. * @return void
  1289. */
  1290. protected function groupWhereSliceForScope(QueryBuilder $query, $whereSlice)
  1291. {
  1292. $whereBooleans = collect($whereSlice)->pluck('boolean');
  1293. // Here we'll check if the given subset of where clauses contains any "or"
  1294. // booleans and in this case create a nested where expression. That way
  1295. // we don't add any unnecessary nesting thus keeping the query clean.
  1296. if ($whereBooleans->contains(fn ($logicalOperator) => str_contains($logicalOperator, 'or'))) {
  1297. $query->wheres[] = $this->createNestedWhere(
  1298. $whereSlice, str_replace(' not', '', $whereBooleans->first())
  1299. );
  1300. } else {
  1301. $query->wheres = array_merge($query->wheres, $whereSlice);
  1302. }
  1303. }
  1304. /**
  1305. * Create a where array with nested where conditions.
  1306. *
  1307. * @param array $whereSlice
  1308. * @param string $boolean
  1309. * @return array
  1310. */
  1311. protected function createNestedWhere($whereSlice, $boolean = 'and')
  1312. {
  1313. $whereGroup = $this->getQuery()->forNestedWhere();
  1314. $whereGroup->wheres = $whereSlice;
  1315. return ['type' => 'Nested', 'query' => $whereGroup, 'boolean' => $boolean];
  1316. }
  1317. /**
  1318. * Set the relationships that should be eager loaded.
  1319. *
  1320. * @param string|array $relations
  1321. * @param string|\Closure|null $callback
  1322. * @return $this
  1323. */
  1324. public function with($relations, $callback = null)
  1325. {
  1326. if ($callback instanceof Closure) {
  1327. $eagerLoad = $this->parseWithRelations([$relations => $callback]);
  1328. } else {
  1329. $eagerLoad = $this->parseWithRelations(is_string($relations) ? func_get_args() : $relations);
  1330. }
  1331. $this->eagerLoad = array_merge($this->eagerLoad, $eagerLoad);
  1332. return $this;
  1333. }
  1334. /**
  1335. * Prevent the specified relations from being eager loaded.
  1336. *
  1337. * @param mixed $relations
  1338. * @return $this
  1339. */
  1340. public function without($relations)
  1341. {
  1342. $this->eagerLoad = array_diff_key($this->eagerLoad, array_flip(
  1343. is_string($relations) ? func_get_args() : $relations
  1344. ));
  1345. return $this;
  1346. }
  1347. /**
  1348. * Set the relationships that should be eager loaded while removing any previously added eager loading specifications.
  1349. *
  1350. * @param mixed $relations
  1351. * @return $this
  1352. */
  1353. public function withOnly($relations)
  1354. {
  1355. $this->eagerLoad = [];
  1356. return $this->with($relations);
  1357. }
  1358. /**
  1359. * Create a new instance of the model being queried.
  1360. *
  1361. * @param array $attributes
  1362. * @return \Illuminate\Database\Eloquent\Model|static
  1363. */
  1364. public function newModelInstance($attributes = [])
  1365. {
  1366. return $this->model->newInstance($attributes)->setConnection(
  1367. $this->query->getConnection()->getName()
  1368. );
  1369. }
  1370. /**
  1371. * Parse a list of relations into individuals.
  1372. *
  1373. * @param array $relations
  1374. * @return array
  1375. */
  1376. protected function parseWithRelations(array $relations)
  1377. {
  1378. if ($relations === []) {
  1379. return [];
  1380. }
  1381. $results = [];
  1382. foreach ($this->prepareNestedWithRelationships($relations) as $name => $constraints) {
  1383. // We need to separate out any nested includes, which allows the developers
  1384. // to load deep relationships using "dots" without stating each level of
  1385. // the relationship with its own key in the array of eager-load names.
  1386. $results = $this->addNestedWiths($name, $results);
  1387. $results[$name] = $constraints;
  1388. }
  1389. return $results;
  1390. }
  1391. /**
  1392. * Prepare nested with relationships.
  1393. *
  1394. * @param array $relations
  1395. * @param string $prefix
  1396. * @return array
  1397. */
  1398. protected function prepareNestedWithRelationships($relations, $prefix = '')
  1399. {
  1400. $preparedRelationships = [];
  1401. if ($prefix !== '') {
  1402. $prefix .= '.';
  1403. }
  1404. // If any of the relationships are formatted with the [$attribute => array()]
  1405. // syntax, we shall loop over the nested relations and prepend each key of
  1406. // this array while flattening into the traditional dot notation format.
  1407. foreach ($relations as $key => $value) {
  1408. if (! is_string($key) || ! is_array($value)) {
  1409. continue;
  1410. }
  1411. [$attribute, $attributeSelectConstraint] = $this->parseNameAndAttributeSelectionConstraint($key);
  1412. $preparedRelationships = array_merge(
  1413. $preparedRelationships,
  1414. ["{$prefix}{$attribute}" => $attributeSelectConstraint],
  1415. $this->prepareNestedWithRelationships($value, "{$prefix}{$attribute}"),
  1416. );
  1417. unset($relations[$key]);
  1418. }
  1419. // We now know that the remaining relationships are in a dot notation format
  1420. // and may be a string or Closure. We'll loop over them and ensure all of
  1421. // the present Closures are merged + strings are made into constraints.
  1422. foreach ($relations as $key => $value) {
  1423. if (is_numeric($key) && is_string($value)) {
  1424. [$key, $value] = $this->parseNameAndAttributeSelectionConstraint($value);
  1425. }
  1426. $preparedRelationships[$prefix.$key] = $this->combineConstraints([
  1427. $value,
  1428. $preparedRelationships[$prefix.$key] ?? static function () {
  1429. //
  1430. },
  1431. ]);
  1432. }
  1433. return $preparedRelationships;
  1434. }
  1435. /**
  1436. * Combine an array of constraints into a single constraint.
  1437. *
  1438. * @param array $constraints
  1439. * @return \Closure
  1440. */
  1441. protected function combineConstraints(array $constraints)
  1442. {
  1443. return function ($builder) use ($constraints) {
  1444. foreach ($constraints as $constraint) {
  1445. $builder = $constraint($builder) ?? $builder;
  1446. }
  1447. return $builder;
  1448. };
  1449. }
  1450. /**
  1451. * Parse the attribute select constraints from the name.
  1452. *
  1453. * @param string $name
  1454. * @return array
  1455. */
  1456. protected function parseNameAndAttributeSelectionConstraint($name)
  1457. {
  1458. return str_contains($name, ':')
  1459. ? $this->createSelectWithConstraint($name)
  1460. : [$name, static function () {
  1461. //
  1462. }];
  1463. }
  1464. /**
  1465. * Create a constraint to select the given columns for the relation.
  1466. *
  1467. * @param string $name
  1468. * @return array
  1469. */
  1470. protected function createSelectWithConstraint($name)
  1471. {
  1472. return [explode(':', $name)[0], static function ($query) use ($name) {
  1473. $query->select(array_map(static function ($column) use ($query) {
  1474. if (str_contains($column, '.')) {
  1475. return $column;
  1476. }
  1477. return $query instanceof BelongsToMany
  1478. ? $query->getRelated()->getTable().'.'.$column
  1479. : $column;
  1480. }, explode(',', explode(':', $name)[1])));
  1481. }];
  1482. }
  1483. /**
  1484. * Parse the nested relationships in a relation.
  1485. *
  1486. * @param string $name
  1487. * @param array $results
  1488. * @return array
  1489. */
  1490. protected function addNestedWiths($name, $results)
  1491. {
  1492. $progress = [];
  1493. // If the relation has already been set on the result array, we will not set it
  1494. // again, since that would override any constraints that were already placed
  1495. // on the relationships. We will only set the ones that are not specified.
  1496. foreach (explode('.', $name) as $segment) {
  1497. $progress[] = $segment;
  1498. if (! isset($results[$last = implode('.', $progress)])) {
  1499. $results[$last] = static function () {
  1500. //
  1501. };
  1502. }
  1503. }
  1504. return $results;
  1505. }
  1506. /**
  1507. * Apply query-time casts to the model instance.
  1508. *
  1509. * @param array $casts
  1510. * @return $this
  1511. */
  1512. public function withCasts($casts)
  1513. {
  1514. $this->model->mergeCasts($casts);
  1515. return $this;
  1516. }
  1517. /**
  1518. * Execute the given Closure within a transaction savepoint if needed.
  1519. *
  1520. * @template TModelValue
  1521. *
  1522. * @param \Closure(): TModelValue $scope
  1523. * @return TModelValue
  1524. */
  1525. public function withSavepointIfNeeded(Closure $scope): mixed
  1526. {
  1527. return $this->getQuery()->getConnection()->transactionLevel() > 0
  1528. ? $this->getQuery()->getConnection()->transaction($scope)
  1529. : $scope();
  1530. }
  1531. /**
  1532. * Get the Eloquent builder instances that are used in the union of the query.
  1533. *
  1534. * @return \Illuminate\Support\Collection
  1535. */
  1536. protected function getUnionBuilders()
  1537. {
  1538. return isset($this->query->unions)
  1539. ? collect($this->query->unions)->pluck('query')
  1540. : collect();
  1541. }
  1542. /**
  1543. * Get the underlying query builder instance.
  1544. *
  1545. * @return \Illuminate\Database\Query\Builder
  1546. */
  1547. public function getQuery()
  1548. {
  1549. return $this->query;
  1550. }
  1551. /**
  1552. * Set the underlying query builder instance.
  1553. *
  1554. * @param \Illuminate\Database\Query\Builder $query
  1555. * @return $this
  1556. */
  1557. public function setQuery($query)
  1558. {
  1559. $this->query = $query;
  1560. return $this;
  1561. }
  1562. /**
  1563. * Get a base query builder instance.
  1564. *
  1565. * @return \Illuminate\Database\Query\Builder
  1566. */
  1567. public function toBase()
  1568. {
  1569. return $this->applyScopes()->getQuery();
  1570. }
  1571. /**
  1572. * Get the relationships being eagerly loaded.
  1573. *
  1574. * @return array
  1575. */
  1576. public function getEagerLoads()
  1577. {
  1578. return $this->eagerLoad;
  1579. }
  1580. /**
  1581. * Set the relationships being eagerly loaded.
  1582. *
  1583. * @param array $eagerLoad
  1584. * @return $this
  1585. */
  1586. public function setEagerLoads(array $eagerLoad)
  1587. {
  1588. $this->eagerLoad = $eagerLoad;
  1589. return $this;
  1590. }
  1591. /**
  1592. * Indicate that the given relationships should not be eagerly loaded.
  1593. *
  1594. * @param array $relations
  1595. * @return $this
  1596. */
  1597. public function withoutEagerLoad(array $relations)
  1598. {
  1599. $relations = array_diff(array_keys($this->model->getRelations()), $relations);
  1600. return $this->with($relations);
  1601. }
  1602. /**
  1603. * Flush the relationships being eagerly loaded.
  1604. *
  1605. * @return $this
  1606. */
  1607. public function withoutEagerLoads()
  1608. {
  1609. return $this->setEagerLoads([]);
  1610. }
  1611. /**
  1612. * Get the default key name of the table.
  1613. *
  1614. * @return string
  1615. */
  1616. protected function defaultKeyName()
  1617. {
  1618. return $this->getModel()->getKeyName();
  1619. }
  1620. /**
  1621. * Get the model instance being queried.
  1622. *
  1623. * @return \Illuminate\Database\Eloquent\Model|static
  1624. */
  1625. public function getModel()
  1626. {
  1627. return $this->model;
  1628. }
  1629. /**
  1630. * Set a model instance for the model being queried.
  1631. *
  1632. * @param \Illuminate\Database\Eloquent\Model $model
  1633. * @return $this
  1634. */
  1635. public function setModel(Model $model)
  1636. {
  1637. $this->model = $model;
  1638. $this->query->from($model->getTable());
  1639. return $this;
  1640. }
  1641. /**
  1642. * Qualify the given column name by the model's table.
  1643. *
  1644. * @param string|\Illuminate\Contracts\Database\Query\Expression $column
  1645. * @return string
  1646. */
  1647. public function qualifyColumn($column)
  1648. {
  1649. $column = $column instanceof Expression ? $column->getValue($this->getGrammar()) : $column;
  1650. return $this->model->qualifyColumn($column);
  1651. }
  1652. /**
  1653. * Qualify the given columns with the model's table.
  1654. *
  1655. * @param array|\Illuminate\Contracts\Database\Query\Expression $columns
  1656. * @return array
  1657. */
  1658. public function qualifyColumns($columns)
  1659. {
  1660. return $this->model->qualifyColumns($columns);
  1661. }
  1662. /**
  1663. * Get the given macro by name.
  1664. *
  1665. * @param string $name
  1666. * @return \Closure
  1667. */
  1668. public function getMacro($name)
  1669. {
  1670. return Arr::get($this->localMacros, $name);
  1671. }
  1672. /**
  1673. * Checks if a macro is registered.
  1674. *
  1675. * @param string $name
  1676. * @return bool
  1677. */
  1678. public function hasMacro($name)
  1679. {
  1680. return isset($this->localMacros[$name]);
  1681. }
  1682. /**
  1683. * Get the given global macro by name.
  1684. *
  1685. * @param string $name
  1686. * @return \Closure
  1687. */
  1688. public static function getGlobalMacro($name)
  1689. {
  1690. return Arr::get(static::$macros, $name);
  1691. }
  1692. /**
  1693. * Checks if a global macro is registered.
  1694. *
  1695. * @param string $name
  1696. * @return bool
  1697. */
  1698. public static function hasGlobalMacro($name)
  1699. {
  1700. return isset(static::$macros[$name]);
  1701. }
  1702. /**
  1703. * Dynamically access builder proxies.
  1704. *
  1705. * @param string $key
  1706. * @return mixed
  1707. *
  1708. * @throws \Exception
  1709. */
  1710. public function __get($key)
  1711. {
  1712. if (in_array($key, ['orWhere', 'whereNot', 'orWhereNot'])) {
  1713. return new HigherOrderBuilderProxy($this, $key);
  1714. }
  1715. if (in_array($key, $this->propertyPassthru)) {
  1716. return $this->toBase()->{$key};
  1717. }
  1718. throw new Exception("Property [{$key}] does not exist on the Eloquent builder instance.");
  1719. }
  1720. /**
  1721. * Dynamically handle calls into the query instance.
  1722. *
  1723. * @param string $method
  1724. * @param array $parameters
  1725. * @return mixed
  1726. */
  1727. public function __call($method, $parameters)
  1728. {
  1729. if ($method === 'macro') {
  1730. $this->localMacros[$parameters[0]] = $parameters[1];
  1731. return;
  1732. }
  1733. if ($this->hasMacro($method)) {
  1734. array_unshift($parameters, $this);
  1735. return $this->localMacros[$method](...$parameters);
  1736. }
  1737. if (static::hasGlobalMacro($method)) {
  1738. $callable = static::$macros[$method];
  1739. if ($callable instanceof Closure) {
  1740. $callable = $callable->bindTo($this, static::class);
  1741. }
  1742. return $callable(...$parameters);
  1743. }
  1744. if ($this->hasNamedScope($method)) {
  1745. return $this->callNamedScope($method, $parameters);
  1746. }
  1747. if (in_array(strtolower($method), $this->passthru)) {
  1748. return $this->toBase()->{$method}(...$parameters);
  1749. }
  1750. $this->forwardCallTo($this->query, $method, $parameters);
  1751. return $this;
  1752. }
  1753. /**
  1754. * Dynamically handle calls into the query instance.
  1755. *
  1756. * @param string $method
  1757. * @param array $parameters
  1758. * @return mixed
  1759. *
  1760. * @throws \BadMethodCallException
  1761. */
  1762. public static function __callStatic($method, $parameters)
  1763. {
  1764. if ($method === 'macro') {
  1765. static::$macros[$parameters[0]] = $parameters[1];
  1766. return;
  1767. }
  1768. if ($method === 'mixin') {
  1769. return static::registerMixin($parameters[0], $parameters[1] ?? true);
  1770. }
  1771. if (! static::hasGlobalMacro($method)) {
  1772. static::throwBadMethodCallException($method);
  1773. }
  1774. $callable = static::$macros[$method];
  1775. if ($callable instanceof Closure) {
  1776. $callable = $callable->bindTo(null, static::class);
  1777. }
  1778. return $callable(...$parameters);
  1779. }
  1780. /**
  1781. * Register the given mixin with the builder.
  1782. *
  1783. * @param string $mixin
  1784. * @param bool $replace
  1785. * @return void
  1786. */
  1787. protected static function registerMixin($mixin, $replace)
  1788. {
  1789. $methods = (new ReflectionClass($mixin))->getMethods(
  1790. ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
  1791. );
  1792. foreach ($methods as $method) {
  1793. if ($replace || ! static::hasGlobalMacro($method->name)) {
  1794. static::macro($method->name, $method->invoke($mixin));
  1795. }
  1796. }
  1797. }
  1798. /**
  1799. * Clone the Eloquent query builder.
  1800. *
  1801. * @return static
  1802. */
  1803. public function clone()
  1804. {
  1805. return clone $this;
  1806. }
  1807. /**
  1808. * Force a clone of the underlying query builder when cloning.
  1809. *
  1810. * @return void
  1811. */
  1812. public function __clone()
  1813. {
  1814. $this->query = clone $this->query;
  1815. }
  1816. }