Collection.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. <?php
  2. namespace Illuminate\Database\Eloquent;
  3. use LogicException;
  4. use Illuminate\Support\Arr;
  5. use Illuminate\Contracts\Support\Arrayable;
  6. use Illuminate\Contracts\Queue\QueueableCollection;
  7. use Illuminate\Support\Collection as BaseCollection;
  8. class Collection extends BaseCollection implements QueueableCollection
  9. {
  10. /**
  11. * Find a model in the collection by key.
  12. *
  13. * @param mixed $key
  14. * @param mixed $default
  15. * @return \Illuminate\Database\Eloquent\Model|static
  16. */
  17. public function find($key, $default = null)
  18. {
  19. if ($key instanceof Model) {
  20. $key = $key->getKey();
  21. }
  22. if ($key instanceof Arrayable) {
  23. $key = $key->toArray();
  24. }
  25. if (is_array($key)) {
  26. if ($this->isEmpty()) {
  27. return new static;
  28. }
  29. return $this->whereIn($this->first()->getKeyName(), $key);
  30. }
  31. return Arr::first($this->items, function ($model) use ($key) {
  32. return $model->getKey() == $key;
  33. }, $default);
  34. }
  35. /**
  36. * Load a set of relationships onto the collection.
  37. *
  38. * @param mixed $relations
  39. * @return $this
  40. */
  41. public function load($relations)
  42. {
  43. if ($this->isNotEmpty()) {
  44. if (is_string($relations)) {
  45. $relations = func_get_args();
  46. }
  47. $query = $this->first()->newQueryWithoutRelationships()->with($relations);
  48. $this->items = $query->eagerLoadRelations($this->items);
  49. }
  50. return $this;
  51. }
  52. /**
  53. * Add an item to the collection.
  54. *
  55. * @param mixed $item
  56. * @return $this
  57. */
  58. public function add($item)
  59. {
  60. $this->items[] = $item;
  61. return $this;
  62. }
  63. /**
  64. * Determine if a key exists in the collection.
  65. *
  66. * @param mixed $key
  67. * @param mixed $operator
  68. * @param mixed $value
  69. * @return bool
  70. */
  71. public function contains($key, $operator = null, $value = null)
  72. {
  73. if (func_num_args() > 1 || $this->useAsCallable($key)) {
  74. return parent::contains(...func_get_args());
  75. }
  76. if ($key instanceof Model) {
  77. return parent::contains(function ($model) use ($key) {
  78. return $model->is($key);
  79. });
  80. }
  81. return parent::contains(function ($model) use ($key) {
  82. return $model->getKey() == $key;
  83. });
  84. }
  85. /**
  86. * Get the array of primary keys.
  87. *
  88. * @return array
  89. */
  90. public function modelKeys()
  91. {
  92. return array_map(function ($model) {
  93. return $model->getKey();
  94. }, $this->items);
  95. }
  96. /**
  97. * Merge the collection with the given items.
  98. *
  99. * @param \ArrayAccess|array $items
  100. * @return static
  101. */
  102. public function merge($items)
  103. {
  104. $dictionary = $this->getDictionary();
  105. foreach ($items as $item) {
  106. $dictionary[$item->getKey()] = $item;
  107. }
  108. return new static(array_values($dictionary));
  109. }
  110. /**
  111. * Run a map over each of the items.
  112. *
  113. * @param callable $callback
  114. * @return \Illuminate\Support\Collection|static
  115. */
  116. public function map(callable $callback)
  117. {
  118. $result = parent::map($callback);
  119. return $result->contains(function ($item) {
  120. return ! $item instanceof Model;
  121. }) ? $result->toBase() : $result;
  122. }
  123. /**
  124. * Reload a fresh model instance from the database for all the entities.
  125. *
  126. * @param array|string $with
  127. * @return static
  128. */
  129. public function fresh($with = [])
  130. {
  131. if ($this->isEmpty()) {
  132. return new static;
  133. }
  134. $model = $this->first();
  135. $freshModels = $model->newQueryWithoutScopes()
  136. ->with(is_string($with) ? func_get_args() : $with)
  137. ->whereIn($model->getKeyName(), $this->modelKeys())
  138. ->get()
  139. ->getDictionary();
  140. return $this->map(function ($model) use ($freshModels) {
  141. return $model->exists && isset($freshModels[$model->getKey()])
  142. ? $freshModels[$model->getKey()] : null;
  143. });
  144. }
  145. /**
  146. * Diff the collection with the given items.
  147. *
  148. * @param \ArrayAccess|array $items
  149. * @return static
  150. */
  151. public function diff($items)
  152. {
  153. $diff = new static;
  154. $dictionary = $this->getDictionary($items);
  155. foreach ($this->items as $item) {
  156. if (! isset($dictionary[$item->getKey()])) {
  157. $diff->add($item);
  158. }
  159. }
  160. return $diff;
  161. }
  162. /**
  163. * Intersect the collection with the given items.
  164. *
  165. * @param \ArrayAccess|array $items
  166. * @return static
  167. */
  168. public function intersect($items)
  169. {
  170. $intersect = new static;
  171. $dictionary = $this->getDictionary($items);
  172. foreach ($this->items as $item) {
  173. if (isset($dictionary[$item->getKey()])) {
  174. $intersect->add($item);
  175. }
  176. }
  177. return $intersect;
  178. }
  179. /**
  180. * Return only unique items from the collection.
  181. *
  182. * @param string|callable|null $key
  183. * @param bool $strict
  184. * @return static|\Illuminate\Support\Collection
  185. */
  186. public function unique($key = null, $strict = false)
  187. {
  188. if (! is_null($key)) {
  189. return parent::unique($key, $strict);
  190. }
  191. return new static(array_values($this->getDictionary()));
  192. }
  193. /**
  194. * Returns only the models from the collection with the specified keys.
  195. *
  196. * @param mixed $keys
  197. * @return static
  198. */
  199. public function only($keys)
  200. {
  201. if (is_null($keys)) {
  202. return new static($this->items);
  203. }
  204. $dictionary = Arr::only($this->getDictionary(), $keys);
  205. return new static(array_values($dictionary));
  206. }
  207. /**
  208. * Returns all models in the collection except the models with specified keys.
  209. *
  210. * @param mixed $keys
  211. * @return static
  212. */
  213. public function except($keys)
  214. {
  215. $dictionary = Arr::except($this->getDictionary(), $keys);
  216. return new static(array_values($dictionary));
  217. }
  218. /**
  219. * Make the given, typically visible, attributes hidden across the entire collection.
  220. *
  221. * @param array|string $attributes
  222. * @return $this
  223. */
  224. public function makeHidden($attributes)
  225. {
  226. return $this->each(function ($model) use ($attributes) {
  227. $model->addHidden($attributes);
  228. });
  229. }
  230. /**
  231. * Make the given, typically hidden, attributes visible across the entire collection.
  232. *
  233. * @param array|string $attributes
  234. * @return $this
  235. */
  236. public function makeVisible($attributes)
  237. {
  238. return $this->each(function ($model) use ($attributes) {
  239. $model->makeVisible($attributes);
  240. });
  241. }
  242. /**
  243. * Get a dictionary keyed by primary keys.
  244. *
  245. * @param \ArrayAccess|array|null $items
  246. * @return array
  247. */
  248. public function getDictionary($items = null)
  249. {
  250. $items = is_null($items) ? $this->items : $items;
  251. $dictionary = [];
  252. foreach ($items as $value) {
  253. $dictionary[$value->getKey()] = $value;
  254. }
  255. return $dictionary;
  256. }
  257. /**
  258. * The following methods are intercepted to always return base collections.
  259. */
  260. /**
  261. * Get an array with the values of a given key.
  262. *
  263. * @param string $value
  264. * @param string|null $key
  265. * @return \Illuminate\Support\Collection
  266. */
  267. public function pluck($value, $key = null)
  268. {
  269. return $this->toBase()->pluck($value, $key);
  270. }
  271. /**
  272. * Get the keys of the collection items.
  273. *
  274. * @return \Illuminate\Support\Collection
  275. */
  276. public function keys()
  277. {
  278. return $this->toBase()->keys();
  279. }
  280. /**
  281. * Zip the collection together with one or more arrays.
  282. *
  283. * @param mixed ...$items
  284. * @return \Illuminate\Support\Collection
  285. */
  286. public function zip($items)
  287. {
  288. return call_user_func_array([$this->toBase(), 'zip'], func_get_args());
  289. }
  290. /**
  291. * Collapse the collection of items into a single array.
  292. *
  293. * @return \Illuminate\Support\Collection
  294. */
  295. public function collapse()
  296. {
  297. return $this->toBase()->collapse();
  298. }
  299. /**
  300. * Get a flattened array of the items in the collection.
  301. *
  302. * @param int $depth
  303. * @return \Illuminate\Support\Collection
  304. */
  305. public function flatten($depth = INF)
  306. {
  307. return $this->toBase()->flatten($depth);
  308. }
  309. /**
  310. * Flip the items in the collection.
  311. *
  312. * @return \Illuminate\Support\Collection
  313. */
  314. public function flip()
  315. {
  316. return $this->toBase()->flip();
  317. }
  318. /**
  319. * Pad collection to the specified length with a value.
  320. *
  321. * @param int $size
  322. * @param mixed $value
  323. * @return \Illuminate\Support\Collection
  324. */
  325. public function pad($size, $value)
  326. {
  327. return $this->toBase()->pad($size, $value);
  328. }
  329. /**
  330. * Get the type of the entities being queued.
  331. *
  332. * @return string|null
  333. * @throws \LogicException
  334. */
  335. public function getQueueableClass()
  336. {
  337. if ($this->isEmpty()) {
  338. return;
  339. }
  340. $class = get_class($this->first());
  341. $this->each(function ($model) use ($class) {
  342. if (get_class($model) !== $class) {
  343. throw new LogicException('Queueing collections with multiple model types is not supported.');
  344. }
  345. });
  346. return $class;
  347. }
  348. /**
  349. * Get the identifiers for all of the entities.
  350. *
  351. * @return array
  352. */
  353. public function getQueueableIds()
  354. {
  355. return $this->modelKeys();
  356. }
  357. /**
  358. * Get the connection of the entities being queued.
  359. *
  360. * @return string|null
  361. * @throws \LogicException
  362. */
  363. public function getQueueableConnection()
  364. {
  365. if ($this->isEmpty()) {
  366. return;
  367. }
  368. $connection = $this->first()->getConnectionName();
  369. $this->each(function ($model) use ($connection) {
  370. if ($model->getConnectionName() !== $connection) {
  371. throw new LogicException('Queueing collections with multiple model connections is not supported.');
  372. }
  373. });
  374. return $connection;
  375. }
  376. }