|
<?php |
|
|
|
use App\Services\ModelSyncManager; |
|
use Illuminate\Support\Collection; |
|
use Illuminate\Support\Facades\DB; |
|
use Illuminate\Database\Eloquent\Model; |
|
use Illuminate\Database\Eloquent\SoftDeletes; |
|
use Illuminate\Database\Eloquent\Factories\Factory; |
|
use Illuminate\Database\Eloquent\Relations\HasMany; |
|
use Illuminate\Database\Eloquent\Factories\HasFactory; |
|
|
|
/** |
|
* We will set up a fake temporary database tables for this test along with associated |
|
* models & factories |
|
*/ |
|
// Define the models |
|
class SyncManagerParent extends Model { |
|
use HasFactory; |
|
|
|
protected $table = 'sync_manager_parent'; |
|
protected $fillable = ['name']; |
|
public $timestamps = false; |
|
|
|
public function children(): HasMany |
|
{ |
|
return $this->hasMany(SyncManagerChild::class, 'parent_id'); |
|
} |
|
} |
|
|
|
class SyncManagerChild extends Model { |
|
use HasFactory; |
|
|
|
protected $table = 'sync_manager_child'; |
|
protected $fillable = ['parent_id', 'name', 'size']; |
|
public $timestamps = false; |
|
} |
|
|
|
// Define the factories |
|
class SyncManagerParentFactory extends Factory { |
|
protected $model = SyncManagerParent::class; |
|
static int $id = 1; |
|
|
|
public function definition() |
|
{ |
|
return [ |
|
'id' => static::$id++, |
|
'name' => $this->faker->name, |
|
]; |
|
} |
|
|
|
public function withChildren(array $children) |
|
{ |
|
return $this->afterCreating(function (SyncManagerParent $parent) use ($children) { |
|
foreach ($children as $child) { |
|
$child['parent_id'] = $parent->id; |
|
(new SyncManagerChildFactory())->create($child); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
class SyncManagerChildFactory extends Factory { |
|
protected $model = SyncManagerChild::class; |
|
static int $id = 1; |
|
|
|
public function definition() |
|
{ |
|
return [ |
|
'id' => static::$id++, |
|
'parent_id' => null, |
|
'name' => $this->faker->name, |
|
'size' => $this->faker->numberBetween(1, 100), |
|
]; |
|
} |
|
} |
|
|
|
beforeEach(function () { |
|
DB::statement(' |
|
CREATE TEMPORARY TABLE IF NOT EXISTS sync_manager_parent ( |
|
id INTEGER PRIMARY KEY, |
|
name TEXT |
|
) |
|
'); |
|
|
|
DB::statement(' |
|
CREATE TEMPORARY TABLE IF NOT EXISTS sync_manager_child ( |
|
id INTEGER PRIMARY KEY, |
|
parent_id INTEGER, |
|
name TEXT, |
|
size INTEGER, |
|
color TEXT DEFAULT NULL, |
|
material TEXT DEFAULT NULL, |
|
created_at timestamp NULL DEFAULT NULL, |
|
updated_at timestamp NULL DEFAULT NULL, |
|
FOREIGN KEY (parent_id) REFERENCES sync_manager_parent(id) |
|
) |
|
'); |
|
|
|
DB::statement('CREATE UNIQUE INDEX IF NOT EXISTS idx_unique_parent_name ON sync_manager_child (parent_id, name)'); |
|
|
|
DB::statement('DELETE FROM sync_manager_parent'); |
|
DB::statement('DELETE FROM sync_manager_child'); |
|
|
|
$this->syncManager= new ModelSyncManager(); |
|
}); |
|
|
|
describe('syncRelated', function () { |
|
it('inserts new items', function () { |
|
$parent = (new SyncManagerParentFactory())->create(); |
|
|
|
$items = [ |
|
['name' => 'foo', 'size' => 10], |
|
['name' => 'bar', 'size' => 20], |
|
]; |
|
|
|
$this->syncManager->uniqueBy('name')->syncRelated($parent, 'children', $items); |
|
|
|
$parent->refresh(); |
|
expect($parent->children->count())->toBe(2); |
|
|
|
expect($parent->children->firstWhere('name', 'foo'))->toBeInstanceOf(SyncManagerChild::class); |
|
expect($parent->children->firstWhere('name', 'bar'))->toBeInstanceOf(SyncManagerChild::class); |
|
}); |
|
|
|
it('updates existing items', function () { |
|
$parent = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 10], |
|
['name' => 'bar', 'size' => 20], |
|
]) |
|
->create(); |
|
|
|
$newItems = [ |
|
['name' => 'foo', 'size' => 100], |
|
['name' => 'bar', 'size' => 200], |
|
]; |
|
|
|
$ids = $parent->children->pluck('id', 'name'); |
|
|
|
$this->syncManager->uniqueBy(['name'])->syncRelated($parent, 'children', $newItems); |
|
|
|
$parent->refresh(); |
|
expect($parent->children->count())->toBe(2); |
|
|
|
$item1 = $parent->children->firstWhere('name', 'foo'); |
|
expect($item1->id)->toBe($ids['foo']); // id should not change |
|
expect($item1->size)->toBe(100); // size should be updated |
|
|
|
$item2 = $parent->children->firstWhere('name', 'bar'); |
|
expect($item2->id)->toBe($ids['bar']); |
|
expect($item2->size)->toBe(200); |
|
}); |
|
|
|
it('only updates records where specified columns have changed', function () { |
|
$parent = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 10, 'color' => 'red', 'material' => 'wood'], |
|
['name' => 'bar', 'size' => 20, 'color' => 'green', 'material' => 'wood'], |
|
['name' => 'baz', 'size' => 30, 'color' => 'blue', 'material' => 'wood'], |
|
]) |
|
->create(); |
|
|
|
$newItems = [ |
|
['name' => 'foo', 'size' => 100, 'color' => 'red', 'material' => 'wood'], |
|
['name' => 'bar', 'size' => 200, 'color' => 'purple', 'material' => 'wood'], |
|
['name' => 'baz', 'size' => 300, 'color' => 'blue', 'material' => 'metal'], |
|
]; |
|
|
|
$ids = $parent->children->pluck('id', 'name'); |
|
|
|
$this->syncManager |
|
->uniqueBy('name') |
|
->ifChanged('color', 'material') |
|
->syncRelated($parent, 'children', $newItems); |
|
|
|
$parent->refresh(); |
|
expect($parent->children->count())->toBe(3); |
|
|
|
// item 1 should not have updated as neither color or material changed |
|
$item1 = $parent->children->firstWhere('name', 'foo'); |
|
expect($item1->size)->toBe(10); |
|
|
|
// item 2 should have updated as color changed |
|
$item2 = $parent->children->firstWhere('name', 'bar'); |
|
expect($item2->id)->toBe($ids['bar']); |
|
expect($item2->size)->toBe(200); |
|
expect($item2->color)->toBe('purple'); |
|
expect($item2->material)->toBe('wood'); |
|
|
|
// item 3 should have updated as material changed |
|
$item3 = $parent->children->firstWhere('name', 'baz'); |
|
expect($item3->id)->toBe($ids['baz']); |
|
expect($item3->size)->toBe(300); |
|
expect($item3->color)->toBe('blue'); |
|
expect($item3->material)->toBe('metal'); |
|
}); |
|
|
|
it('deletes missing items', function () { |
|
$parent = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 10], |
|
['name' => 'bar', 'size' => 20], |
|
]) |
|
->create(); |
|
|
|
$newItems = [ |
|
['name' => 'foo', 'size' => 100], |
|
]; |
|
|
|
$this->syncManager->uniqueBy(['name'])->syncRelated($parent, 'children', $newItems); |
|
|
|
$parent->refresh(); |
|
expect($parent->children->count())->toBe(1); |
|
|
|
expect($parent->children->first()->name)->toBe('foo'); |
|
}); |
|
|
|
it('inserts, updates, and deletes items correctly with no unique columns', function () { |
|
$parent = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 10], |
|
['name' => 'bar', 'size' => 20], |
|
]) |
|
->create(); |
|
|
|
$ids = $parent->children->pluck('id', 'name'); |
|
|
|
$newItems = $parent->children->sortBy('name')->toArray(); |
|
array_shift($newItems); |
|
$newItems[0]['size'] = 100; |
|
$newItems[] = ['parent_id' => $parent->id, 'name' => 'baz', 'size' => 300]; // insert |
|
|
|
$result = $this->syncManager->syncRelated($parent, 'children', $newItems); |
|
|
|
// ensure the returned results look correct |
|
expect($result)->toBeInstanceOf(Collection::class); |
|
expect($result->count())->toBe(2); |
|
expect($result->first()->id)->toBe($ids['foo']); |
|
expect($result->first()->name)->toBe('foo'); |
|
expect($result->first()->size)->toBe(100); |
|
expect($result->last()->name)->toBe('baz'); |
|
expect($result->last()->size)->toBe(300); |
|
|
|
// ensure the database entries are correct |
|
$parent->refresh(); |
|
expect($parent->children->count())->toBe(2); |
|
|
|
$item1 = $parent->children->firstWhere('name', 'foo'); |
|
expect($item1->id)->toBe($ids['foo']); // id should not change |
|
expect($item1->size)->toBe(100); // size should be updated |
|
|
|
$item2 = $parent->children->firstWhere('name', 'baz'); |
|
expect($item2->id)->not->toBeNull(); |
|
expect($item2->size)->toBe(300); |
|
}); |
|
|
|
it('inserts, updates, and deletes items correctly with unique columns', function () { |
|
$parent = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 10], |
|
['name' => 'bar', 'size' => 20], |
|
]) |
|
->create(); |
|
$ids = $parent->children->pluck('id', 'name'); |
|
|
|
// these should not impact the existing items with a different parent_id part of the unique id |
|
$irrelevant = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 50], |
|
['name' => 'bar', 'size' => 60], |
|
['name' => 'baz', 'size' => 70], |
|
]) |
|
->create(); |
|
$irrelevantIds = $irrelevant->children->pluck('id', 'name'); |
|
|
|
$newItems = [ |
|
['parent_id' => $parent->id, 'name' => 'foo', 'size' => 100], // update |
|
['parent_id' => $parent->id, 'name' => 'baz', 'size' => 30], // insert |
|
]; |
|
|
|
$this->syncManager->uniqueBy('parent_id', 'name') |
|
->syncRelated($parent, 'children', $newItems); |
|
|
|
$parent->refresh(); |
|
expect($parent->children->count())->toBe(2); |
|
|
|
$item1 = $parent->children->firstWhere('name', 'foo'); |
|
expect($item1->id)->toBe($ids['foo']); // id should not change |
|
expect($item1->size)->toBe(100); // size should be updated |
|
|
|
$item2 = $parent->children->firstWhere('name', 'baz'); |
|
expect($item2->id)->not->toBe($irrelevantIds['baz']); // it should be a new item - not the existing item from another parent |
|
expect($item2->size)->toBe(30); |
|
|
|
// check the irrelevant items are not impacted |
|
$irrelevant->refresh(); |
|
expect($irrelevant->children->count())->toBe(3); |
|
|
|
$item1 = $irrelevant->children->firstWhere('name', 'foo'); |
|
expect($item1->id)->toBe($irrelevantIds['foo']); // id should not change |
|
expect($item1->size)->toBe(50); // size should not change |
|
|
|
$item2 = $irrelevant->children->firstWhere('name', 'bar'); |
|
expect($item2->id)->toBe($irrelevantIds['bar']); // id should not change |
|
expect($item2->size)->toBe(60); // size should not change |
|
|
|
$item3 = $irrelevant->children->firstWhere('name', 'baz'); |
|
expect($item3->id)->toBe($irrelevantIds['baz']); // id should not change |
|
expect($item3->size)->toBe(70); // size should not change |
|
}); |
|
|
|
it('returns new models in correct order', function () { |
|
$parent = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 10], |
|
['name' => 'bar', 'size' => 20], |
|
]) |
|
->create(); |
|
|
|
$newItems = [ |
|
['parent_id' => $parent->id, 'name' => 'baz', 'size' => 30], // insert |
|
['parent_id' => $parent->id, 'name' => 'foo', 'size' => 100], // update |
|
]; |
|
|
|
$models = $this->syncManager->uniqueBy('name') |
|
->syncRelated($parent, 'children', $newItems); |
|
|
|
expect($models)->toHaveCount(2); |
|
|
|
expect($models->pluck('name')->all())->toBe(['baz', 'foo']); |
|
}); |
|
}); |
|
|
|
describe('sync', function () { |
|
it('inserts, updates, and deletes items correctly', function () { |
|
$parent1 = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 10], |
|
['name' => 'bar', 'size' => 20], |
|
]) |
|
->create(); |
|
$ids1 = $parent1->children->pluck('id', 'name'); |
|
|
|
// these should not impact the existing items with a different parent_id part of the unique id |
|
$parent2 = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 50], |
|
['name' => 'bar', 'size' => 60], |
|
['name' => 'baz', 'size' => 70], |
|
]) |
|
->create(); |
|
$ids2 = $parent2->children->pluck('id', 'name'); |
|
|
|
$existingItems = [ |
|
...$parent1->children, |
|
...$parent2->children, |
|
]; |
|
|
|
// prepare updates, deleting parent1.bar, and parent2.foo |
|
$newItems = [ |
|
['parent_id' => $parent1->id, 'name' => 'foo', 'size' => 100], // update |
|
['parent_id' => $parent1->id, 'name' => 'baz', 'size' => 300], // insert |
|
['parent_id' => $parent2->id, 'name' => 'bar', 'size' => 600], // update |
|
['parent_id' => $parent2->id, 'name' => 'baz', 'size' => 700], // update |
|
['parent_id' => $parent2->id, 'name' => 'qux', 'size' => 800], // insert |
|
]; |
|
|
|
$this->syncManager->forModel(SyncManagerChild::class) |
|
->uniqueBy(['parent_id', 'name']) |
|
->sync($existingItems, $newItems); |
|
|
|
|
|
// parent1 looks correct |
|
$parent1->refresh(); |
|
expect($parent1->children->count())->toBe(2); |
|
$this->assertDatabaseHas('sync_manager_child', ['id' => $ids1['foo'], 'parent_id' => $parent1->id, 'name' => 'foo', 'size' => 100]); |
|
$this->assertDatabaseMissing('sync_manager_child', ['id' => $ids1['bar']]); |
|
$this->assertDatabaseHas('sync_manager_child', ['parent_id' => $parent1->id, 'name' => 'baz', 'size' => 300]); |
|
|
|
// parent2 looks correct |
|
$parent2->refresh(); |
|
expect($parent2->children->count())->toBe(3); |
|
$this->assertDatabaseMissing('sync_manager_child', ['id' => $ids2['foo']]); |
|
$this->assertDatabaseHas('sync_manager_child', ['id' => $ids2['bar'], 'parent_id' => $parent2->id, 'name' => 'bar', 'size' => 600]); |
|
$this->assertDatabaseHas('sync_manager_child', ['id' => $ids2['baz'], 'parent_id' => $parent2->id, 'name' => 'baz', 'size' => 700]); |
|
$this->assertDatabaseHas('sync_manager_child', ['parent_id' => $parent2->id, 'name' => 'qux', 'size' => 800]); |
|
}); |
|
}); |
|
|
|
describe('mass sync', function () { |
|
it('inserts, updates, and deletes items correctly', function () { |
|
$parent1 = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 10], |
|
['name' => 'bar', 'size' => 20], |
|
]) |
|
->create(); |
|
$ids1 = $parent1->children->pluck('id', 'name'); |
|
|
|
// these should not impact the existing items with a different parent_id part of the unique id |
|
$parent2 = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 50], |
|
['name' => 'bar', 'size' => 60], |
|
['name' => 'baz', 'size' => 70], |
|
]) |
|
->create(); |
|
$ids2 = $parent2->children->pluck('id', 'name'); |
|
|
|
$existingItems = [ |
|
...$parent1->children, |
|
...$parent2->children, |
|
]; |
|
|
|
// prepare updates, deleting parent1.bar, and parent2.foo |
|
$newItems = [ |
|
['parent_id' => $parent1->id, 'name' => 'foo', 'size' => 100], // update |
|
['parent_id' => $parent1->id, 'name' => 'baz', 'size' => 300], // insert |
|
['parent_id' => $parent2->id, 'name' => 'bar', 'size' => 600], // update |
|
['parent_id' => $parent2->id, 'name' => 'baz', 'size' => 700], // update |
|
['parent_id' => $parent2->id, 'name' => 'qux', 'size' => 800], // insert |
|
]; |
|
|
|
$this->syncManager->forModel(SyncManagerChild::class) |
|
->uniqueBy(['parent_id', 'name']) |
|
->massSync($existingItems, $newItems); |
|
|
|
// parent1 looks correct |
|
$parent1->refresh(); |
|
expect($parent1->children->count())->toBe(2); |
|
$this->assertDatabaseHas('sync_manager_child', ['id' => $ids1['foo'], 'parent_id' => $parent1->id, 'name' => 'foo', 'size' => 100]); |
|
$this->assertDatabaseMissing('sync_manager_child', ['id' => $ids1['bar']]); |
|
$this->assertDatabaseHas('sync_manager_child', ['parent_id' => $parent1->id, 'name' => 'baz', 'size' => 300]); |
|
|
|
// parent2 looks correct |
|
$parent2->refresh(); |
|
expect($parent2->children->count())->toBe(3); |
|
$this->assertDatabaseMissing('sync_manager_child', ['id' => $ids2['foo']]); |
|
$this->assertDatabaseHas('sync_manager_child', ['id' => $ids2['bar'], 'parent_id' => $parent2->id, 'name' => 'bar', 'size' => 600]); |
|
$this->assertDatabaseHas('sync_manager_child', ['id' => $ids2['baz'], 'parent_id' => $parent2->id, 'name' => 'baz', 'size' => 700]); |
|
$this->assertDatabaseHas('sync_manager_child', ['parent_id' => $parent2->id, 'name' => 'qux', 'size' => 800]); |
|
}); |
|
|
|
it('only updates specified columns if set', function () { |
|
$parent1 = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 10, 'color' => 'red'], |
|
['name' => 'bar', 'size' => 20, 'color' => 'green'], |
|
]) |
|
->create(); |
|
|
|
$ids1 = $parent1->children->pluck('id', 'name'); |
|
|
|
$newItems = [ |
|
['parent_id' => $parent1->id, 'name' => 'foo', 'size' => 100, 'color' => 'red'], // color not changed - don't update |
|
['parent_id' => $parent1->id, 'name' => 'bar', 'size' => 200, 'color' => 'blue'], // color changed - updated |
|
['parent_id' => $parent1->id, 'name' => 'baz', 'size' => 300, 'color' => 'green'], // insert |
|
]; |
|
|
|
$this->syncManager->forModel(SyncManagerChild::class) |
|
->uniqueBy(['parent_id', 'name']) |
|
->massSync($parent1->children, $newItems, ['color']); |
|
|
|
$parent1->refresh(); |
|
expect($parent1->children->count())->toBe(3); |
|
$this->assertDatabaseHas('sync_manager_child', ['id' => $ids1['foo'], 'parent_id' => $parent1->id, 'name' => 'foo', 'size' => 10, 'color' => 'red']); // no changes as color not updated |
|
$this->assertDatabaseHas('sync_manager_child', ['id' => $ids1['bar'], 'parent_id' => $parent1->id, 'name' => 'bar', 'size' => 20, 'color' => 'blue']); // color only updated |
|
$this->assertDatabaseHas('sync_manager_child', ['parent_id' => $parent1->id, 'name' => 'baz', 'size' => 300, 'color' => 'green']); // inserted so all columns set |
|
}); |
|
|
|
it('only updates relevant records if using ifChanged', function () { |
|
$parent1 = (new SyncManagerParentFactory()) |
|
->withChildren([ |
|
['name' => 'foo', 'size' => 10, 'color' => 'red', 'updated_at' => '2020-01-01 00:00:00'], |
|
['name' => 'bar', 'size' => 20, 'color' => 'green'], |
|
]) |
|
->create(); |
|
|
|
$ids1 = $parent1->children->pluck('id', 'name'); |
|
|
|
$newItems = [ |
|
['parent_id' => $parent1->id, 'name' => 'foo', 'size' => 100, 'color' => 'red'], // color not changed - don't update |
|
['parent_id' => $parent1->id, 'name' => 'bar', 'size' => 200, 'color' => 'blue'], // color changed - updated |
|
]; |
|
|
|
$this->syncManager->forModel(SyncManagerChild::class) |
|
->uniqueBy('parent_id', 'name') |
|
->ifChanged('color') |
|
->massSync($parent1->children, $newItems); |
|
|
|
$parent1->refresh(); |
|
expect($parent1->children->count())->toBe(2); |
|
$this->assertDatabaseHas('sync_manager_child', ['id' => $ids1['foo'], 'parent_id' => $parent1->id, 'name' => 'foo', 'size' => 10, 'color' => 'red', 'updated_at' => '2020-01-01 00:00:00']); // not updated as color didn't change |
|
$this->assertDatabaseHas('sync_manager_child', ['id' => $ids1['bar'], 'parent_id' => $parent1->id, 'name' => 'bar', 'size' => 200, 'color' => 'blue']); // updated as color changed |
|
}); |
|
}); |
|
|
|
describe('force delete', function () { |
|
class SyncManagerSoftChild extends SyncManagerChild { |
|
use HasFactory; |
|
use SoftDeletes; |
|
|
|
protected $table = 'sync_manager_soft_child'; |
|
} |
|
|
|
class SyncManagerSoftChildFactory extends SyncManagerChildFactory { |
|
protected $model = SyncManagerSoftChild::class; |
|
} |
|
|
|
beforeEach(function () { |
|
DB::statement(' |
|
CREATE TEMPORARY TABLE IF NOT EXISTS sync_manager_soft_child ( |
|
id INTEGER PRIMARY KEY, |
|
parent_id INTEGER, |
|
name TEXT, |
|
size INTEGER, |
|
deleted_at TIMESTAMP DEFAULT NULl, |
|
FOREIGN KEY (parent_id) REFERENCES sync_manager_parent(id) |
|
) |
|
'); |
|
|
|
DB::statement('DELETE FROM sync_manager_soft_child'); |
|
}); |
|
|
|
it('should soft delete records by default', function () { |
|
$parent = (new SyncManagerParentFactory())->create(); |
|
|
|
$existing1 = ['parent_id' => $parent->id, 'name' => 'foo', 'size' => 10]; |
|
$child1 = (new SyncManagerSoftChildFactory())->create($existing1); |
|
|
|
$this->syncManager |
|
->forModel(SyncManagerSoftChild::class) |
|
->uniqueBy(['parent_id', 'name']) |
|
->sync([$child1], []); |
|
|
|
$child = SyncManagerSoftChild::withTrashed()->find($child1->id); |
|
expect($child)->toBeInstanceOf(SyncManagerSoftChild::class); |
|
expect($child->deleted_at)->not->toBeNull(); |
|
}); |
|
|
|
it('should force delete records when requested', function () { |
|
$parent = (new SyncManagerParentFactory())->create(); |
|
|
|
$existing1 = ['parent_id' => $parent->id, 'name' => 'foo', 'size' => 10]; |
|
$child1 = (new SyncManagerSoftChildFactory())->create($existing1); |
|
|
|
// it should be there to start |
|
expect(SyncManagerSoftChild::withTrashed()->find($child1->id)) |
|
->toBeInstanceOf(SyncManagerSoftChild::class); |
|
|
|
$this->syncManager |
|
->forModel(SyncManagerSoftChild::class) |
|
->uniqueBy(['parent_id', 'name']) |
|
->force() |
|
->sync([$child1], []); |
|
|
|
// but gone after |
|
$child = SyncManagerSoftChild::withTrashed()->find($child1->id); |
|
expect($child)->toBeNull(); |
|
}); |
|
}); |