Skip to content

Commit

Permalink
Use InteractsWithDictionary in Eloquent collection (#46196)
Browse files Browse the repository at this point in the history
This improves the behaviour of database collections when using non-scalar primary keys by using the same dictionary behaviour used elsewhere.
alcaeus authored Feb 21, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 9776ba7 commit d91e03a
Showing 2 changed files with 40 additions and 6 deletions.
15 changes: 9 additions & 6 deletions src/Illuminate/Database/Eloquent/Collection.php
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
use Illuminate\Contracts\Queue\QueueableCollection;
use Illuminate\Contracts\Queue\QueueableEntity;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithDictionary;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection as BaseCollection;
use LogicException;
@@ -17,6 +18,8 @@
*/
class Collection extends BaseCollection implements QueueableCollection
{
use InteractsWithDictionary;

/**
* Find a model in the collection by key.
*
@@ -322,7 +325,7 @@ public function merge($items)
$dictionary = $this->getDictionary();

foreach ($items as $item) {
$dictionary[$item->getKey()] = $item;
$dictionary[$this->getDictionaryKey($item->getKey())] = $item;
}

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

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

foreach ($this->items as $item) {
if (isset($dictionary[$item->getKey()])) {
if (isset($dictionary[$this->getDictionaryKey($item->getKey())])) {
$intersect->add($item);
}
}
@@ -459,7 +462,7 @@ public function only($keys)
return new static($this->items);
}

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

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

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

foreach ($items as $value) {
$dictionary[$value->getKey()] = $value;
$dictionary[$this->getDictionaryKey($value->getKey())] = $value;
}

return $dictionary;
31 changes: 31 additions & 0 deletions tests/Database/DatabaseEloquentCollectionTest.php
Original file line number Diff line number Diff line change
@@ -608,6 +608,25 @@ public function testLoadExistsShouldCastBool()
$this->assertContainsOnly('bool', $commentsExists);
}

public function testWithNonScalarKey()
{
$fooKey = new EloquentTestKey('foo');
$foo = m::mock(Model::class);
$foo->shouldReceive('getKey')->andReturn($fooKey);

$barKey = new EloquentTestKey('bar');
$bar = m::mock(Model::class);
$bar->shouldReceive('getKey')->andReturn($barKey);

$collection = new Collection([$foo, $bar]);

$this->assertCount(1, $collection->only([$fooKey]));
$this->assertSame($foo, $collection->only($fooKey)->first());

$this->assertCount(1, $collection->except([$fooKey]));
$this->assertSame($bar, $collection->except($fooKey)->first());
}

/**
* Helpers...
*/
@@ -689,3 +708,15 @@ class EloquentTestCommentModel extends Model
protected $guarded = [];
public $timestamps = false;
}

class EloquentTestKey
{
public function __construct(private readonly string $key)
{
}

public function __toString()
{
return $this->key;
}
}

0 comments on commit d91e03a

Please sign in to comment.