getKey(); } if ($key instanceof Arrayable) { $key = $key->toArray(); } if (is_array($key)) { if ($this->isEmpty()) { return new static; } return $this->whereIn($this->first()->getKeyName(), $key); } return Arr::first($this->items, function ($model) use ($key) { return $model->getKey() == $key; }, $default); } /** * Load a set of relationships onto the collection. * * @param array|string $relations * @return $this */ public function load($relations) { if ($this->isNotEmpty()) { if (is_string($relations)) { $relations = func_get_args(); } $query = $this->first()->newQueryWithoutRelationships()->with($relations); $this->items = $query->eagerLoadRelations($this->items); } return $this; } /** * Load a set of relationships onto the collection if they are not already eager loaded. * * @param array|string $relations * @return $this */ public function loadMissing($relations) { if (is_string($relations)) { $relations = func_get_args(); } foreach ($relations as $key => $value) { if (is_numeric($key)) { $key = $value; } $segments = explode('.', explode(':', $key)[0]); if (Str::contains($key, ':')) { $segments[count($segments) - 1] .= ':'.explode(':', $key)[1]; } $path = array_combine($segments, $segments); if (is_callable($value)) { $path[end($segments)] = $value; } $this->loadMissingRelation($this, $path); } return $this; } /** * Load a relationship path if it is not already eager loaded. * * @param \Illuminate\Database\Eloquent\Collection $models * @param array $path * @return void */ protected function loadMissingRelation(Collection $models, array $path) { $relation = array_splice($path, 0, 1); $name = explode(':', key($relation))[0]; if (is_string(reset($relation))) { $relation = reset($relation); } $models->filter(function ($model) use ($name) { return ! is_null($model) && ! $model->relationLoaded($name); })->load($relation); if (empty($path)) { return; } $models = $models->pluck($name); if ($models->first() instanceof BaseCollection) { $models = $models->collapse(); } $this->loadMissingRelation(new static($models), $path); } /** * Load a set of relationships onto the mixed relationship collection. * * @param string $relation * @param array $relations * @return $this */ public function loadMorph($relation, $relations) { $this->pluck($relation) ->filter() ->groupBy(function ($model) { return get_class($model); }) ->filter(function ($models, $className) use ($relations) { return Arr::has($relations, $className); }) ->each(function ($models, $className) use ($relations) { $className::with($relations[$className]) ->eagerLoadRelations($models->all()); }); return $this; } /** * Add an item to the collection. * * @param mixed $item * @return $this */ public function add($item) { $this->items[] = $item; return $this; } /** * Determine if a key exists in the collection. * * @param mixed $key * @param mixed $operator * @param mixed $value * @return bool */ public function contains($key, $operator = null, $value = null) { if (func_num_args() > 1 || $this->useAsCallable($key)) { return parent::contains(...func_get_args()); } if ($key instanceof Model) { return parent::contains(function ($model) use ($key) { return $model->is($key); }); } return parent::contains(function ($model) use ($key) { return $model->getKey() == $key; }); } /** * Get the array of primary keys. * * @return array */ public function modelKeys() { return array_map(function ($model) { return $model->getKey(); }, $this->items); } /** * Merge the collection with the given items. * * @param \ArrayAccess|array $items * @return static */ public function merge($items) { $dictionary = $this->getDictionary(); foreach ($items as $item) { $dictionary[$item->getKey()] = $item; } return new static(array_values($dictionary)); } /** * Run a map over each of the items. * * @param callable $callback * @return \Illuminate\Support\Collection|static */ public function map(callable $callback) { $result = parent::map($callback); return $result->contains(function ($item) { return ! $item instanceof Model; }) ? $result->toBase() : $result; } /** * Reload a fresh model instance from the database for all the entities. * * @param array|string $with * @return static */ public function fresh($with = []) { if ($this->isEmpty()) { return new static; } $model = $this->first(); $freshModels = $model->newQueryWithoutScopes() ->with(is_string($with) ? func_get_args() : $with) ->whereIn($model->getKeyName(), $this->modelKeys()) ->get() ->getDictionary(); return $this->map(function ($model) use ($freshModels) { return $model->exists && isset($freshModels[$model->getKey()]) ? $freshModels[$model->getKey()] : null; }); } /** * Diff the collection with the given items. * * @param \ArrayAccess|array $items * @return static */ public function diff($items) { $diff = new static; $dictionary = $this->getDictionary($items); foreach ($this->items as $item) { if (! isset($dictionary[$item->getKey()])) { $diff->add($item); } } return $diff; } /** * Intersect the collection with the given items. * * @param \ArrayAccess|array $items * @return static */ public function intersect($items) { $intersect = new static; $dictionary = $this->getDictionary($items); foreach ($this->items as $item) { if (isset($dictionary[$item->getKey()])) { $intersect->add($item); } } return $intersect; } /** * Return only unique items from the collection. * * @param string|callable|null $key * @param bool $strict * @return static|\Illuminate\Support\Collection */ public function unique($key = null, $strict = false) { if (! is_null($key)) { return parent::unique($key, $strict); } return new static(array_values($this->getDictionary())); } /** * Returns only the models from the collection with the specified keys. * * @param mixed $keys * @return static */ public function only($keys) { if (is_null($keys)) { return new static($this->items); } $dictionary = Arr::only($this->getDictionary(), $keys); return new static(array_values($dictionary)); } /** * Returns all models in the collection except the models with specified keys. * * @param mixed $keys * @return static */ public function except($keys) { $dictionary = Arr::except($this->getDictionary(), $keys); return new static(array_values($dictionary)); } /** * Make the given, typically visible, attributes hidden across the entire collection. * * @param array|string $attributes * @return $this */ public function makeHidden($attributes) { return $this->each(function ($model) use ($attributes) { $model->addHidden($attributes); }); } /** * Make the given, typically hidden, attributes visible across the entire collection. * * @param array|string $attributes * @return $this */ public function makeVisible($attributes) { return $this->each(function ($model) use ($attributes) { $model->makeVisible($attributes); }); } /** * Get a dictionary keyed by primary keys. * * @param \ArrayAccess|array|null $items * @return array */ public function getDictionary($items = null) { $items = is_null($items) ? $this->items : $items; $dictionary = []; foreach ($items as $value) { $dictionary[$value->getKey()] = $value; } return $dictionary; } /** * The following methods are intercepted to always return base collections. */ /** * Get an array with the values of a given key. * * @param string $value * @param string|null $key * @return \Illuminate\Support\Collection */ public function pluck($value, $key = null) { return $this->toBase()->pluck($value, $key); } /** * Get the keys of the collection items. * * @return \Illuminate\Support\Collection */ public function keys() { return $this->toBase()->keys(); } /** * Zip the collection together with one or more arrays. * * @param mixed ...$items * @return \Illuminate\Support\Collection */ public function zip($items) { return call_user_func_array([$this->toBase(), 'zip'], func_get_args()); } /** * Collapse the collection of items into a single array. * * @return \Illuminate\Support\Collection */ public function collapse() { return $this->toBase()->collapse(); } /** * Get a flattened array of the items in the collection. * * @param int $depth * @return \Illuminate\Support\Collection */ public function flatten($depth = INF) { return $this->toBase()->flatten($depth); } /** * Flip the items in the collection. * * @return \Illuminate\Support\Collection */ public function flip() { return $this->toBase()->flip(); } /** * Pad collection to the specified length with a value. * * @param int $size * @param mixed $value * @return \Illuminate\Support\Collection */ public function pad($size, $value) { return $this->toBase()->pad($size, $value); } /** * Get the type of the entities being queued. * * @return string|null * @throws \LogicException */ public function getQueueableClass() { if ($this->isEmpty()) { return; } $class = get_class($this->first()); $this->each(function ($model) use ($class) { if (get_class($model) !== $class) { throw new LogicException('Queueing collections with multiple model types is not supported.'); } }); return $class; } /** * Get the identifiers for all of the entities. * * @return array */ public function getQueueableIds() { if ($this->isEmpty()) { return []; } return $this->first() instanceof Pivot ? $this->map->getQueueableId()->all() : $this->modelKeys(); } /** * Get the relationships of the entities being queued. * * @return array */ public function getQueueableRelations() { return $this->isNotEmpty() ? $this->first()->getQueueableRelations() : []; } /** * Get the connection of the entities being queued. * * @return string|null * @throws \LogicException */ public function getQueueableConnection() { if ($this->isEmpty()) { return; } $connection = $this->first()->getConnectionName(); $this->each(function ($model) use ($connection) { if ($model->getConnectionName() !== $connection) { throw new LogicException('Queueing collections with multiple model connections is not supported.'); } }); return $connection; } }