From 1632ca57fc82f014b120f3507b159e27974e411a Mon Sep 17 00:00:00 2001 From: Erik Gaal Date: Mon, 17 Dec 2018 17:55:21 +0100 Subject: [PATCH 1/3] Add regression test for setting relation via pivot field --- tests/Feature/AttachPivotMutationTest.php | 32 +++++++++++++++++++ tests/Stubs/Models/Role.php | 2 +- tests/Stubs/Models/User.php | 2 +- tests/Stubs/Models/UserRole.php | 5 +++ tests/Stubs/Schemas/UserRoleSchema.php | 9 +++++- ...00_00_00_000005_create_role_user_table.php | 5 +++ 6 files changed, 52 insertions(+), 3 deletions(-) diff --git a/tests/Feature/AttachPivotMutationTest.php b/tests/Feature/AttachPivotMutationTest.php index b6dcb699..b839dcc8 100644 --- a/tests/Feature/AttachPivotMutationTest.php +++ b/tests/Feature/AttachPivotMutationTest.php @@ -67,6 +67,38 @@ public function it_lets_you_attach_pivot_ids_with_pivot_data() ]); } + /** @test */ + public function it_lets_you_attach_pivot_ids_with_pivot_relation_data() + { + $user = factory(User::class)->create(); + $role = factory(Role::class)->create(); + $tag = factory(Tag::class)->create(); + $this->actingAs($user); + + $query = ' + mutation { + attachCustomRolesOnUser(id: "'.$user->id.'", input: [ + { id: "'.$role->id.'", customPivot: {tagId: "'.$tag->id.'" } } + ]) { + id + customRoles { + customPivot { + comment + } + } + } + } + '; + + $response = $this->json('GET', '/graphql', ['query' => $query]); + $response->assertJsonKey('id'); + $this->assertDatabaseHas('role_user', [ + 'user_id' => '1', + 'role_id' => '1', + 'tag_id' => '1', + ]); + } + /** @test */ public function it_lets_you_attach_pivot_ids_with_pivot_data_inversed() { diff --git a/tests/Stubs/Models/Role.php b/tests/Stubs/Models/Role.php index a866d344..acb97f90 100644 --- a/tests/Stubs/Models/Role.php +++ b/tests/Stubs/Models/Role.php @@ -19,7 +19,7 @@ public function users() { return $this->belongsToMany(User::class) ->using(UserRole::class) - ->withPivot('comment') + ->withPivot(['comment', 'tag_id']) ->withTimestamps(); } } diff --git a/tests/Stubs/Models/User.php b/tests/Stubs/Models/User.php index a54e4510..ade95594 100644 --- a/tests/Stubs/Models/User.php +++ b/tests/Stubs/Models/User.php @@ -40,7 +40,7 @@ public function customRoles() return $this->belongsToMany(Role::class) ->as('customPivot') ->using(UserRole::class) - ->withPivot('comment') + ->withPivot(['comment', 'tag_id']) ->withTimestamps(); } } diff --git a/tests/Stubs/Models/UserRole.php b/tests/Stubs/Models/UserRole.php index d17f2767..23c92e66 100644 --- a/tests/Stubs/Models/UserRole.php +++ b/tests/Stubs/Models/UserRole.php @@ -11,4 +11,9 @@ class UserRole extends Pivot public $fillable = [ 'comment', ]; + + public function tag() + { + $this->belongsTo(Tag::class); + } } diff --git a/tests/Stubs/Schemas/UserRoleSchema.php b/tests/Stubs/Schemas/UserRoleSchema.php index 4b66118d..d4ccbbfb 100644 --- a/tests/Stubs/Schemas/UserRoleSchema.php +++ b/tests/Stubs/Schemas/UserRoleSchema.php @@ -13,7 +13,14 @@ class UserRoleSchema extends ModelSchema public function fields(): array { return [ - 'comment' => Field::string(), + 'comment' => Field::string()->nullable(), + ]; + } + + public function relations(): array + { + return [ + 'tag' => Field::model(TagSchema::class)->nullable(), ]; } } diff --git a/tests/migrations/0000_00_00_000005_create_role_user_table.php b/tests/migrations/0000_00_00_000005_create_role_user_table.php index d56e3e0b..1d3a41ae 100644 --- a/tests/migrations/0000_00_00_000005_create_role_user_table.php +++ b/tests/migrations/0000_00_00_000005_create_role_user_table.php @@ -13,6 +13,7 @@ public function up() Schema::create('role_user', function ($table) { $table->unsignedInteger('user_id'); $table->unsignedInteger('role_id'); + $table->unsignedInteger('tag_id')->nullable(); $table->text('comment')->nullable(); $table->timestamps(); @@ -23,6 +24,10 @@ public function up() $table->foreign('role_id') ->references('id')->on('roles') ->onDelete('cascade'); + + $table->foreign('tag_id') + ->references('id')->on('tags') + ->onDelete('cascade'); }); } From ac357e19bc157cc7aedb2fc3a6f34f0672e23754 Mon Sep 17 00:00:00 2001 From: Erik Gaal Date: Tue, 18 Dec 2018 17:01:45 +0100 Subject: [PATCH 2/3] Implement relational pivot data --- .../Concerns/InteractsWithRelations.php | 28 +++++++++++++++++-- src/Mutations/AttachPivotMutation.php | 25 +++++++---------- tests/IntegrationTest.php | 1 + tests/Stubs/Models/UserRole.php | 6 +--- tests/Stubs/Policies/UserRolePolicy.php | 21 ++++++++++++++ 5 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 tests/Stubs/Policies/UserRolePolicy.php diff --git a/src/Eloquent/Concerns/InteractsWithRelations.php b/src/Eloquent/Concerns/InteractsWithRelations.php index d3cf987c..25013202 100644 --- a/src/Eloquent/Concerns/InteractsWithRelations.php +++ b/src/Eloquent/Concerns/InteractsWithRelations.php @@ -236,23 +236,45 @@ protected function fillHasManyRelation(Relations\HasMany $relation, array $value * * @param Relations\BelongsToMany $relation * @param array $data + * @param bool $detaching * @return void */ - public function connectBelongsToManyRelation(Relations\BelongsToMany $relation, array $data) + public function connectBelongsToManyRelation(Relations\BelongsToMany $relation, array $data, $detaching = true) { $accessor = $relation->getPivotAccessor(); $relatedKey = $relation->getRelated()->getKeyName(); + $pivotClass = $relation->getPivotClass(); + $pivotInstance = resolve($pivotClass); + $data = collect($data)->mapWithKeys(function ($data, $key) use ($accessor, $relatedKey) { if (! is_array($data)) { return [$key => $data]; } return [$data[$relatedKey] => $data[$accessor]]; + })->map(function ($attributes) use ($pivotClass) { + if (! is_array($attributes)) { + return $attributes; + } + + $instance = new $pivotClass; + $pivotSchema = $this->registry->getSchemaForModel($instance); + $pivotSchema->fill($attributes); + + return $pivotSchema->getInstance()->getAttributes(); }); - $this->queue(function () use ($relation, $data) { - $relation->sync($data); + $this->queue(function () use ($pivotInstance, $data, $detaching, $relation) { + if ($pivotInstance) { + $pivotInstance::unguard(); + } + + $relation->sync($data, $detaching); + + if ($pivotInstance) { + $pivotInstance::reguard(); + } }); } diff --git a/src/Mutations/AttachPivotMutation.php b/src/Mutations/AttachPivotMutation.php index ee2c973f..b881a14e 100644 --- a/src/Mutations/AttachPivotMutation.php +++ b/src/Mutations/AttachPivotMutation.php @@ -3,6 +3,7 @@ namespace Bakery\Mutations; use Bakery\Fields\Field; +use Illuminate\Support\Facades\DB; use Illuminate\Database\Eloquent\Model; use GraphQL\Type\Definition\ResolveInfo; use Bakery\Types\Concerns\InteractsWithPivot; @@ -74,26 +75,20 @@ protected function getRelation(Model $model): BelongsToMany */ public function resolve($root, array $args, $context, ResolveInfo $info): Model { + $input = $args['input']; $model = $this->findOrFail($root, $args, $context, $info); - $relation = $this->getRelation($model); - $modelSchema = $this->registry->getSchemaForModel($model); - $permission = 'set'.studly_case($relation->getRelationName()); - $modelSchema->authorize($permission, $model); + return DB::transaction(function () use ($input, $model) { + $modelSchema = $this->registry->getSchemaForModel($model); - $relatedKey = $relation->getRelated()->getKeyName(); - $accessor = $relation->getPivotAccessor(); + $relation = $this->getRelation($model); - $data = collect($args['input'])->mapWithKeys(function ($data, $key) use ($accessor, $relatedKey) { - if (! is_array($data)) { - return [$key => $data]; - } + $permission = 'set'.studly_case($relation->getRelationName()); + $modelSchema->authorize($permission, $model); + $modelSchema->connectBelongsToManyRelation($relation, $input, false); + $modelSchema->save(); - return [$data[$relatedKey] => $data[$accessor]]; + return $modelSchema->getInstance(); }); - - $relation->attach($data); - - return $model; } } diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index 290e0077..bb62a747 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -41,6 +41,7 @@ protected function setUp() $this->gate = resolve(Gate::class); $this->gate->policy(Models\User::class, Policies\UserPolicy::class); $this->gate->policy(Models\Role::class, Policies\RolePolicy::class); + $this->gate->policy(Models\UserRole::class, Policies\UserRolePolicy::class); $this->gate->policy(Models\Article::class, Policies\ArticlePolicy::class); $this->gate->policy(Models\Phone::class, Policies\PhonePolicy::class); $this->gate->policy(Models\Comment::class, Policies\CommentPolicy::class); diff --git a/tests/Stubs/Models/UserRole.php b/tests/Stubs/Models/UserRole.php index 23c92e66..5af54632 100644 --- a/tests/Stubs/Models/UserRole.php +++ b/tests/Stubs/Models/UserRole.php @@ -8,12 +8,8 @@ class UserRole extends Pivot { protected $primaryKey = null; - public $fillable = [ - 'comment', - ]; - public function tag() { - $this->belongsTo(Tag::class); + return $this->belongsTo(Tag::class); } } diff --git a/tests/Stubs/Policies/UserRolePolicy.php b/tests/Stubs/Policies/UserRolePolicy.php new file mode 100644 index 00000000..5c0edc88 --- /dev/null +++ b/tests/Stubs/Policies/UserRolePolicy.php @@ -0,0 +1,21 @@ + Date: Tue, 18 Dec 2018 17:25:35 +0100 Subject: [PATCH 3/3] Add another test for creating pivot data --- tests/Feature/AttachPivotMutationTest.php | 35 +++++++++++++++++++++++ tests/Stubs/Policies/UserRolePolicy.php | 5 ++++ 2 files changed, 40 insertions(+) diff --git a/tests/Feature/AttachPivotMutationTest.php b/tests/Feature/AttachPivotMutationTest.php index b839dcc8..b9415c34 100644 --- a/tests/Feature/AttachPivotMutationTest.php +++ b/tests/Feature/AttachPivotMutationTest.php @@ -99,6 +99,41 @@ public function it_lets_you_attach_pivot_ids_with_pivot_relation_data() ]); } + /** @test */ + public function it_lets_you_attach_pivot_with_create() + { + $user = factory(User::class)->create(); + $role = factory(Role::class)->create(); + $this->actingAs($user); + + $query = ' + mutation { + attachCustomRolesOnUser(id: "'.$user->id.'", input: [ + { id: "'.$role->id.'", customPivot: {tag: {name: "foobar"} } } + ]) { + id + customRoles { + customPivot { + comment + } + } + } + } + '; + + $response = $this->json('GET', '/graphql', ['query' => $query]); + $response->assertJsonKey('id'); + $this->assertDatabaseHas('role_user', [ + 'user_id' => '1', + 'role_id' => '1', + 'tag_id' => '1', + ]); + + $this->assertDatabaseHas('tags', [ + 'name' => 'foobar', + ]); + } + /** @test */ public function it_lets_you_attach_pivot_ids_with_pivot_data_inversed() { diff --git a/tests/Stubs/Policies/UserRolePolicy.php b/tests/Stubs/Policies/UserRolePolicy.php index 5c0edc88..7c7de063 100644 --- a/tests/Stubs/Policies/UserRolePolicy.php +++ b/tests/Stubs/Policies/UserRolePolicy.php @@ -18,4 +18,9 @@ public function setTag(): bool { return true; } + + public function createTag(): bool + { + return true; + } }