Skip to content

Commit 74811d4

Browse files
authored
[12.x] Add Closure-support to $key/$value in Collection pluck() method (#56188)
* WIP * Style
1 parent 0820018 commit 74811d4

File tree

5 files changed

+64
-12
lines changed

5 files changed

+64
-12
lines changed

src/Illuminate/Collections/Arr.php

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use ArgumentCountError;
66
use ArrayAccess;
7+
use Closure;
78
use Illuminate\Contracts\Support\Arrayable;
89
use Illuminate\Contracts\Support\Jsonable;
910
use Illuminate\Support\Traits\Macroable;
@@ -686,8 +687,8 @@ public static function select($array, $keys)
686687
* Pluck an array of values from an array.
687688
*
688689
* @param iterable $array
689-
* @param string|array|int|null $value
690-
* @param string|array|null $key
690+
* @param string|array|int|Closure|null $value
691+
* @param string|array|Closure|null $key
691692
* @return array
692693
*/
693694
public static function pluck($array, $value, $key = null)
@@ -697,15 +698,19 @@ public static function pluck($array, $value, $key = null)
697698
[$value, $key] = static::explodePluckParameters($value, $key);
698699

699700
foreach ($array as $item) {
700-
$itemValue = data_get($item, $value);
701+
$itemValue = $value instanceof Closure
702+
? $value($item)
703+
: data_get($item, $value);
701704

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

710715
if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
711716
$itemKey = (string) $itemKey;
@@ -721,15 +726,15 @@ public static function pluck($array, $value, $key = null)
721726
/**
722727
* Explode the "value" and "key" arguments passed to "pluck".
723728
*
724-
* @param string|array $value
725-
* @param string|array|null $key
729+
* @param string|array|Closure $value
730+
* @param string|array|Closure|null $key
726731
* @return array
727732
*/
728733
protected static function explodePluckParameters($value, $key)
729734
{
730735
$value = is_string($value) ? explode('.', $value) : $value;
731736

732-
$key = is_null($key) || is_array($key) ? $key : explode('.', $key);
737+
$key = is_null($key) || is_array($key) || $key instanceof Closure ? $key : explode('.', $key);
733738

734739
return [$value, $key];
735740
}

src/Illuminate/Collections/LazyCollection.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -777,12 +777,16 @@ public function pluck($value, $key = null)
777777
[$value, $key] = $this->explodePluckParameters($value, $key);
778778

779779
foreach ($this as $item) {
780-
$itemValue = data_get($item, $value);
780+
$itemValue = $value instanceof Closure
781+
? $value($item)
782+
: data_get($item, $value);
781783

782784
if (is_null($key)) {
783785
yield $itemValue;
784786
} else {
785-
$itemKey = data_get($item, $key);
787+
$itemKey = $key instanceof Closure
788+
? $key($item)
789+
: data_get($item, $key);
786790

787791
if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
788792
$itemKey = (string) $itemKey;
@@ -1869,7 +1873,7 @@ protected function explodePluckParameters($value, $key)
18691873
{
18701874
$value = is_string($value) ? explode('.', $value) : $value;
18711875

1872-
$key = is_null($key) || is_array($key) ? $key : explode('.', $key);
1876+
$key = is_null($key) || is_array($key) || $key instanceof Closure ? $key : explode('.', $key);
18731877

18741878
return [$value, $key];
18751879
}

src/Illuminate/Database/Eloquent/Collection.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Illuminate\Database\Eloquent;
44

5+
use Closure;
56
use Illuminate\Contracts\Queue\QueueableCollection;
67
use Illuminate\Contracts\Queue\QueueableEntity;
78
use Illuminate\Contracts\Support\Arrayable;
@@ -717,8 +718,8 @@ public function partition($key, $operator = null, $value = null)
717718
/**
718719
* Get an array with the values of a given key.
719720
*
720-
* @param string|array<array-key, string>|null $value
721-
* @param string|null $key
721+
* @param string|array<array-key, string>|Closure|null $value
722+
* @param string|Closure|null $key
722723
* @return \Illuminate\Support\Collection<array-key, mixed>
723724
*/
724725
public function pluck($value, $key = null)

tests/Database/DatabaseEloquentCollectionTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,26 @@ public function testWithNonScalarKey()
732732
$this->assertSame($bar, $collection->except($fooKey)->first());
733733
}
734734

735+
public function testPluck()
736+
{
737+
$model1 = (new TestEloquentCollectionModel)->forceFill(['id' => 1, 'name' => 'John', 'country' => 'US']);
738+
$model2 = (new TestEloquentCollectionModel)->forceFill(['id' => 2, 'name' => 'Jane', 'country' => 'NL']);
739+
$model3 = (new TestEloquentCollectionModel)->forceFill(['id' => 3, 'name' => 'Taylor', 'country' => 'US']);
740+
741+
$c = new Collection;
742+
743+
$c->push($model1)->push($model2)->push($model3);
744+
745+
$this->assertInstanceOf(BaseCollection::class, $c->pluck('id'));
746+
$this->assertEquals([1, 2, 3], $c->pluck('id')->all());
747+
748+
$this->assertInstanceOf(BaseCollection::class, $c->pluck('id', 'id'));
749+
$this->assertEquals([1 => 1, 2 => 2, 3 => 3], $c->pluck('id', 'id')->all());
750+
$this->assertInstanceOf(BaseCollection::class, $c->pluck('test'));
751+
752+
$this->assertEquals(['John (US)', 'Jane (NL)', 'Taylor (US)'], $c->pluck(fn (TestEloquentCollectionModel $model) => "{$model->name} ({$model->country})")->all());
753+
}
754+
735755
/**
736756
* Helpers...
737757
*/

tests/Support/SupportCollectionTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2323,6 +2323,28 @@ public function testPluckWithDotNotation($collection)
23232323
$this->assertEquals([['php', 'python'], ['php', 'asp', 'java']], $data->pluck('skill.backend')->all());
23242324
}
23252325

2326+
#[DataProvider('collectionClassProvider')]
2327+
public function testPluckWithClosure($collection)
2328+
{
2329+
$data = new $collection([
2330+
[
2331+
'name' => 'amir',
2332+
'skill' => [
2333+
'backend' => ['php', 'python'],
2334+
],
2335+
],
2336+
[
2337+
'name' => 'taylor',
2338+
'skill' => [
2339+
'backend' => ['php', 'asp', 'java'],
2340+
],
2341+
],
2342+
]);
2343+
2344+
$this->assertEquals(['amir (verified)', 'taylor (verified)'], $data->pluck(fn (array $row) => "{$row['name']} (verified)")->all());
2345+
$this->assertEquals(['php/python' => 'amir', 'php/asp/java' => 'taylor'], $data->pluck('name', fn (array $row) => implode('/', $row['skill']['backend']))->all());
2346+
}
2347+
23262348
#[DataProvider('collectionClassProvider')]
23272349
public function testPluckDuplicateKeysExist($collection)
23282350
{

0 commit comments

Comments
 (0)