Skip to content

Commit

Permalink
add foreign_id_key option to use specific array key as id
Browse files Browse the repository at this point in the history
  • Loading branch information
eisfeuer committed Apr 8, 2020
1 parent b3d0e4e commit f7cd3b1
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 7 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ build
composer.lock
docs
vendor
coverage
coverage
.phpunit.result.cache
1 change: 0 additions & 1 deletion .phpunit.result.cache

This file was deleted.

8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ $user->tasks()->sync(
);
```

Sync using values by given key as ids
``` php
$user->tasks()->sync([
['task_id' => 1, status' => 'wip', 'priority' => 1],
['task_id' => 4, status' => 'finished', 'priority' => 3],
], [foreign_id_key => 'task_id']);
```

Result is the same as the result of the sync method of `BelongsToMany`, an array with attach, detached and updated rows.

### Changelog
Expand Down
20 changes: 16 additions & 4 deletions src/IdAndAttributesCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

namespace Elbgoods\SyncOneToMany;

use Illuminate\Support\Arr;
use Illuminate\Support\Collection as LaravelCollection;
use InvalidArgumentException;

class IdAndAttributesCollection extends LaravelCollection
{
public function __construct(array $items = [])
public function __construct(array $items = [], array $options = [])
{
parent::__construct($this->convertItems($items));
parent::__construct($this->convertItems($items, $options));
}

public function getIds(): array
Expand All @@ -18,9 +20,19 @@ public function getIds(): array
})->toArray();
}

protected function convertItems(array $items): array
protected function convertItems(array $items, array $options): array
{
return collect($items)->map(static function ($value, $key) {
return collect($items)->map(static function ($value, $key) use ($options) {
if (isset($options['foreign_id_key'])) {
$foreignIdKey = $options['foreign_id_key'];

if (! isset($value[$foreignIdKey])) {
throw new InvalidArgumentException("Any value must have a {$foreignIdKey} field (foreign id key)");
}

return new IdAndAttributesContainer($value[$foreignIdKey], Arr::except($value, $foreignIdKey));
}

return new IdAndAttributesContainer($key, $value);
})->values()->toArray();
}
Expand Down
2 changes: 1 addition & 1 deletion src/OneToManySync.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static function make(HasMany $hasMany, array $ids, array $options): self
public function __construct(HasMany $hasMany, array $idsAndAttributes, array $options)
{
$this->hasMany = $hasMany;
$this->idsAndAttributes = new IdAndAttributesCollection($idsAndAttributes);
$this->idsAndAttributes = new IdAndAttributesCollection($idsAndAttributes, $options);
$this->options = $options;
}

Expand Down
43 changes: 43 additions & 0 deletions tests/IdAndAttributesCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Elbgoods\SyncOneToMany\IdAndAttributesCollection;
use Elbgoods\SyncOneToMany\IdAndAttributesContainer;
use Illuminate\Support\Collection as LaravelCollection;
use InvalidArgumentException;

final class IdAndAttributesCollectionTest extends TestCase
{
Expand Down Expand Up @@ -38,6 +39,20 @@ public function it_returns_keys_of_assoc_array_as_id(): void
$this->assertArrayContainsExact([2, 4], $collection->getIds());
}

/**
* @test
*/
public function it_returns_given_foreign_ids_as_ids()
{
$collection = new IdAndAttributesCollection([
['task_id' => 2, 'status' => 'wip'],
['task_id' => 4, 'status' => 'finished'],
], [
'foreign_id_key' => 'task_id',
]);
$this->assertArrayContainsExact([2, 4], $collection->getIds());
}

/**
* @test
*/
Expand All @@ -52,4 +67,32 @@ public function it_converts_items_to_IdAndAttributeContainer_objects(): void
$this->assertEquals(4, $collection->first()->getId());
$this->assertEquals(['status' => 'wip'], $collection->first()->getAdditionalAttributes());
}

/**
* @test
*/
public function it_converts_items_to_IdAndAttributeContainer_objects_2(): void
{
$collection = new IdAndAttributesCollection([
['task_id' => 4, 'status' => 'wip'],
], ['foreign_id_key' => 'task_id']);

$this->assertCount(1, $collection);
$this->assertInstanceOf(IdAndAttributesContainer::class, $collection->first());
$this->assertEquals(4, $collection->first()->getId());
$this->assertEquals(['status' => 'wip'], $collection->first()->getAdditionalAttributes());
}

/**
* @test
*/
public function it_raises_exception_when_foreign_id_key_is_missing(): void
{
$this->expectException(InvalidArgumentException::class);

new IdAndAttributesCollection([
['task_id' => 4, 'status' => 'wip'],
['status' => 'done'],
], ['foreign_id_key' => 'task_id']);
}
}
52 changes: 52 additions & 0 deletions tests/OneToManySyncTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -353,4 +353,56 @@ public function it_attaches_changes_and_detach_models(): void
$this->assertModelEquals($user, $task5->user);
$this->assertEquals('wip', $task5->status);
}

/**
* @test
*/
public function it_syncs_model_by_given_key()
{
$user = factory(User::class)->create();
$task1 = factory(Task::class)->create([
'user_id' => $user->id,
'status' => 'wip',
]);
$task2 = factory(Task::class)->create([
'user_id' => $user->id,
'status' => 'finished',
]);
$task3 = factory(Task::class)->create([
'user_id' => $user->id,
'status' => 'wip',
]);
$task4 = factory(Task::class)->create();
$task5 = factory(Task::class)->create();

$result = $user->tasks()->sync([
$task1->id => ['task_id' => $task1->id, 'status' => 'finished'],
$task2->id => ['task_id' => $task2->id, 'status' => 'finished'],
$task4->id => ['task_id' => $task4->id, 'status' => 'wip'],
$task5->id => ['task_id' => $task5->id, 'status' => 'wip'],
], [
'foreign_id_key' => 'task_id',
]);

$this->assertAttached([$task4->id, $task5->id], $result);
$this->assertChanged([$task1->id, $task2->id], $result);
$this->assertDetached([$task3->id], $result);

$task1->refresh();
$task2->refresh();
$task3->refresh();
$task4->refresh();
$task5->refresh();

$this->assertModelEquals($user, $task1->user);
$this->assertEquals('finished', $task1->status);
$this->assertModelEquals($user, $task2->user);
$this->assertEquals('finished', $task2->status);
$this->assertNull($task3->user);
$this->assertEquals('wip', $task3->status);
$this->assertModelEquals($user, $task4->user);
$this->assertEquals('wip', $task4->status);
$this->assertModelEquals($user, $task5->user);
$this->assertEquals('wip', $task5->status);
}
}

0 comments on commit f7cd3b1

Please sign in to comment.