Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions src/Illuminate/Collections/Arr.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use ArgumentCountError;
use ArrayAccess;
use Closure;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Support\Traits\Macroable;
Expand Down Expand Up @@ -686,8 +687,8 @@ public static function select($array, $keys)
* Pluck an array of values from an array.
*
* @param iterable $array
* @param string|array|int|null $value
* @param string|array|null $key
* @param string|array|int|Closure|null $value
* @param string|array|Closure|null $key
* @return array
*/
public static function pluck($array, $value, $key = null)
Expand All @@ -697,15 +698,19 @@ public static function pluck($array, $value, $key = null)
[$value, $key] = static::explodePluckParameters($value, $key);

foreach ($array as $item) {
$itemValue = data_get($item, $value);
$itemValue = $value instanceof Closure
? $value($item)
: data_get($item, $value);

// If the key is "null", we will just append the value to the array and keep
// looping. Otherwise we will key the array using the value of the key we
// received from the developer. Then we'll return the final array form.
if (is_null($key)) {
$results[] = $itemValue;
} else {
$itemKey = data_get($item, $key);
$itemKey = $key instanceof Closure
? $key($item)
: data_get($item, $key);

if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
$itemKey = (string) $itemKey;
Expand All @@ -721,15 +726,15 @@ public static function pluck($array, $value, $key = null)
/**
* Explode the "value" and "key" arguments passed to "pluck".
*
* @param string|array $value
* @param string|array|null $key
* @param string|array|Closure $value
* @param string|array|Closure|null $key
* @return array
*/
protected static function explodePluckParameters($value, $key)
{
$value = is_string($value) ? explode('.', $value) : $value;

$key = is_null($key) || is_array($key) ? $key : explode('.', $key);
$key = is_null($key) || is_array($key) || $key instanceof Closure ? $key : explode('.', $key);

return [$value, $key];
}
Expand Down
10 changes: 7 additions & 3 deletions src/Illuminate/Collections/LazyCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -777,12 +777,16 @@ public function pluck($value, $key = null)
[$value, $key] = $this->explodePluckParameters($value, $key);

foreach ($this as $item) {
$itemValue = data_get($item, $value);
$itemValue = $value instanceof Closure
? $value($item)
: data_get($item, $value);

if (is_null($key)) {
yield $itemValue;
} else {
$itemKey = data_get($item, $key);
$itemKey = $key instanceof Closure
? $key($item)
: data_get($item, $key);

if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
$itemKey = (string) $itemKey;
Expand Down Expand Up @@ -1869,7 +1873,7 @@ protected function explodePluckParameters($value, $key)
{
$value = is_string($value) ? explode('.', $value) : $value;

$key = is_null($key) || is_array($key) ? $key : explode('.', $key);
$key = is_null($key) || is_array($key) || $key instanceof Closure ? $key : explode('.', $key);

return [$value, $key];
}
Expand Down
5 changes: 3 additions & 2 deletions src/Illuminate/Database/Eloquent/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Illuminate\Database\Eloquent;

use Closure;
use Illuminate\Contracts\Queue\QueueableCollection;
use Illuminate\Contracts\Queue\QueueableEntity;
use Illuminate\Contracts\Support\Arrayable;
Expand Down Expand Up @@ -717,8 +718,8 @@ public function partition($key, $operator = null, $value = null)
/**
* Get an array with the values of a given key.
*
* @param string|array<array-key, string>|null $value
* @param string|null $key
* @param string|array<array-key, string>|Closure|null $value
* @param string|Closure|null $key
* @return \Illuminate\Support\Collection<array-key, mixed>
*/
public function pluck($value, $key = null)
Expand Down
20 changes: 20 additions & 0 deletions tests/Database/DatabaseEloquentCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,26 @@ public function testWithNonScalarKey()
$this->assertSame($bar, $collection->except($fooKey)->first());
}

public function testPluck()
{
$model1 = (new TestEloquentCollectionModel)->forceFill(['id' => 1, 'name' => 'John', 'country' => 'US']);
$model2 = (new TestEloquentCollectionModel)->forceFill(['id' => 2, 'name' => 'Jane', 'country' => 'NL']);
$model3 = (new TestEloquentCollectionModel)->forceFill(['id' => 3, 'name' => 'Taylor', 'country' => 'US']);

$c = new Collection;

$c->push($model1)->push($model2)->push($model3);

$this->assertInstanceOf(BaseCollection::class, $c->pluck('id'));
$this->assertEquals([1, 2, 3], $c->pluck('id')->all());

$this->assertInstanceOf(BaseCollection::class, $c->pluck('id', 'id'));
$this->assertEquals([1 => 1, 2 => 2, 3 => 3], $c->pluck('id', 'id')->all());
$this->assertInstanceOf(BaseCollection::class, $c->pluck('test'));

$this->assertEquals(['John (US)', 'Jane (NL)', 'Taylor (US)'], $c->pluck(fn (TestEloquentCollectionModel $model) => "{$model->name} ({$model->country})")->all());
}

/**
* Helpers...
*/
Expand Down
22 changes: 22 additions & 0 deletions tests/Support/SupportCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2323,6 +2323,28 @@ public function testPluckWithDotNotation($collection)
$this->assertEquals([['php', 'python'], ['php', 'asp', 'java']], $data->pluck('skill.backend')->all());
}

#[DataProvider('collectionClassProvider')]
public function testPluckWithClosure($collection)
{
$data = new $collection([
[
'name' => 'amir',
'skill' => [
'backend' => ['php', 'python'],
],
],
[
'name' => 'taylor',
'skill' => [
'backend' => ['php', 'asp', 'java'],
],
],
]);

$this->assertEquals(['amir (verified)', 'taylor (verified)'], $data->pluck(fn (array $row) => "{$row['name']} (verified)")->all());
$this->assertEquals(['php/python' => 'amir', 'php/asp/java' => 'taylor'], $data->pluck('name', fn (array $row) => implode('/', $row['skill']['backend']))->all());
}

#[DataProvider('collectionClassProvider')]
public function testPluckDuplicateKeysExist($collection)
{
Expand Down
Loading