Skip to content

Commit d91e03a

Browse files
authored
Use InteractsWithDictionary in Eloquent collection (#46196)
This improves the behaviour of database collections when using non-scalar primary keys by using the same dictionary behaviour used elsewhere.
1 parent 9776ba7 commit d91e03a

File tree

2 files changed

+40
-6
lines changed

2 files changed

+40
-6
lines changed

src/Illuminate/Database/Eloquent/Collection.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Illuminate\Contracts\Queue\QueueableCollection;
66
use Illuminate\Contracts\Queue\QueueableEntity;
77
use Illuminate\Contracts\Support\Arrayable;
8+
use Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithDictionary;
89
use Illuminate\Support\Arr;
910
use Illuminate\Support\Collection as BaseCollection;
1011
use LogicException;
@@ -17,6 +18,8 @@
1718
*/
1819
class Collection extends BaseCollection implements QueueableCollection
1920
{
21+
use InteractsWithDictionary;
22+
2023
/**
2124
* Find a model in the collection by key.
2225
*
@@ -322,7 +325,7 @@ public function merge($items)
322325
$dictionary = $this->getDictionary();
323326

324327
foreach ($items as $item) {
325-
$dictionary[$item->getKey()] = $item;
328+
$dictionary[$this->getDictionaryKey($item->getKey())] = $item;
326329
}
327330

328331
return new static(array_values($dictionary));
@@ -398,7 +401,7 @@ public function diff($items)
398401
$dictionary = $this->getDictionary($items);
399402

400403
foreach ($this->items as $item) {
401-
if (! isset($dictionary[$item->getKey()])) {
404+
if (! isset($dictionary[$this->getDictionaryKey($item->getKey())])) {
402405
$diff->add($item);
403406
}
404407
}
@@ -423,7 +426,7 @@ public function intersect($items)
423426
$dictionary = $this->getDictionary($items);
424427

425428
foreach ($this->items as $item) {
426-
if (isset($dictionary[$item->getKey()])) {
429+
if (isset($dictionary[$this->getDictionaryKey($item->getKey())])) {
427430
$intersect->add($item);
428431
}
429432
}
@@ -459,7 +462,7 @@ public function only($keys)
459462
return new static($this->items);
460463
}
461464

462-
$dictionary = Arr::only($this->getDictionary(), $keys);
465+
$dictionary = Arr::only($this->getDictionary(), array_map($this->getDictionaryKey(...), (array) $keys));
463466

464467
return new static(array_values($dictionary));
465468
}
@@ -472,7 +475,7 @@ public function only($keys)
472475
*/
473476
public function except($keys)
474477
{
475-
$dictionary = Arr::except($this->getDictionary(), $keys);
478+
$dictionary = Arr::except($this->getDictionary(), array_map($this->getDictionaryKey(...), (array) $keys));
476479

477480
return new static(array_values($dictionary));
478481
}
@@ -545,7 +548,7 @@ public function getDictionary($items = null)
545548
$dictionary = [];
546549

547550
foreach ($items as $value) {
548-
$dictionary[$value->getKey()] = $value;
551+
$dictionary[$this->getDictionaryKey($value->getKey())] = $value;
549552
}
550553

551554
return $dictionary;

tests/Database/DatabaseEloquentCollectionTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,25 @@ public function testLoadExistsShouldCastBool()
608608
$this->assertContainsOnly('bool', $commentsExists);
609609
}
610610

611+
public function testWithNonScalarKey()
612+
{
613+
$fooKey = new EloquentTestKey('foo');
614+
$foo = m::mock(Model::class);
615+
$foo->shouldReceive('getKey')->andReturn($fooKey);
616+
617+
$barKey = new EloquentTestKey('bar');
618+
$bar = m::mock(Model::class);
619+
$bar->shouldReceive('getKey')->andReturn($barKey);
620+
621+
$collection = new Collection([$foo, $bar]);
622+
623+
$this->assertCount(1, $collection->only([$fooKey]));
624+
$this->assertSame($foo, $collection->only($fooKey)->first());
625+
626+
$this->assertCount(1, $collection->except([$fooKey]));
627+
$this->assertSame($bar, $collection->except($fooKey)->first());
628+
}
629+
611630
/**
612631
* Helpers...
613632
*/
@@ -689,3 +708,15 @@ class EloquentTestCommentModel extends Model
689708
protected $guarded = [];
690709
public $timestamps = false;
691710
}
711+
712+
class EloquentTestKey
713+
{
714+
public function __construct(private readonly string $key)
715+
{
716+
}
717+
718+
public function __toString()
719+
{
720+
return $this->key;
721+
}
722+
}

0 commit comments

Comments
 (0)