Skip to content

Commit 5387d54

Browse files
authored
[Fix] BelongsToMany sync does't use configured keys (mongodb#2667)
* Merge testSyncBelongsToMany into testBelongsToManySync; * modelKeys replaced with parseIds in sync method; * Add test for handling a collection in sync method;
1 parent 0cdba6a commit 5387d54

File tree

4 files changed

+108
-42
lines changed

4 files changed

+108
-42
lines changed

src/Relations/BelongsToMany.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ protected function setWhere()
7676
{
7777
$foreign = $this->getForeignKey();
7878

79-
$this->query->where($foreign, '=', $this->parent->getKey());
79+
$this->query->where($foreign, '=', $this->parent->{$this->parentKey});
8080

8181
return $this;
8282
}
@@ -116,7 +116,7 @@ public function sync($ids, $detaching = true)
116116
];
117117

118118
if ($ids instanceof Collection) {
119-
$ids = $ids->modelKeys();
119+
$ids = $this->parseIds($ids);
120120
} elseif ($ids instanceof Model) {
121121
$ids = $this->parseIds($ids);
122122
}
@@ -190,10 +190,10 @@ public function attach($id, array $attributes = [], $touch = true)
190190

191191
$query = $this->newRelatedQuery();
192192

193-
$query->whereIn($this->related->getKeyName(), (array) $id);
193+
$query->whereIn($this->relatedKey, (array) $id);
194194

195195
// Attach the new parent id to the related model.
196-
$query->push($this->foreignPivotKey, $this->parent->getKey(), true);
196+
$query->push($this->foreignPivotKey, $this->parent->{$this->parentKey}, true);
197197
}
198198

199199
// Attach the new ids to the parent model.

tests/Models/Experience.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MongoDB\Laravel\Tests\Models;
6+
7+
use MongoDB\Laravel\Eloquent\Model as Eloquent;
8+
9+
class Experience extends Eloquent
10+
{
11+
protected $connection = 'mongodb';
12+
protected $collection = 'experiences';
13+
protected static $unguarded = true;
14+
15+
protected $casts = ['years' => 'int'];
16+
17+
public function skillsWithCustomRelatedKey()
18+
{
19+
return $this->belongsToMany(Skill::class, relatedKey: 'cskill_id');
20+
}
21+
22+
public function skillsWithCustomParentKey()
23+
{
24+
return $this->belongsToMany(Skill::class, parentKey: 'cexperience_id');
25+
}
26+
}

tests/Models/Skill.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MongoDB\Laravel\Tests\Models;
6+
7+
use MongoDB\Laravel\Eloquent\Model as Eloquent;
8+
9+
class Skill extends Eloquent
10+
{
11+
protected $connection = 'mongodb';
12+
protected $collection = 'skills';
13+
protected static $unguarded = true;
14+
}

tests/RelationsTest.php

Lines changed: 64 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@
66

77
use Illuminate\Database\Eloquent\Collection;
88
use Mockery;
9+
use MongoDB\BSON\ObjectId;
910
use MongoDB\Laravel\Tests\Models\Address;
1011
use MongoDB\Laravel\Tests\Models\Book;
1112
use MongoDB\Laravel\Tests\Models\Client;
13+
use MongoDB\Laravel\Tests\Models\Experience;
1214
use MongoDB\Laravel\Tests\Models\Group;
1315
use MongoDB\Laravel\Tests\Models\Item;
1416
use MongoDB\Laravel\Tests\Models\Photo;
1517
use MongoDB\Laravel\Tests\Models\Role;
18+
use MongoDB\Laravel\Tests\Models\Skill;
1619
use MongoDB\Laravel\Tests\Models\Soft;
1720
use MongoDB\Laravel\Tests\Models\User;
1821

@@ -30,6 +33,8 @@ public function tearDown(): void
3033
Role::truncate();
3134
Group::truncate();
3235
Photo::truncate();
36+
Skill::truncate();
37+
Experience::truncate();
3338
}
3439

3540
public function testHasMany(): void
@@ -255,36 +260,6 @@ public function testBelongsToMany(): void
255260
$this->assertCount(1, $client->users);
256261
}
257262

258-
public function testSyncBelongsToMany()
259-
{
260-
$user = User::create(['name' => 'John Doe']);
261-
262-
$first = Client::query()->create(['name' => 'Hans']);
263-
$second = Client::query()->create(['name' => 'Thomas']);
264-
265-
$user->load('clients');
266-
self::assertEmpty($user->clients);
267-
268-
$user->clients()->sync($first);
269-
270-
$user->load('clients');
271-
self::assertCount(1, $user->clients);
272-
self::assertTrue($user->clients->first()->is($first));
273-
274-
$user->clients()->sync($second);
275-
276-
$user->load('clients');
277-
self::assertCount(1, $user->clients);
278-
self::assertTrue($user->clients->first()->is($second));
279-
280-
$user->clients()->syncWithoutDetaching($first);
281-
282-
$user->load('clients');
283-
self::assertCount(2, $user->clients);
284-
self::assertTrue($user->clients->first()->is($first));
285-
self::assertTrue($user->clients->last()->is($second));
286-
}
287-
288263
public function testBelongsToManyAttachesExistingModels(): void
289264
{
290265
$user = User::create(['name' => 'John Doe', 'client_ids' => ['1234523']]);
@@ -327,20 +302,27 @@ public function testBelongsToManyAttachesExistingModels(): void
327302
public function testBelongsToManySync(): void
328303
{
329304
// create test instances
330-
$user = User::create(['name' => 'John Doe']);
331-
$client1 = Client::create(['name' => 'Pork Pies Ltd.'])->_id;
332-
$client2 = Client::create(['name' => 'Buffet Bar Inc.'])->_id;
305+
$user = User::create(['name' => 'Hans Thomas']);
306+
$client1 = Client::create(['name' => 'Pork Pies Ltd.']);
307+
$client2 = Client::create(['name' => 'Buffet Bar Inc.']);
333308

334309
// Sync multiple
335-
$user->clients()->sync([$client1, $client2]);
310+
$user->clients()->sync([$client1->_id, $client2->_id]);
336311
$this->assertCount(2, $user->clients);
337312

338-
// Refresh user
339-
$user = User::where('name', '=', 'John Doe')->first();
313+
// Sync single wrapped by an array
314+
$user->clients()->sync([$client1->_id]);
315+
$user->load('clients');
316+
317+
$this->assertCount(1, $user->clients);
318+
self::assertTrue($user->clients->first()->is($client1));
319+
320+
// Sync single model
321+
$user->clients()->sync($client2);
322+
$user->load('clients');
340323

341-
// Sync single
342-
$user->clients()->sync([$client1]);
343324
$this->assertCount(1, $user->clients);
325+
self::assertTrue($user->clients->first()->is($client2));
344326
}
345327

346328
public function testBelongsToManyAttachArray(): void
@@ -366,6 +348,50 @@ public function testBelongsToManyAttachEloquentCollection(): void
366348
$this->assertCount(2, $user->clients);
367349
}
368350

351+
public function testBelongsToManySyncEloquentCollectionWithCustomRelatedKey(): void
352+
{
353+
$experience = Experience::create(['years' => '5']);
354+
$skill1 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'PHP']);
355+
$skill2 = Skill::create(['cskill_id' => (string) (new ObjectId()), 'name' => 'Laravel']);
356+
$collection = new Collection([$skill1, $skill2]);
357+
358+
$experience = Experience::query()->find($experience->id);
359+
$experience->skillsWithCustomRelatedKey()->sync($collection);
360+
$this->assertCount(2, $experience->skillsWithCustomRelatedKey);
361+
362+
self::assertIsString($skill1->cskill_id);
363+
self::assertContains($skill1->cskill_id, $experience->skillsWithCustomRelatedKey->pluck('cskill_id'));
364+
365+
self::assertIsString($skill2->cskill_id);
366+
self::assertContains($skill2->cskill_id, $experience->skillsWithCustomRelatedKey->pluck('cskill_id'));
367+
368+
$skill1->refresh();
369+
self::assertIsString($skill1->_id);
370+
self::assertNotContains($skill1->_id, $experience->skillsWithCustomRelatedKey->pluck('cskill_id'));
371+
372+
$skill2->refresh();
373+
self::assertIsString($skill2->_id);
374+
self::assertNotContains($skill2->_id, $experience->skillsWithCustomRelatedKey->pluck('cskill_id'));
375+
}
376+
377+
public function testBelongsToManySyncEloquentCollectionWithCustomParentKey(): void
378+
{
379+
$experience = Experience::create(['cexperience_id' => (string) (new ObjectId()), 'years' => '5']);
380+
$skill1 = Skill::create(['name' => 'PHP']);
381+
$skill2 = Skill::create(['name' => 'Laravel']);
382+
$collection = new Collection([$skill1, $skill2]);
383+
384+
$experience = Experience::query()->find($experience->id);
385+
$experience->skillsWithCustomParentKey()->sync($collection);
386+
$this->assertCount(2, $experience->skillsWithCustomParentKey);
387+
388+
self::assertIsString($skill1->_id);
389+
self::assertContains($skill1->_id, $experience->skillsWithCustomParentKey->pluck('_id'));
390+
391+
self::assertIsString($skill2->_id);
392+
self::assertContains($skill2->_id, $experience->skillsWithCustomParentKey->pluck('_id'));
393+
}
394+
369395
public function testBelongsToManySyncAlreadyPresent(): void
370396
{
371397
$user = User::create(['name' => 'John Doe']);

0 commit comments

Comments
 (0)