diff --git a/src/Concerns/Relations/PerformsRelationOperations.php b/src/Concerns/Relations/PerformsRelationOperations.php new file mode 100644 index 0000000..3d0c3b7 --- /dev/null +++ b/src/Concerns/Relations/PerformsRelationOperations.php @@ -0,0 +1,168 @@ +make(QueryBuilder::class, ['resource' => $relation->resource()]) + ->applyMutation($mutation, $attributes); + + $this->resource()->authorizeToAttach($model, $toPerformActionModel); + + $model + ->{$relation->relation}() + ->syncWithoutDetaching( + [ + $toPerformActionModel->getKey() => $mutation['pivot'] ?? [], + ] + ); + } + + /** + * @param Model $model + * @param Relation $relation + * @param array $mutation + * @param array $attributes + * + * @return void + */ + public function update(Model $model, Relation $relation, array $mutation = [], array $attributes = []) + { + $toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) + ->mutations($mutation, $attributes); + + foreach ($toPerformActionModels as $toPerformActionModel) { + $this->resource()->authorizeToAttach($model, $toPerformActionModel); + } + + $model->{$relation->relation}() + ->syncWithoutDetaching( + collect($toPerformActionModels) + ->mapWithKeys( + fn (Model $toPerformActionModel) => [$toPerformActionModel->getKey() => $mutation['pivot'] ?? []] + )->toArray() + ); + } + + /** + * @param Model $model + * @param Relation $relation + * @param array $mutation + * @param array $attributes + * + * @return void + */ + public function attach(Model $model, Relation $relation, array $mutation = [], array $attributes = []) + { + $toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) + ->mutations($mutation, $attributes); + + foreach ($toPerformActionModels as $toPerformActionModel) { + $this->resource()->authorizeToAttach($model, $toPerformActionModel); + } + + $model->{$relation->relation}() + ->attach( + collect($toPerformActionModels) + ->mapWithKeys( + fn (Model $toPerformActionModel) => [$toPerformActionModel->getKey() => $mutation['pivot'] ?? []] + )->toArray() + ); + } + + /** + * @param Model $model + * @param Relation $relation + * @param array $mutation + * @param array $attributes + * + * @return void + */ + public function detach(Model $model, Relation $relation, array $mutation = [], array $attributes = []) + { + $toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) + ->mutations($mutation, $attributes); + + foreach ($toPerformActionModels as $toPerformActionModel) { + $this->resource()->authorizeToDetach($model, $toPerformActionModel); + } + + $model->{$relation->relation}()->detach($toPerformActionModels); + } + + /** + * @param Model $model + * @param Relation $relation + * @param array $mutation + * @param array $attributes + * + * @return void + */ + public function toggle(Model $model, Relation $relation, array $mutation = [], array $attributes = []) + { + $toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) + ->mutations($mutation, $attributes); + + $results = $model->{$relation->relation}() + ->toggle( + collect($toPerformActionModels) + ->mapWithKeys( + fn (Model $toPerformActionModel) => [$toPerformActionModel->getKey() => $mutation['pivot'] ?? []] + )->toArray(), + ); + + foreach ($results['attached'] as $attached) { + $this->resource()->authorizeToAttach($model, $relation->resource()::$model::find($attached)); + } + + foreach ($results['detached'] as $detached) { + $this->resource()->authorizeToDetach($model, $relation->resource()::$model::find($detached)); + } + } + + /** + * @param Model $model + * @param Relation $relation + * @param array $mutation + * @param array $attributes + * @param bool $withoutDetaching + * + * @return void + */ + public function sync(Model $model, Relation $relation, array $mutation = [], array $attributes = [], $withoutDetaching = false) + { + $toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) + ->mutations($mutation, $attributes); + + $results = $model->{$relation->relation}() + ->sync( + collect($toPerformActionModels) + ->mapWithKeys( + fn (Model $toPerformActionModel) => [$toPerformActionModel->getKey() => $mutation['pivot'] ?? []] + )->toArray(), + $withoutDetaching + ); + + foreach ($results['attached'] as $attached) { + $this->resource()->authorizeToAttach($model, $relation->resource()::$model::find($attached)); + } + + foreach ($results['detached'] as $detached) { + $this->resource()->authorizeToDetach($model, $relation->resource()::$model::find($detached)); + } + } +} diff --git a/src/Query/Traits/PerformMutation.php b/src/Query/Traits/PerformMutation.php index 575321a..9461e93 100644 --- a/src/Query/Traits/PerformMutation.php +++ b/src/Query/Traits/PerformMutation.php @@ -49,14 +49,14 @@ public function mutate(array $parameters = []) * * @return Model The mutated model. */ - public function applyMutation(array $mutation = [], $attributes = []) + public function applyMutation(array $mutation = [], $attributes = [], $key = null) { $allAttributes = array_merge($attributes, $mutation['attributes'] ?? []); if ($mutation['operation'] === 'create') { $model = $this->resource::newModel(); } else { - $model = $this->resource::newModel()::findOrFail($mutation['key']); + $model = $this->resource::newModel()::query()->findOrFail($key ?? $mutation['key']); } if ($mutation['operation'] === 'create') { @@ -74,6 +74,26 @@ public function applyMutation(array $mutation = [], $attributes = []) ); } + /** + * Apply a mutation to the model based on the provided mutation parameters. + * + * @param array $mutation An array of mutation parameters. + * @param array $attributes Additional attributes to apply to the model. + * + * @return array The mutated model. + */ + public function mutations(array $mutation = [], $attributes = []) + { + $mutations = []; + $keys = $mutation['keys'] ?? [$mutation['key']]; + + foreach ($keys as $key) { + $mutations[] = $this->applyMutation($mutation, $attributes, $key); + } + + return $mutations; + } + /** * Mutate the model by applying attributes and relations. * diff --git a/src/Relations/BelongsToMany.php b/src/Relations/BelongsToMany.php index 8374f77..c2d428d 100644 --- a/src/Relations/BelongsToMany.php +++ b/src/Relations/BelongsToMany.php @@ -3,8 +3,9 @@ namespace Lomkit\Rest\Relations; use Illuminate\Database\Eloquent\Model; +use InvalidArgumentException; use Lomkit\Rest\Concerns\Relations\HasPivotFields; -use Lomkit\Rest\Contracts\QueryBuilder; +use Lomkit\Rest\Concerns\Relations\PerformsRelationOperations; use Lomkit\Rest\Contracts\RelationResource; use Lomkit\Rest\Http\Resource; use Lomkit\Rest\Relations\Traits\HasMultipleResults; @@ -14,6 +15,7 @@ class BelongsToMany extends Relation implements RelationResource { use HasPivotFields; use HasMultipleResults; + use PerformsRelationOperations; /** * Define validation rules for the BelongsToMany relation. @@ -46,82 +48,15 @@ public function rules(Resource $resource, string $prefix) public function afterMutating(Model $model, Relation $relation, array $mutationRelations) { foreach ($mutationRelations[$relation->relation] as $mutationRelation) { - if ($mutationRelation['operation'] === 'detach') { - $toDetachModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation); - - $this->resource()->authorizeToDetach($model, $toDetachModel); - - $model - ->{$relation->relation}() - ->detach( - $toDetachModel->getKey() - ); - } elseif ($mutationRelation['operation'] === 'attach') { - $toAttachModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation); - - $this->resource()->authorizeToAttach($model, $toAttachModel); - - $model - ->{$relation->relation}() - ->attach( - [ - $toAttachModel->getKey() => $mutationRelation['pivot'] ?? [], - ] - ); - } elseif ($mutationRelation['operation'] === 'toggle') { - $results = $model - ->{$relation->relation}() - ->toggle( - [ - app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation) - ->getKey() => $mutationRelation['pivot'] ?? [], - ] - ); - - foreach ($results['attached'] as $attached) { - $this->resource()->authorizeToAttach($model, $relation->resource()::$model::find($attached)); - } - - foreach ($results['detached'] as $detached) { - $this->resource()->authorizeToDetach($model, $relation->resource()::$model::find($detached)); - } - } elseif ($mutationRelation['operation'] === 'sync') { - $results = $model - ->{$relation->relation}() - ->sync( - [ - app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation) - ->getKey() => $mutationRelation['pivot'] ?? [], - ], - !isset($mutationRelation['without_detaching']) || !$mutationRelation['without_detaching'] - ); - - foreach ($results['attached'] as $attached) { - $this->resource()->authorizeToAttach($model, $relation->resource()::$model::find($attached)); - } - - foreach ($results['detached'] as $detached) { - $this->resource()->authorizeToDetach($model, $relation->resource()::$model::find($detached)); - } - } elseif (in_array($mutationRelation['operation'], ['create', 'update'])) { - $toAttachModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation); - - $this->resource()->authorizeToAttach($model, $toAttachModel); - - $model - ->{$relation->relation}() - ->syncWithoutDetaching( - [ - $toAttachModel - ->getKey() => $mutationRelation['pivot'] ?? [], - ] - ); - } + match ($mutationRelation['operation']) { + 'create' => $this->create($model, $relation, $mutationRelation), + 'update' => $this->update($model, $relation, $mutationRelation), + 'attach' => $this->attach($model, $relation, $mutationRelation), + 'detach' => $this->detach($model, $relation, $mutationRelation), + 'toggle' => $this->toggle($model, $relation, $mutationRelation), + 'sync' => $this->sync($model, $relation, $mutationRelation, withoutDetaching: !isset($mutationRelation['without_detaching']) || !$mutationRelation['without_detaching']), + default => throw new InvalidArgumentException("Unknown operation: {$mutationRelation['operation']}"), + }; } } } diff --git a/src/Relations/HasMany.php b/src/Relations/HasMany.php index 169971b..4abdd87 100644 --- a/src/Relations/HasMany.php +++ b/src/Relations/HasMany.php @@ -25,18 +25,23 @@ public function afterMutating(Model $model, Relation $relation, array $mutationR $model->{$relation->relation}()->getForeignKeyName() => $mutationRelation['operation'] === 'detach' ? null : $model->{$relation->relation}()->getParentKey(), ]; - $toPerformActionModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation, $attributes); + if ($mutationRelation['operation'] === 'create') { + $toPerformActionModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) + ->applyMutation($mutationRelation, $attributes); - switch ($mutationRelation['operation']) { - case 'create': - case 'update': - case 'attach': - $this->resource()->authorizeToAttach($model, $toPerformActionModel); - break; - case 'detach': + $this->resource()->authorizeToAttach($model, $toPerformActionModel); + continue; + } + + $toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) + ->mutations($mutationRelation, $attributes); + + foreach ($toPerformActionModels as $toPerformActionModel) { + if ($mutationRelation['operation'] === 'detach') { $this->resource()->authorizeToDetach($model, $toPerformActionModel); - break; + } else { + $this->resource()->authorizeToAttach($model, $toPerformActionModel); + } } } } diff --git a/src/Relations/MorphMany.php b/src/Relations/MorphMany.php index 2b4e888..375ef5d 100644 --- a/src/Relations/MorphMany.php +++ b/src/Relations/MorphMany.php @@ -26,18 +26,23 @@ public function afterMutating(Model $model, Relation $relation, array $mutationR $model->{$relation->relation}()->getMorphType() => $model::class, ]; - $toPerformActionModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation, $attributes); + if ($mutationRelation['operation'] === 'create') { + $toPerformActionModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) + ->applyMutation($mutationRelation, $attributes); - switch ($mutationRelation['operation']) { - case 'create': - case 'update': - case 'attach': - $this->resource()->authorizeToAttach($model, $toPerformActionModel); - break; - case 'detach': + $this->resource()->authorizeToAttach($model, $toPerformActionModel); + continue; + } + + $toPerformActionModels = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) + ->mutations($mutationRelation, $attributes); + + foreach ($toPerformActionModels as $toPerformActionModel) { + if ($mutationRelation['operation'] === 'detach') { $this->resource()->authorizeToDetach($model, $toPerformActionModel); - break; + } else { + $this->resource()->authorizeToAttach($model, $toPerformActionModel); + } } } } diff --git a/src/Relations/MorphToMany.php b/src/Relations/MorphToMany.php index a8fa9be..8c74719 100644 --- a/src/Relations/MorphToMany.php +++ b/src/Relations/MorphToMany.php @@ -3,8 +3,9 @@ namespace Lomkit\Rest\Relations; use Illuminate\Database\Eloquent\Model; +use InvalidArgumentException; use Lomkit\Rest\Concerns\Relations\HasPivotFields; -use Lomkit\Rest\Contracts\QueryBuilder; +use Lomkit\Rest\Concerns\Relations\PerformsRelationOperations; use Lomkit\Rest\Contracts\RelationResource; use Lomkit\Rest\Http\Resource; use Lomkit\Rest\Relations\Traits\HasMultipleResults; @@ -14,6 +15,7 @@ class MorphToMany extends MorphRelation implements RelationResource { use HasPivotFields; use HasMultipleResults; + use PerformsRelationOperations; /** * Define validation rules for the MorphToMany relation. @@ -46,82 +48,15 @@ public function rules(Resource $resource, string $prefix) public function afterMutating(Model $model, Relation $relation, array $mutationRelations) { foreach ($mutationRelations[$relation->relation] as $mutationRelation) { - if ($mutationRelation['operation'] === 'detach') { - $toDetachModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation); - - $this->resource()->authorizeToDetach($model, $toDetachModel); - - $model - ->{$relation->relation}() - ->detach( - $toDetachModel->getKey() - ); - } elseif ($mutationRelation['operation'] === 'attach') { - $toAttachModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation); - - $this->resource()->authorizeToAttach($model, $toAttachModel); - - $model - ->{$relation->relation}() - ->attach( - [ - $toAttachModel->getKey() => $mutationRelation['pivot'] ?? [], - ] - ); - } elseif ($mutationRelation['operation'] === 'toggle') { - $results = $model - ->{$relation->relation}() - ->toggle( - [ - app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation) - ->getKey() => $mutationRelation['pivot'] ?? [], - ] - ); - - foreach ($results['attached'] as $attached) { - $this->resource()->authorizeToAttach($model, $relation->resource()::$model::find($attached)); - } - - foreach ($results['detached'] as $detached) { - $this->resource()->authorizeToDetach($model, $relation->resource()::$model::find($detached)); - } - } elseif ($mutationRelation['operation'] === 'sync') { - $results = $model - ->{$relation->relation}() - ->sync( - [ - app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation) - ->getKey() => $mutationRelation['pivot'] ?? [], - ], - !isset($mutationRelation['without_detaching']) || !$mutationRelation['without_detaching'] - ); - - foreach ($results['attached'] as $attached) { - $this->resource()->authorizeToAttach($model, $relation->resource()::$model::find($attached)); - } - - foreach ($results['detached'] as $detached) { - $this->resource()->authorizeToDetach($model, $relation->resource()::$model::find($detached)); - } - } elseif (in_array($mutationRelation['operation'], ['create', 'update'])) { - $toAttachModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation); - - $this->resource()->authorizeToAttach($model, $toAttachModel); - - $model - ->{$relation->relation}() - ->syncWithoutDetaching( - [ - $toAttachModel - ->getKey() => $mutationRelation['pivot'] ?? [], - ] - ); - } + match ($mutationRelation['operation']) { + 'create' => $this->create($model, $relation, $mutationRelation), + 'update' => $this->update($model, $relation, $mutationRelation), + 'attach' => $this->attach($model, $relation, $mutationRelation), + 'detach' => $this->detach($model, $relation, $mutationRelation), + 'toggle' => $this->toggle($model, $relation, $mutationRelation), + 'sync' => $this->sync($model, $relation, $mutationRelation, withoutDetaching: !isset($mutationRelation['without_detaching']) || !$mutationRelation['without_detaching']), + default => throw new InvalidArgumentException("Unknown operation: {$mutationRelation['operation']}"), + }; } } } diff --git a/src/Relations/MorphedByMany.php b/src/Relations/MorphedByMany.php index da86254..5f0dcea 100644 --- a/src/Relations/MorphedByMany.php +++ b/src/Relations/MorphedByMany.php @@ -3,8 +3,9 @@ namespace Lomkit\Rest\Relations; use Illuminate\Database\Eloquent\Model; +use InvalidArgumentException; use Lomkit\Rest\Concerns\Relations\HasPivotFields; -use Lomkit\Rest\Contracts\QueryBuilder; +use Lomkit\Rest\Concerns\Relations\PerformsRelationOperations; use Lomkit\Rest\Contracts\RelationResource; use Lomkit\Rest\Http\Resource; use Lomkit\Rest\Relations\Traits\HasMultipleResults; @@ -14,6 +15,7 @@ class MorphedByMany extends MorphRelation implements RelationResource { use HasPivotFields; use HasMultipleResults; + use PerformsRelationOperations; /** * Define validation rules for the MorphedByMany relation. @@ -46,82 +48,15 @@ public function rules(Resource $resource, string $prefix) public function afterMutating(Model $model, Relation $relation, array $mutationRelations) { foreach ($mutationRelations[$relation->relation] as $mutationRelation) { - if ($mutationRelation['operation'] === 'detach') { - $toDetachModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation); - - $this->resource()->authorizeToDetach($model, $toDetachModel); - - $model - ->{$relation->relation}() - ->detach( - $toDetachModel->getKey() - ); - } elseif ($mutationRelation['operation'] === 'attach') { - $toAttachModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation); - - $this->resource()->authorizeToAttach($model, $toAttachModel); - - $model - ->{$relation->relation}() - ->attach( - [ - $toAttachModel->getKey() => $mutationRelation['pivot'] ?? [], - ] - ); - } elseif ($mutationRelation['operation'] === 'toggle') { - $results = $model - ->{$relation->relation}() - ->toggle( - [ - app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation) - ->getKey() => $mutationRelation['pivot'] ?? [], - ] - ); - - foreach ($results['attached'] as $attached) { - $this->resource()->authorizeToAttach($model, $relation->resource()::$model::find($attached)); - } - - foreach ($results['detached'] as $detached) { - $this->resource()->authorizeToDetach($model, $relation->resource()::$model::find($detached)); - } - } elseif ($mutationRelation['operation'] === 'sync') { - $results = $model - ->{$relation->relation}() - ->sync( - [ - app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation) - ->getKey() => $mutationRelation['pivot'] ?? [], - ], - !isset($mutationRelation['without_detaching']) || !$mutationRelation['without_detaching'] - ); - - foreach ($results['attached'] as $attached) { - $this->resource()->authorizeToAttach($model, $relation->resource()::$model::find($attached)); - } - - foreach ($results['detached'] as $detached) { - $this->resource()->authorizeToDetach($model, $relation->resource()::$model::find($detached)); - } - } elseif (in_array($mutationRelation['operation'], ['create', 'update'])) { - $toAttachModel = app()->make(QueryBuilder::class, ['resource' => $relation->resource()]) - ->applyMutation($mutationRelation); - - $this->resource()->authorizeToAttach($model, $toAttachModel); - - $model - ->{$relation->relation}() - ->syncWithoutDetaching( - [ - $toAttachModel - ->getKey() => $mutationRelation['pivot'] ?? [], - ] - ); - } + match ($mutationRelation['operation']) { + 'create' => $this->create($model, $relation, $mutationRelation), + 'update' => $this->update($model, $relation, $mutationRelation), + 'attach' => $this->attach($model, $relation, $mutationRelation), + 'detach' => $this->detach($model, $relation, $mutationRelation), + 'toggle' => $this->toggle($model, $relation, $mutationRelation), + 'sync' => $this->sync($model, $relation, $mutationRelation, withoutDetaching: !isset($mutationRelation['without_detaching']) || !$mutationRelation['without_detaching']), + default => throw new InvalidArgumentException("Unknown operation: {$mutationRelation['operation']}"), + }; } } } diff --git a/src/Rules/MutateRules.php b/src/Rules/MutateRules.php index 7c5682b..e0600e9 100644 --- a/src/Rules/MutateRules.php +++ b/src/Rules/MutateRules.php @@ -5,7 +5,6 @@ use Closure; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Contracts\Validation\ValidatorAwareRule; -use Illuminate\Http\Client\Request; use Illuminate\Validation\Rule; use Illuminate\Validation\Validator; use Lomkit\Rest\Http\Requests\RestRequest; @@ -94,13 +93,21 @@ public function validate(string $attribute, mixed $value, Closure $fail): void new ArrayWith($this->resource->getFields($this->request)), ], $attribute.'.key' => [ - 'required_if:'.$attribute.'.operation,update', - 'required_if:'.$attribute.'.operation,attach', - 'required_if:'.$attribute.'.operation,detach', - 'required_if:'.$attribute.'.operation,toggle', - 'required_if:'.$attribute.'.operation,sync', + fn (string $attribute, mixed $value, Closure $fail) => is_array($value) && $fail('The key field must not be an array.'), + 'prohibits:'.$attribute.'.keys', 'prohibited_if:'.$attribute.'.operation,create', 'exists:'.$this->resource::newModel()->getTable().','.$this->resource::newModel()->getKeyName(), + new OperationDependentRequiredKey($attribute.'.keys'), + ], + $attribute.'.keys' => [ + 'array', + ...(!$this->relation?->hasMultipleEntries() ? ['prohibited'] : []), + 'prohibits:'.$attribute.'.key', + 'prohibited_if:'.$attribute.'.operation,create', + new OperationDependentRequiredKey($attribute.'.key'), + ], + $attribute.'.keys.*' => [ + 'exists:'.$this->resource::newModel()->getTable().','.$this->resource::newModel()->getKeyName(), ], $attribute.'.without_detaching' => [ 'boolean', diff --git a/src/Rules/OperationDependentRequiredKey.php b/src/Rules/OperationDependentRequiredKey.php new file mode 100644 index 0000000..9f3dd25 --- /dev/null +++ b/src/Rules/OperationDependentRequiredKey.php @@ -0,0 +1,65 @@ + + */ + protected $data = []; + + /** + * Set the data under validation. + * + * @param array $data + */ + public function setData(array $data): static + { + $this->data = $data; + + return $this; + } + + public function __construct( + public string $otherField + ) { + } + + public function validate(string $attribute, mixed $value, Closure $fail): void + { + $arrayDot = Arr::dot($this->data); + $numericSegments = array_filter(explode('.', $attribute), 'is_numeric'); + $index = end($numericSegments); + + $key = Arr::get($this->data, $attribute); + $keys = Arr::get($this->data, Str::replace('*', $index, $this->otherField)); + + $operation = $arrayDot[ + Str::of($attribute) + ->beforeLast('.') + ->beforeLast('.') + ->append('.'.$index.'.operation') + ->toString() + ] ?? null; + + if (in_array($operation, ['update', 'attach', 'detach', 'toggle', 'sync']) && !$key && !$keys) { + $fail("The $attribute field is required when the operation is '$operation' and the $this->otherField field is missing."); + } + } +} diff --git a/tests/Feature/Controllers/MutateCreateMorphOperationsTest.php b/tests/Feature/Controllers/MutateCreateMorphOperationsTest.php index 1d321ce..023ed09 100644 --- a/tests/Feature/Controllers/MutateCreateMorphOperationsTest.php +++ b/tests/Feature/Controllers/MutateCreateMorphOperationsTest.php @@ -298,6 +298,59 @@ public function test_creating_a_resource_with_attaching_morph_many_relation(): v ); } + public function test_creating_a_resource_with_attaching_multiple_morph_many_relation(): void + { + $modelToCreate = ModelFactory::new()->makeOne(); + $morphManyRelationToAttach1 = MorphManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + $morphManyRelationToAttach2 = MorphManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'create', + 'attributes' => [ + 'name' => $modelToCreate->name, + 'number' => $modelToCreate->number, + ], + 'relations' => [ + 'morphManyRelation' => [ + [ + 'operation' => 'attach', + 'keys' => [$morphManyRelationToAttach1->getKey(), $morphManyRelationToAttach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [$modelToCreate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('created.0'))->morphManyRelation()->count(), + 2 + ); + } + public function test_creating_a_resource_with_updating_morph_many_relation(): void { $modelToCreate = ModelFactory::new()->makeOne(); @@ -353,6 +406,70 @@ public function test_creating_a_resource_with_updating_morph_many_relation(): vo ); } + public function test_creating_a_resource_with_updating_multiple_morph_many_relation(): void + { + $modelToCreate = ModelFactory::new()->makeOne(); + $morphManyRelationToAttach1 = MorphManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + $morphManyRelationToAttach2 = MorphManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'create', + 'attributes' => [ + 'name' => $modelToCreate->name, + 'number' => $modelToCreate->number, + ], + 'relations' => [ + 'morphManyRelation' => [ + [ + 'operation' => 'update', + 'keys' => [$morphManyRelationToAttach1->getKey(), $morphManyRelationToAttach2->getKey()], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [$modelToCreate], + ); + + // Here we test that the number has been modified on the relation + $this->assertEquals( + $morphManyRelationToAttach1->fresh()->number, + 5001 + ); + $this->assertEquals( + $morphManyRelationToAttach2->fresh()->number, + 5001 + ); + + // Here we test that the model is correctly linked + $this->assertEquals( + Model::find($response->json('created.0'))->morphManyRelation()->count(), + 2 + ); + } + public function test_creating_a_resource_with_creating_morph_one_relation(): void { $modelToCreate = ModelFactory::new()->makeOne(); @@ -730,6 +847,59 @@ public function test_creating_a_resource_with_attaching_morph_to_many_relation() ); } + public function test_creating_a_resource_with_attaching_multiple_morph_to_many_relation(): void + { + $modelToCreate = ModelFactory::new()->makeOne(); + $morphToManyRelationToAttach1 = MorphToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $morphToManyRelationToAttach2 = MorphToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphToManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'create', + 'attributes' => [ + 'name' => $modelToCreate->name, + 'number' => $modelToCreate->number, + ], + 'relations' => [ + 'morphToManyRelation' => [ + [ + 'operation' => 'attach', + 'keys' => [$morphToManyRelationToAttach1->getKey(), $morphToManyRelationToAttach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [$modelToCreate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('created.0'))->morphToManyRelation()->count(), + 2 + ); + } + public function test_creating_a_resource_with_updating_morph_to_many_relation(): void { $modelToCreate = ModelFactory::new()->makeOne(); @@ -785,6 +955,70 @@ public function test_creating_a_resource_with_updating_morph_to_many_relation(): ); } + public function test_creating_a_resource_with_updating_multiple_morph_to_many_relation(): void + { + $modelToCreate = ModelFactory::new()->makeOne(); + $morphToManyRelationToAttach1 = MorphToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $morphToManyRelationToAttach2 = MorphToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphToManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'create', + 'attributes' => [ + 'name' => $modelToCreate->name, + 'number' => $modelToCreate->number, + ], + 'relations' => [ + 'morphToManyRelation' => [ + [ + 'operation' => 'update', + 'keys' => [$morphToManyRelationToAttach1->getKey(), $morphToManyRelationToAttach2->getKey()], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [$modelToCreate], + ); + + // Here we test that the number has been modified on the relation + $this->assertEquals( + $morphToManyRelationToAttach1->fresh()->number, + 5001 + ); + $this->assertEquals( + $morphToManyRelationToAttach2->fresh()->number, + 5001 + ); + + // Here we test that the model is correctly linked + $this->assertEquals( + Model::find($response->json('created.0'))->morphToManyRelation()->count(), + 2 + ); + } + public function test_creating_a_resource_with_creating_morph_by_many_relation(): void { $modelToCreate = ModelFactory::new()->makeOne(); @@ -1022,6 +1256,59 @@ public function test_creating_a_resource_with_attaching_morphed_by_many_relation ); } + public function test_creating_a_resource_with_attaching_multiple_morphed_by_many_relation(): void + { + $modelToCreate = ModelFactory::new()->makeOne(); + $morphedByManyRelationToAttach1 = MorphedByManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $morphedByManyRelationToAttach2 = MorphedByManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'create', + 'attributes' => [ + 'name' => $modelToCreate->name, + 'number' => $modelToCreate->number, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'attach', + 'keys' => [$morphedByManyRelationToAttach1->getKey(), $morphedByManyRelationToAttach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [$modelToCreate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('created.0'))->morphedByManyRelation()->count(), + 2 + ); + } + public function test_creating_a_resource_with_updating_morphed_by_many_relation(): void { $modelToCreate = ModelFactory::new()->makeOne(); @@ -1076,4 +1363,68 @@ public function test_creating_a_resource_with_updating_morphed_by_many_relation( 1 ); } + + public function test_creating_a_resource_with_updating_multiple_morphed_by_many_relation(): void + { + $modelToCreate = ModelFactory::new()->makeOne(); + $morphedByManyRelationToAttach1 = MorphedByManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $morphedByManyRelationToAttach2 = MorphedByManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'create', + 'attributes' => [ + 'name' => $modelToCreate->name, + 'number' => $modelToCreate->number, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'update', + 'keys' => [$morphedByManyRelationToAttach1->getKey(), $morphedByManyRelationToAttach2->getKey()], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [$modelToCreate], + ); + + // Here we test that the number has been modified on the relation + $this->assertEquals( + $morphedByManyRelationToAttach1->fresh()->number, + 5001 + ); + $this->assertEquals( + $morphedByManyRelationToAttach2->fresh()->number, + 5001 + ); + + // Here we test that the model is correctly linked + $this->assertEquals( + Model::find($response->json('created.0'))->morphedByManyRelation()->count(), + 2 + ); + } } diff --git a/tests/Feature/Controllers/MutateCreateOperationsTest.php b/tests/Feature/Controllers/MutateCreateOperationsTest.php index 3952dc3..073d25e 100644 --- a/tests/Feature/Controllers/MutateCreateOperationsTest.php +++ b/tests/Feature/Controllers/MutateCreateOperationsTest.php @@ -621,6 +621,59 @@ public function test_creating_a_resource_with_attaching_has_many_relation(): voi ); } + public function test_creating_a_resource_with_attaching_multiple_has_many_relation(): void + { + $modelToCreate = ModelFactory::new()->makeOne(); + $hasManyRelationToAttach1 = HasManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + $hasManyRelationToAttach2 = HasManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(HasManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'create', + 'attributes' => [ + 'name' => $modelToCreate->name, + 'number' => $modelToCreate->number, + ], + 'relations' => [ + 'hasManyRelation' => [ + [ + 'operation' => 'attach', + 'keys' => [$hasManyRelationToAttach1->getKey(), $hasManyRelationToAttach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [$modelToCreate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('created.0'))->hasManyRelation()->count(), + 2 + ); + } + public function test_creating_a_resource_with_updating_has_many_relation(): void { $modelToCreate = ModelFactory::new()->makeOne(); @@ -676,6 +729,70 @@ public function test_creating_a_resource_with_updating_has_many_relation(): void ); } + public function test_creating_a_resource_with_updating_multiple_has_many_relation(): void + { + $modelToCreate = ModelFactory::new()->makeOne(); + $hasManyRelationToUpdate1 = HasManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + $hasManyRelationToUpdate2 = HasManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(HasManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'create', + 'attributes' => [ + 'name' => $modelToCreate->name, + 'number' => $modelToCreate->number, + ], + 'relations' => [ + 'hasManyRelation' => [ + [ + 'operation' => 'update', + 'keys' => [$hasManyRelationToUpdate1->getKey(), $hasManyRelationToUpdate2->getKey()], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [$modelToCreate], + ); + + // Here we test that the number has been modified on the relation + $this->assertEquals( + $hasManyRelationToUpdate1->fresh()->number, + 5001 + ); + $this->assertEquals( + $hasManyRelationToUpdate2->fresh()->number, + 5001 + ); + + // Here we test that the model is correctly linked + $this->assertEquals( + Model::find($response->json('created.0'))->hasManyRelation()->count(), + 2 + ); + } + public function test_creating_a_resource_with_creating_has_one_relation(): void { $modelToCreate = ModelFactory::new()->makeOne(); @@ -1193,6 +1310,59 @@ public function test_creating_a_resource_with_attaching_belongs_to_many_relation ); } + public function test_creating_a_resource_with_attaching_multiple_belongs_to_many_relation(): void + { + $modelToCreate = ModelFactory::new()->makeOne(); + $belongsToManyRelationToAttach1 = BelongsToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $belongsToManyRelationToAttach2 = BelongsToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(BelongsToManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'create', + 'attributes' => [ + 'name' => $modelToCreate->name, + 'number' => $modelToCreate->number, + ], + 'relations' => [ + 'belongsToManyRelation' => [ + [ + 'operation' => 'attach', + 'keys' => [$belongsToManyRelationToAttach1->getKey(), $belongsToManyRelationToAttach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [$modelToCreate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('created.0'))->belongsToManyRelation()->count(), + 2 + ); + } + public function test_creating_a_resource_with_attaching_belongs_to_many_relation_with_pivot_fields(): void { $modelToCreate = ModelFactory::new()->makeOne(); @@ -1264,6 +1434,86 @@ public function test_creating_a_resource_with_attaching_belongs_to_many_relation ); } + public function test_creating_a_resource_with_attaching_multiple_belongs_to_many_relation_with_pivot_fields(): void + { + $modelToCreate = ModelFactory::new()->makeOne(); + $belongsToManyRelationToAttach1 = BelongsToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $belongsToManyRelationToAttach2 = BelongsToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $belongsToManyRelationToAttach3 = BelongsToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(BelongsToManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'create', + 'attributes' => [ + 'name' => $modelToCreate->name, + 'number' => $modelToCreate->number, + ], + 'relations' => [ + 'belongsToManyRelation' => [ + [ + 'operation' => 'attach', + 'keys' => [$belongsToManyRelationToAttach1->getKey(), $belongsToManyRelationToAttach3->getKey()], + 'pivot' => [ + 'number' => 20, + ], + ], + [ + 'operation' => 'attach', + 'key' => $belongsToManyRelationToAttach2->getKey(), + 'pivot' => [ + 'number' => 30, + ], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [$modelToCreate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('created.0'))->belongsToManyRelation()->count(), + 3 + ); + $this->assertEquals( + Model::find($response->json('created.0'))->belongsToManyRelation[0]->belongs_to_many_pivot->number, + 20 + ); + $this->assertEquals( + Model::find($response->json('created.0'))->belongsToManyRelation[2]->belongs_to_many_pivot->number, + 20 + ); + $this->assertEquals( + Model::find($response->json('created.0'))->belongsToManyRelation[1]->belongs_to_many_pivot->number, + 30 + ); + } + public function test_creating_a_resource_with_updating_belongs_to_many_relation(): void { $modelToCreate = ModelFactory::new()->makeOne(); @@ -1319,6 +1569,70 @@ public function test_creating_a_resource_with_updating_belongs_to_many_relation( ); } + public function test_creating_a_resource_with_updating_multiple_belongs_to_many_relation(): void + { + $modelToCreate = ModelFactory::new()->makeOne(); + $belongsToManyRelationToUpdate1 = BelongsToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $belongsToManyRelationToUpdate2 = BelongsToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(BelongsToManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'create', + 'attributes' => [ + 'name' => $modelToCreate->name, + 'number' => $modelToCreate->number, + ], + 'relations' => [ + 'belongsToManyRelation' => [ + [ + 'operation' => 'update', + 'keys' => [$belongsToManyRelationToUpdate1->getKey(), $belongsToManyRelationToUpdate2->getKey()], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [$modelToCreate], + ); + + // Here we test that the number has been modified on the relation + $this->assertEquals( + $belongsToManyRelationToUpdate1->fresh()->number, + 5001 + ); + $this->assertEquals( + $belongsToManyRelationToUpdate2->fresh()->number, + 5001 + ); + + // Here we test that the model is correctly linked + $this->assertEquals( + Model::find($response->json('created.0'))->belongsToManyRelation()->count(), + 2 + ); + } + public function test_creating_a_resource_with_updating_belongs_to_many_relation_with_pivot_fields(): void { $modelToCreate = ModelFactory::new()->makeOne(); @@ -1401,4 +1715,100 @@ public function test_creating_a_resource_with_updating_belongs_to_many_relation_ 30 ); } + + public function test_creating_a_resource_with_updating_multiple_belongs_to_many_relation_with_pivot_fields(): void + { + $modelToCreate = ModelFactory::new()->makeOne(); + $belongsToManyRelationToUpdate1 = BelongsToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $belongsToManyRelationToUpdate2 = BelongsToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $belongsToManyRelationToUpdate3 = BelongsToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(BelongsToManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'create', + 'attributes' => [ + 'name' => $modelToCreate->name, + 'number' => $modelToCreate->number, + ], + 'relations' => [ + 'belongsToManyRelation' => [ + [ + 'operation' => 'update', + 'keys' => [$belongsToManyRelationToUpdate1->getKey(), $belongsToManyRelationToUpdate3->getKey()], + 'pivot' => [ + 'number' => 20, + ], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + ], + [ + 'operation' => 'update', + 'key' => $belongsToManyRelationToUpdate2->getKey(), + 'pivot' => [ + 'number' => 30, + ], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [$modelToCreate], + ); + + // Here we test that the number has been modified on the relation + $this->assertEquals( + $belongsToManyRelationToUpdate1->fresh()->number, + 5001 + ); + $this->assertEquals( + $belongsToManyRelationToUpdate2->fresh()->number, + 5001 + ); + $this->assertEquals( + $belongsToManyRelationToUpdate3->fresh()->number, + 5001 + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('created.0'))->belongsToManyRelation()->count(), + 3 + ); + $this->assertEquals( + Model::find($response->json('created.0'))->belongsToManyRelation()->where('belongs_to_many_relation_id', $belongsToManyRelationToUpdate1->getKey())->first()->belongs_to_many_pivot->number, + 20 + ); + $this->assertEquals( + Model::find($response->json('created.0'))->belongsToManyRelation()->where('belongs_to_many_relation_id', $belongsToManyRelationToUpdate2->getKey())->first()->belongs_to_many_pivot->number, + 30 + ); + $this->assertEquals( + Model::find($response->json('created.0'))->belongsToManyRelation()->where('belongs_to_many_relation_id', $belongsToManyRelationToUpdate3->getKey())->first()->belongs_to_many_pivot->number, + 20 + ); + } } diff --git a/tests/Feature/Controllers/MutateUpdateMorphOperationsTest.php b/tests/Feature/Controllers/MutateUpdateMorphOperationsTest.php index b92d3cd..64dd846 100644 --- a/tests/Feature/Controllers/MutateUpdateMorphOperationsTest.php +++ b/tests/Feature/Controllers/MutateUpdateMorphOperationsTest.php @@ -375,6 +375,61 @@ public function test_updating_a_resource_with_attaching_morph_many_relation(): v ); } + public function test_updating_a_resource_with_attaching_multiple_morph_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $morphManyRelationToAttach1 = MorphManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + $morphManyRelationToAttach2 = MorphManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphManyRelation' => [ + [ + 'operation' => 'attach', + 'keys' => [$morphManyRelationToAttach1->getKey(), $morphManyRelationToAttach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphManyRelation()->count(), + 2 + ); + } + public function test_updating_a_resource_with_detaching_morph_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); @@ -423,6 +478,57 @@ public function test_updating_a_resource_with_detaching_morph_many_relation(): v ); } + public function test_updating_a_resource_with_detaching_multiple_morph_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $morphManyRelationToDetach1 = MorphManyRelationFactory::new() + ->for($modelToUpdate) + ->createOne(); + $morphManyRelationToDetach2 = MorphManyRelationFactory::new() + ->for($modelToUpdate) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphManyRelation' => [ + [ + 'operation' => 'detach', + 'keys' => [$morphManyRelationToDetach1->getKey(), $morphManyRelationToDetach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphManyRelation()->count(), + 0 + ); + } + public function test_updating_a_resource_with_updating_morph_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); @@ -480,6 +586,72 @@ public function test_updating_a_resource_with_updating_morph_many_relation(): vo ); } + public function test_updating_a_resource_with_updating_multiple_morph_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $morphManyRelationToAttach1 = MorphManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + $morphManyRelationToAttach2 = MorphManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphManyRelation' => [ + [ + 'operation' => 'update', + 'keys' => [$morphManyRelationToAttach1->getKey(), $morphManyRelationToAttach2->getKey()], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the number has been modified on the relation + $this->assertEquals( + $morphManyRelationToAttach1->fresh()->number, + 5001 + ); + $this->assertEquals( + $morphManyRelationToAttach2->fresh()->number, + 5001 + ); + + // Here we test that the model is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphManyRelation()->count(), + 2 + ); + } + public function test_updating_a_resource_with_creating_morph_one_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); @@ -918,11 +1090,18 @@ public function test_updating_a_resource_with_attaching_morph_to_many_relation() ); } - public function test_updating_a_resource_with_detaching_morph_to_many_relation(): void + public function test_updating_a_resource_with_attaching_multiple_morph_to_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); - $morphToManyRelationToDetach = MorphToManyRelationFactory::new() - ->recycle($modelToUpdate) + $morphToManyRelationToAttach1 = MorphToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $morphToManyRelationToAttach2 = MorphToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) ->createOne(); Gate::policy(Model::class, GreenPolicy::class); @@ -942,8 +1121,8 @@ public function test_updating_a_resource_with_detaching_morph_to_many_relation() 'relations' => [ 'morphToManyRelation' => [ [ - 'operation' => 'detach', - 'key' => $morphToManyRelationToDetach->getKey(), + 'operation' => 'attach', + 'keys' => [$morphToManyRelationToAttach1->getKey(), $morphToManyRelationToAttach2->getKey()], ], ], ], @@ -962,17 +1141,15 @@ public function test_updating_a_resource_with_detaching_morph_to_many_relation() // Here we test that the relation is correctly linked $this->assertEquals( Model::find($response->json('updated.0'))->morphToManyRelation()->count(), - 0 + 2 ); } - public function test_updating_a_resource_with_updating_morph_to_many_relation(): void + public function test_updating_a_resource_with_detaching_morph_to_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); - $morphToManyRelationToUpdate = MorphToManyRelationFactory::new() - ->has( - ModelFactory::new()->count(1) - ) + $morphToManyRelationToDetach = MorphToManyRelationFactory::new() + ->recycle($modelToUpdate) ->createOne(); Gate::policy(Model::class, GreenPolicy::class); @@ -992,9 +1169,8 @@ public function test_updating_a_resource_with_updating_morph_to_many_relation(): 'relations' => [ 'morphToManyRelation' => [ [ - 'operation' => 'update', - 'key' => $morphToManyRelationToUpdate->getKey(), - 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + 'operation' => 'detach', + 'key' => $morphToManyRelationToDetach->getKey(), ], ], ], @@ -1010,25 +1186,25 @@ public function test_updating_a_resource_with_updating_morph_to_many_relation(): [$modelToUpdate], ); - // Here we test that the number has been modified on the relation - $this->assertEquals( - $morphToManyRelationToUpdate->fresh()->number, - 5001 - ); - - // Here we test that the model is correctly linked + // Here we test that the relation is correctly linked $this->assertEquals( Model::find($response->json('updated.0'))->morphToManyRelation()->count(), - 1 + 0 ); } - public function test_updating_a_resource_with_creating_morphed_by_many_relation(): void + public function test_updating_a_resource_with_detaching_multiple_morph_to_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); + $morphToManyRelationToDetach1 = MorphToManyRelationFactory::new() + ->recycle($modelToUpdate) + ->createOne(); + $morphToManyRelationToDetach2 = MorphToManyRelationFactory::new() + ->recycle($modelToUpdate) + ->createOne(); Gate::policy(Model::class, GreenPolicy::class); - Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + Gate::policy(MorphToManyRelation::class, GreenPolicy::class); $response = $this->post( '/api/models/mutate', @@ -1042,10 +1218,10 @@ public function test_updating_a_resource_with_creating_morphed_by_many_relation( 'number' => 5001, ], 'relations' => [ - 'morphedByManyRelation' => [ + 'morphToManyRelation' => [ [ - 'operation' => 'create', - 'attributes' => [], + 'operation' => 'detach', + 'keys' => [$morphToManyRelationToDetach1->getKey(), $morphToManyRelationToDetach2->getKey()], ], ], ], @@ -1063,17 +1239,22 @@ public function test_updating_a_resource_with_creating_morphed_by_many_relation( // Here we test that the relation is correctly linked $this->assertEquals( - Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), - 1 + Model::find($response->json('updated.0'))->morphToManyRelation()->count(), + 0 ); } - public function test_updating_a_resource_with_creating_morphed_by_many_relation_with_unauthorized_pivot_fields(): void + public function test_updating_a_resource_with_updating_morph_to_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); + $morphToManyRelationToUpdate = MorphToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); Gate::policy(Model::class, GreenPolicy::class); - Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + Gate::policy(MorphToManyRelation::class, GreenPolicy::class); $response = $this->post( '/api/models/mutate', @@ -1087,13 +1268,11 @@ public function test_updating_a_resource_with_creating_morphed_by_many_relation_ 'number' => 5001, ], 'relations' => [ - 'morphedByManyRelation' => [ + 'morphToManyRelation' => [ [ - 'operation' => 'create', - 'attributes' => [], - 'pivot' => [ - 'unauthorized_field' => 20, - ], + 'operation' => 'update', + 'key' => $morphToManyRelationToUpdate->getKey(), + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 ], ], ], @@ -1103,18 +1282,183 @@ public function test_updating_a_resource_with_creating_morphed_by_many_relation_ ['Accept' => 'application/json'] ); - $response->assertStatus(422); - $response->assertJsonStructure(['message', 'errors' => ['mutate.0.relations.morphedByManyRelation.0.pivot']]); + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the number has been modified on the relation + $this->assertEquals( + $morphToManyRelationToUpdate->fresh()->number, + 5001 + ); + + // Here we test that the model is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphToManyRelation()->count(), + 1 + ); } - public function test_updating_a_resource_with_creating_morphed_by_many_relation_with_pivot_fields(): void + public function test_updating_a_resource_with_updating_multiple_morph_to_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); - - Gate::policy(Model::class, GreenPolicy::class); - Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); - - $response = $this->post( + $morphToManyRelationToUpdate1 = MorphToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $morphToManyRelationToUpdate2 = MorphToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphToManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphToManyRelation' => [ + [ + 'operation' => 'update', + 'keys' => [$morphToManyRelationToUpdate1->getKey(), $morphToManyRelationToUpdate2->getKey()], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the number has been modified on the relation + $this->assertEquals( + $morphToManyRelationToUpdate1->fresh()->number, + 5001 + ); + $this->assertEquals( + $morphToManyRelationToUpdate2->fresh()->number, + 5001 + ); + + // Here we test that the model is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphToManyRelation()->count(), + 2 + ); + } + + public function test_updating_a_resource_with_creating_morphed_by_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'create', + 'attributes' => [], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), + 1 + ); + } + + public function test_updating_a_resource_with_creating_morphed_by_many_relation_with_unauthorized_pivot_fields(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'create', + 'attributes' => [], + 'pivot' => [ + 'unauthorized_field' => 20, + ], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $response->assertStatus(422); + $response->assertJsonStructure(['message', 'errors' => ['mutate.0.relations.morphedByManyRelation.0.pivot']]); + } + + public function test_updating_a_resource_with_creating_morphed_by_many_relation_with_pivot_fields(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( '/api/models/mutate', [ 'mutate' => [ @@ -1269,6 +1613,61 @@ public function test_updating_a_resource_with_attaching_morphed_by_many_relation ); } + public function test_updating_a_resource_with_attaching_multiple_morphed_by_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $morphedByManyRelationToAttach1 = MorphedByManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $morphedByManyRelationToAttach2 = MorphedByManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'attach', + 'keys' => [$morphedByManyRelationToAttach1->getKey(), $morphedByManyRelationToAttach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), + 2 + ); + } + public function test_updating_a_resource_with_detaching_morphed_by_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); @@ -1317,14 +1716,549 @@ public function test_updating_a_resource_with_detaching_morphed_by_many_relation ); } - public function test_updating_a_resource_with_updating_morphed_by_many_relation(): void + public function test_updating_a_resource_with_detaching_multiple_morphed_by_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $morphedByManyRelationToDetach1 = MorphedByManyRelationFactory::new() + ->recycle($modelToUpdate) + ->createOne(); + $morphedByManyRelationToDetach2 = MorphedByManyRelationFactory::new() + ->recycle($modelToUpdate) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'detach', + 'keys' => [$morphedByManyRelationToDetach1->getKey(), $morphedByManyRelationToDetach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), + 0 + ); + } + + public function test_updating_a_resource_with_updating_morphed_by_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $morphedByManyRelationToUpdate = MorphedByManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'update', + 'key' => $morphedByManyRelationToUpdate->getKey(), + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the number has been modified on the relation + $this->assertEquals( + $morphedByManyRelationToUpdate->fresh()->number, + 5001 + ); + + // Here we test that the model is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), + 1 + ); + } + + public function test_updating_a_resource_with_updating_multiple_morphed_by_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $morphedByManyRelationToUpdate1 = MorphedByManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $morphedByManyRelationToUpdate2 = MorphedByManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'update', + 'keys' => [$morphedByManyRelationToUpdate1->getKey(), $morphedByManyRelationToUpdate2->getKey()], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the number has been modified on the relation + $this->assertEquals( + $morphedByManyRelationToUpdate1->fresh()->number, + 5001 + ); + $this->assertEquals( + $morphedByManyRelationToUpdate2->fresh()->number, + 5001 + ); + + // Here we test that the model is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), + 2 + ); + } + + public function test_updating_a_resource_with_syncing_morphed_by_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $morphedByManySynced = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyNotSynced = MorphedByManyRelationFactory::new()->createOne(); + + $modelToUpdate->morphedByManyRelation() + ->attach($morphedByManySynced); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'sync', + 'key' => $morphedByManyNotSynced->getKey(), + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + 'pivot' => [ + 'number' => 20, + ], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), + 1 + ); + + $this->assertDatabaseHas( + $morphedByManySynced->getTable(), + ['number' => 5001] + ); + $this->assertDatabaseHas( + $modelToUpdate->morphedByManyRelation()->getTable(), + ['number' => 20] + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->first()->getKey(), + $morphedByManyNotSynced->getKey() + ); + } + + public function test_updating_a_resource_with_syncing_multiple_morphed_by_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $morphedByManySynced = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyNotSynced1 = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyNotSynced2 = MorphedByManyRelationFactory::new()->createOne(); + + $modelToUpdate->morphedByManyRelation() + ->attach($morphedByManySynced); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'sync', + 'keys' => [$morphedByManyNotSynced1->getKey(), $morphedByManyNotSynced2->getKey()], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + 'pivot' => [ + 'number' => 20, + ], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), + 2 + ); + + $this->assertDatabaseHas( + $morphedByManySynced->getTable(), + ['number' => 5001] + ); + $this->assertDatabaseHas( + $modelToUpdate->morphedByManyRelation()->getTable(), + ['number' => 20] + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->pluck('id')->toArray(), + [ + $morphedByManyNotSynced1->getKey(), + $morphedByManyNotSynced2->getKey(), + ] + ); + } + + public function test_updating_a_resource_with_syncing_morphed_by_many_relation_without_detaching_with_already_attached(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $morphedByManySynced = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyNotSynced = MorphedByManyRelationFactory::new()->createOne(); + + $modelToUpdate->morphedByManyRelation() + ->attach($morphedByManySynced); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'sync', + 'key' => $morphedByManyNotSynced->getKey(), + 'without_detaching' => true, + 'attributes' => ['number' => 5002], // 5001 because with factory it can't exceed 5000 + 'pivot' => [ + 'number' => 21, + ], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), + 2 + ); + + $this->assertDatabaseHas( + $morphedByManyNotSynced->getTable(), + ['number' => 5002] + ); + $this->assertDatabaseHas( + $modelToUpdate->morphedByManyRelation()->getTable(), + ['number' => 21] + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->pluck('id')->toArray(), + [ + $morphedByManySynced->getKey(), + $morphedByManyNotSynced->getKey(), + ] + ); + } + + public function test_updating_a_resource_with_syncing_multiple_morphed_by_many_relation_without_detaching_with_already_attached(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $morphedByManySynced = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyNotSynced1 = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyNotSynced2 = MorphedByManyRelationFactory::new()->createOne(); + + $modelToUpdate->morphedByManyRelation() + ->attach($morphedByManySynced); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'sync', + 'keys' => [$morphedByManyNotSynced1->getKey(), $morphedByManyNotSynced2->getKey()], + 'without_detaching' => true, + 'attributes' => ['number' => 5002], // 5001 because with factory it can't exceed 5000 + 'pivot' => [ + 'number' => 21, + ], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), + 3 + ); + + $this->assertDatabaseHas( + $morphedByManyNotSynced1->getTable(), + ['number' => 5002] + ); + $this->assertDatabaseHas( + $modelToUpdate->morphedByManyRelation()->getTable(), + ['number' => 21] + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->pluck('id')->toArray(), + [ + $morphedByManySynced->getKey(), + $morphedByManyNotSynced1->getKey(), + $morphedByManyNotSynced2->getKey(), + ] + ); + } + + public function test_updating_a_resource_with_syncing_morphed_by_many_relation_with_detaching_with_already_attached(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $morphedByManySynced = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyNotSynced = MorphedByManyRelationFactory::new()->createOne(); + + $modelToUpdate->morphedByManyRelation() + ->attach($morphedByManySynced); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'morphedByManyRelation' => [ + [ + 'operation' => 'sync', + 'key' => $morphedByManyNotSynced->getKey(), + 'attributes' => ['number' => 5002], // 5001 because with factory it can't exceed 5000 + 'pivot' => [ + 'number' => 21, + ], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), + 1 + ); + + $this->assertDatabaseHas( + $morphedByManyNotSynced->getTable(), + ['number' => 5002] + ); + $this->assertDatabaseHas( + $modelToUpdate->morphedByManyRelation()->getTable(), + ['number' => 21] + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->morphedByManyRelation()->pluck('id')->toArray(), + [ + $morphedByManyNotSynced->getKey(), + ] + ); + } + + public function test_updating_a_resource_with_toggling_morphed_by_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); - $morphedByManyRelationToUpdate = MorphedByManyRelationFactory::new() - ->has( - ModelFactory::new()->count(1) - ) - ->createOne(); + $morphedByManyToggled = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyNotToggled = MorphedByManyRelationFactory::new()->createOne(); + + $modelToUpdate->morphedByManyRelation() + ->attach($morphedByManyToggled); Gate::policy(Model::class, GreenPolicy::class); Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); @@ -1343,9 +2277,12 @@ public function test_updating_a_resource_with_updating_morphed_by_many_relation( 'relations' => [ 'morphedByManyRelation' => [ [ - 'operation' => 'update', - 'key' => $morphedByManyRelationToUpdate->getKey(), - 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + 'operation' => 'toggle', + 'key' => $morphedByManyToggled->getKey(), + ], + [ + 'operation' => 'toggle', + 'key' => $morphedByManyNotToggled->getKey(), ], ], ], @@ -1361,27 +2298,27 @@ public function test_updating_a_resource_with_updating_morphed_by_many_relation( [$modelToUpdate], ); - // Here we test that the number has been modified on the relation + // Here we test that the relation is correctly linked $this->assertEquals( - $morphedByManyRelationToUpdate->fresh()->number, - 5001 + Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), + 1 ); - // Here we test that the model is correctly linked + // Here we test that the relation is correctly linked $this->assertEquals( - Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), - 1 + Model::find($response->json('updated.0'))->morphedByManyRelation()->first()->getKey(), + $morphedByManyNotToggled->getKey() ); } - public function test_updating_a_resource_with_syncing_morphed_by_many_relation(): void + public function test_updating_a_resource_with_toggling_multiple_morphed_by_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); - $morphedByManySynced = MorphedByManyRelationFactory::new()->createOne(); - $morphedByManyNotSynced = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyToggled = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyNotToggled = MorphedByManyRelationFactory::new()->createOne(); $modelToUpdate->morphedByManyRelation() - ->attach($morphedByManySynced); + ->attach($morphedByManyToggled); Gate::policy(Model::class, GreenPolicy::class); Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); @@ -1400,12 +2337,8 @@ public function test_updating_a_resource_with_syncing_morphed_by_many_relation() 'relations' => [ 'morphedByManyRelation' => [ [ - 'operation' => 'sync', - 'key' => $morphedByManyNotSynced->getKey(), - 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 - 'pivot' => [ - 'number' => 20, - ], + 'operation' => 'toggle', + 'keys' => [$morphedByManyToggled->getKey(), $morphedByManyNotToggled->getKey()], ], ], ], @@ -1427,30 +2360,21 @@ public function test_updating_a_resource_with_syncing_morphed_by_many_relation() 1 ); - $this->assertDatabaseHas( - $morphedByManySynced->getTable(), - ['number' => 5001] - ); - $this->assertDatabaseHas( - $modelToUpdate->morphedByManyRelation()->getTable(), - ['number' => 20] - ); - // Here we test that the relation is correctly linked $this->assertEquals( Model::find($response->json('updated.0'))->morphedByManyRelation()->first()->getKey(), - $morphedByManyNotSynced->getKey() + $morphedByManyNotToggled->getKey() ); } - public function test_updating_a_resource_with_syncing_morphed_by_many_relation_without_detaching_with_already_attached(): void + public function test_updating_a_resource_with_toggling_morphed_by_many_relation_and_updating_attributes_and_pivot(): void { $modelToUpdate = ModelFactory::new()->createOne(); - $morphedByManySynced = MorphedByManyRelationFactory::new()->createOne(); - $morphedByManyNotSynced = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyToggled = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyNotToggled = MorphedByManyRelationFactory::new()->createOne(); $modelToUpdate->morphedByManyRelation() - ->attach($morphedByManySynced); + ->attach($morphedByManyToggled); Gate::policy(Model::class, GreenPolicy::class); Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); @@ -1469,11 +2393,18 @@ public function test_updating_a_resource_with_syncing_morphed_by_many_relation_w 'relations' => [ 'morphedByManyRelation' => [ [ - 'operation' => 'sync', - 'key' => $morphedByManyNotSynced->getKey(), - 'without_detaching' => true, - 'attributes' => ['number' => 5002], // 5001 because with factory it can't exceed 5000 - 'pivot' => [ + 'operation' => 'toggle', + 'key' => $morphedByManyToggled->getKey(), + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + 'pivot' => [ + 'number' => 20, + ], + ], + [ + 'operation' => 'toggle', + 'key' => $morphedByManyNotToggled->getKey(), + 'attributes' => ['number' => 5002], // 5002 because with factory it can't exceed 5000 + 'pivot' => [ 'number' => 21, ], ], @@ -1494,13 +2425,18 @@ public function test_updating_a_resource_with_syncing_morphed_by_many_relation_w // Here we test that the relation is correctly linked $this->assertEquals( Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), - 2 + 1 ); $this->assertDatabaseHas( - $morphedByManyNotSynced->getTable(), + $morphedByManyToggled->getTable(), + ['number' => 5001] + ); + $this->assertDatabaseHas( + $morphedByManyToggled->getTable(), ['number' => 5002] ); + $this->assertDatabaseHas( $modelToUpdate->morphedByManyRelation()->getTable(), ['number' => 21] @@ -1508,22 +2444,20 @@ public function test_updating_a_resource_with_syncing_morphed_by_many_relation_w // Here we test that the relation is correctly linked $this->assertEquals( - Model::find($response->json('updated.0'))->morphedByManyRelation()->pluck('id')->toArray(), - [ - $morphedByManySynced->getKey(), - $morphedByManyNotSynced->getKey(), - ] + Model::find($response->json('updated.0'))->morphedByManyRelation()->first()->getKey(), + $morphedByManyNotToggled->getKey() ); } - public function test_updating_a_resource_with_syncing_morphed_by_many_relation_with_detaching_with_already_attached(): void + public function test_updating_a_resource_with_toggling_multiple_morphed_by_many_relation_and_updating_attributes_and_pivot(): void { $modelToUpdate = ModelFactory::new()->createOne(); - $morphedByManySynced = MorphedByManyRelationFactory::new()->createOne(); - $morphedByManyNotSynced = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyToggled = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyNotToggled1 = MorphedByManyRelationFactory::new()->createOne(); + $morphedByManyNotToggled2 = MorphedByManyRelationFactory::new()->createOne(); $modelToUpdate->morphedByManyRelation() - ->attach($morphedByManySynced); + ->attach($morphedByManyToggled); Gate::policy(Model::class, GreenPolicy::class); Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); @@ -1542,9 +2476,17 @@ public function test_updating_a_resource_with_syncing_morphed_by_many_relation_w 'relations' => [ 'morphedByManyRelation' => [ [ - 'operation' => 'sync', - 'key' => $morphedByManyNotSynced->getKey(), - 'attributes' => ['number' => 5002], // 5001 because with factory it can't exceed 5000 + 'operation' => 'toggle', + 'key' => $morphedByManyToggled->getKey(), + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + 'pivot' => [ + 'number' => 20, + ], + ], + [ + 'operation' => 'toggle', + 'keys' => [$morphedByManyNotToggled1->getKey(), $morphedByManyNotToggled2->getKey()], + 'attributes' => ['number' => 5002], // 5002 because with factory it can't exceed 5000 'pivot' => [ 'number' => 21, ], @@ -1566,13 +2508,18 @@ public function test_updating_a_resource_with_syncing_morphed_by_many_relation_w // Here we test that the relation is correctly linked $this->assertEquals( Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), - 1 + 2 ); $this->assertDatabaseHas( - $morphedByManyNotSynced->getTable(), + $morphedByManyToggled->getTable(), + ['number' => 5001] + ); + $this->assertDatabaseHas( + $morphedByManyToggled->getTable(), ['number' => 5002] ); + $this->assertDatabaseHas( $modelToUpdate->morphedByManyRelation()->getTable(), ['number' => 21] @@ -1582,22 +2529,23 @@ public function test_updating_a_resource_with_syncing_morphed_by_many_relation_w $this->assertEquals( Model::find($response->json('updated.0'))->morphedByManyRelation()->pluck('id')->toArray(), [ - $morphedByManyNotSynced->getKey(), + $morphedByManyNotToggled1->getKey(), + $morphedByManyNotToggled2->getKey(), ] ); } - public function test_updating_a_resource_with_toggling_morphed_by_many_relation(): void + public function test_updating_a_resource_with_syncing_morph_to_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); - $morphedByManyToggled = MorphedByManyRelationFactory::new()->createOne(); - $morphedByManyNotToggled = MorphedByManyRelationFactory::new()->createOne(); + $morphToManySynced = MorphToManyRelationFactory::new()->createOne(); + $morphToManyNotSynced = MorphToManyRelationFactory::new()->createOne(); - $modelToUpdate->morphedByManyRelation() - ->attach($morphedByManyToggled); + $modelToUpdate->morphToManyRelation() + ->attach($morphToManySynced); Gate::policy(Model::class, GreenPolicy::class); - Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + Gate::policy(MorphToManyRelation::class, GreenPolicy::class); $response = $this->post( '/api/models/mutate', @@ -1611,14 +2559,14 @@ public function test_updating_a_resource_with_toggling_morphed_by_many_relation( 'number' => 5001, ], 'relations' => [ - 'morphedByManyRelation' => [ - [ - 'operation' => 'toggle', - 'key' => $morphedByManyToggled->getKey(), - ], + 'morphToManyRelation' => [ [ - 'operation' => 'toggle', - 'key' => $morphedByManyNotToggled->getKey(), + 'operation' => 'sync', + 'key' => $morphToManyNotSynced->getKey(), + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + 'pivot' => [ + 'number' => 20, + ], ], ], ], @@ -1636,28 +2584,38 @@ public function test_updating_a_resource_with_toggling_morphed_by_many_relation( // Here we test that the relation is correctly linked $this->assertEquals( - Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), + Model::find($response->json('updated.0'))->morphToManyRelation()->count(), 1 ); + $this->assertDatabaseHas( + $morphToManySynced->getTable(), + ['number' => 5001] + ); + $this->assertDatabaseHas( + $modelToUpdate->morphToManyRelation()->getTable(), + ['number' => 20] + ); + // Here we test that the relation is correctly linked $this->assertEquals( - Model::find($response->json('updated.0'))->morphedByManyRelation()->first()->getKey(), - $morphedByManyNotToggled->getKey() + Model::find($response->json('updated.0'))->morphToManyRelation()->first()->getKey(), + $morphToManyNotSynced->getKey() ); } - public function test_updating_a_resource_with_toggling_morphed_by_many_relation_and_updating_attributes_and_pivot(): void + public function test_updating_a_resource_with_syncing_multiple_morph_to_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); - $morphedByManyToggled = MorphedByManyRelationFactory::new()->createOne(); - $morphedByManyNotToggled = MorphedByManyRelationFactory::new()->createOne(); + $morphToManySynced = MorphToManyRelationFactory::new()->createOne(); + $morphToManyNotSynced1 = MorphToManyRelationFactory::new()->createOne(); + $morphToManyNotSynced2 = MorphToManyRelationFactory::new()->createOne(); - $modelToUpdate->morphedByManyRelation() - ->attach($morphedByManyToggled); + $modelToUpdate->morphToManyRelation() + ->attach($morphToManySynced); Gate::policy(Model::class, GreenPolicy::class); - Gate::policy(MorphedByManyRelation::class, GreenPolicy::class); + Gate::policy(MorphToManyRelation::class, GreenPolicy::class); $response = $this->post( '/api/models/mutate', @@ -1671,23 +2629,15 @@ public function test_updating_a_resource_with_toggling_morphed_by_many_relation_ 'number' => 5001, ], 'relations' => [ - 'morphedByManyRelation' => [ + 'morphToManyRelation' => [ [ - 'operation' => 'toggle', - 'key' => $morphedByManyToggled->getKey(), + 'operation' => 'sync', + 'keys' => [$morphToManyNotSynced1->getKey(), $morphToManyNotSynced2->getKey()], 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 'pivot' => [ 'number' => 20, ], ], - [ - 'operation' => 'toggle', - 'key' => $morphedByManyNotToggled->getKey(), - 'attributes' => ['number' => 5002], // 5002 because with factory it can't exceed 5000 - 'pivot' => [ - 'number' => 21, - ], - ], ], ], ], @@ -1704,39 +2654,34 @@ public function test_updating_a_resource_with_toggling_morphed_by_many_relation_ // Here we test that the relation is correctly linked $this->assertEquals( - Model::find($response->json('updated.0'))->morphedByManyRelation()->count(), - 1 + Model::find($response->json('updated.0'))->morphToManyRelation()->count(), + 2 ); $this->assertDatabaseHas( - $morphedByManyToggled->getTable(), + $morphToManySynced->getTable(), ['number' => 5001] ); $this->assertDatabaseHas( - $morphedByManyToggled->getTable(), - ['number' => 5002] - ); - - $this->assertDatabaseHas( - $modelToUpdate->morphedByManyRelation()->getTable(), - ['number' => 21] + $modelToUpdate->morphToManyRelation()->getTable(), + ['number' => 20] ); // Here we test that the relation is correctly linked $this->assertEquals( - Model::find($response->json('updated.0'))->morphedByManyRelation()->first()->getKey(), - $morphedByManyNotToggled->getKey() + Model::find($response->json('updated.0'))->morphToManyRelation()->pluck('id')->toArray(), + [ + $morphToManyNotSynced1->getKey(), + $morphToManyNotSynced2->getKey(), + ] ); } - public function test_updating_a_resource_with_syncing_morph_to_many_relation(): void + public function test_updating_a_resource_with_syncing_morph_to_many_relation_without_detaching(): void { $modelToUpdate = ModelFactory::new()->createOne(); - $morphToManySynced = MorphToManyRelationFactory::new()->createOne(); - $morphToManyNotSynced = MorphToManyRelationFactory::new()->createOne(); - - $modelToUpdate->morphToManyRelation() - ->attach($morphToManySynced); + $morphToManyToSync1 = MorphToManyRelationFactory::new()->createOne(); + $morphToManyToSync2 = MorphToManyRelationFactory::new()->createOne(); Gate::policy(Model::class, GreenPolicy::class); Gate::policy(MorphToManyRelation::class, GreenPolicy::class); @@ -1756,12 +2701,21 @@ public function test_updating_a_resource_with_syncing_morph_to_many_relation(): 'morphToManyRelation' => [ [ 'operation' => 'sync', - 'key' => $morphToManyNotSynced->getKey(), + 'key' => $morphToManyToSync1->getKey(), 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 'pivot' => [ 'number' => 20, ], ], + [ + 'operation' => 'sync', + 'key' => $morphToManyToSync2->getKey(), + 'without_detaching' => true, + 'attributes' => ['number' => 5002], // 5001 because with factory it can't exceed 5000 + 'pivot' => [ + 'number' => 21, + ], + ], ], ], ], @@ -1779,30 +2733,42 @@ public function test_updating_a_resource_with_syncing_morph_to_many_relation(): // Here we test that the relation is correctly linked $this->assertEquals( Model::find($response->json('updated.0'))->morphToManyRelation()->count(), - 1 + 2 ); $this->assertDatabaseHas( - $morphToManySynced->getTable(), + $morphToManyToSync1->getTable(), ['number' => 5001] ); + $this->assertDatabaseHas( + $morphToManyToSync2->getTable(), + ['number' => 5002] + ); $this->assertDatabaseHas( $modelToUpdate->morphToManyRelation()->getTable(), ['number' => 20] ); + $this->assertDatabaseHas( + $modelToUpdate->morphToManyRelation()->getTable(), + ['number' => 21] + ); // Here we test that the relation is correctly linked $this->assertEquals( - Model::find($response->json('updated.0'))->morphToManyRelation()->first()->getKey(), - $morphToManyNotSynced->getKey() + Model::find($response->json('updated.0'))->morphToManyRelation()->pluck('id')->toArray(), + [ + $morphToManyToSync1->getKey(), + $morphToManyToSync2->getKey(), + ] ); } - public function test_updating_a_resource_with_syncing_morph_to_many_relation_without_detaching(): void + public function test_updating_a_resource_with_syncing_multiple_morph_to_many_relation_without_detaching(): void { $modelToUpdate = ModelFactory::new()->createOne(); $morphToManyToSync1 = MorphToManyRelationFactory::new()->createOne(); $morphToManyToSync2 = MorphToManyRelationFactory::new()->createOne(); + $morphToManyToSync3 = MorphToManyRelationFactory::new()->createOne(); Gate::policy(Model::class, GreenPolicy::class); Gate::policy(MorphToManyRelation::class, GreenPolicy::class); @@ -1830,7 +2796,7 @@ public function test_updating_a_resource_with_syncing_morph_to_many_relation_wit ], [ 'operation' => 'sync', - 'key' => $morphToManyToSync2->getKey(), + 'keys' => [$morphToManyToSync2->getKey(), $morphToManyToSync3->getKey()], 'without_detaching' => true, 'attributes' => ['number' => 5002], // 5001 because with factory it can't exceed 5000 'pivot' => [ @@ -1854,7 +2820,7 @@ public function test_updating_a_resource_with_syncing_morph_to_many_relation_wit // Here we test that the relation is correctly linked $this->assertEquals( Model::find($response->json('updated.0'))->morphToManyRelation()->count(), - 2 + 3 ); $this->assertDatabaseHas( @@ -1880,6 +2846,7 @@ public function test_updating_a_resource_with_syncing_morph_to_many_relation_wit [ $morphToManyToSync1->getKey(), $morphToManyToSync2->getKey(), + $morphToManyToSync3->getKey(), ] ); } diff --git a/tests/Feature/Controllers/MutateUpdateOperationsTest.php b/tests/Feature/Controllers/MutateUpdateOperationsTest.php index 2235c35..180afb4 100644 --- a/tests/Feature/Controllers/MutateUpdateOperationsTest.php +++ b/tests/Feature/Controllers/MutateUpdateOperationsTest.php @@ -594,6 +594,55 @@ public function test_updating_a_resource_with_attaching_has_many_relation(): voi ); } + public function test_updating_a_resource_with_attaching_multiple_has_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $hasManyRelationToAttach1 = HasManyRelationFactory::new() + ->createOne(); + $hasManyRelationToAttach2 = HasManyRelationFactory::new() + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(HasManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'hasManyRelation' => [ + [ + 'operation' => 'attach', + 'keys' => [$hasManyRelationToAttach1->getKey(), $hasManyRelationToAttach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->hasManyRelation()->count(), + 2 + ); + } + public function test_updating_a_resource_with_attaching_has_many_relation_with_required_rules(): void { $modelToUpdate = ModelFactory::new()->createOne(); @@ -696,6 +745,57 @@ public function test_updating_a_resource_with_detaching_has_many_relation(): voi ); } + public function test_updating_a_resource_with_detaching_multiple_has_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $hasManyRelationToDetach1 = HasManyRelationFactory::new() + ->for($modelToUpdate) + ->createOne(); + $hasManyRelationToDetach2 = HasManyRelationFactory::new() + ->for($modelToUpdate) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(HasManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'hasManyRelation' => [ + [ + 'operation' => 'detach', + 'keys' => [$hasManyRelationToDetach1->getKey(), $hasManyRelationToDetach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->hasManyRelation()->count(), + 0 + ); + } + public function test_updating_a_resource_with_updating_has_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); @@ -753,6 +853,73 @@ public function test_updating_a_resource_with_updating_has_many_relation(): void ); } + public function test_updating_a_resource_with_updating_multiple_has_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $hasManyRelationToAttach1 = HasManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + $hasManyRelationToAttach2 = HasManyRelationFactory::new() + ->for( + ModelFactory::new()->createOne() + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(HasManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'hasManyRelation' => [ + [ + 'operation' => 'update', + 'keys' => [$hasManyRelationToAttach1->getKey(), $hasManyRelationToAttach2->getKey()], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the number has been modified on the relation + $this->assertEquals( + $hasManyRelationToAttach1->fresh()->number, + 5001 + ); + + $this->assertEquals( + $hasManyRelationToAttach2->fresh()->number, + 5001 + ); + + // Here we test that the model is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->hasManyRelation()->count(), + 2 + ); + } + public function test_updating_a_resource_with_creating_has_one_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); @@ -1242,6 +1409,67 @@ public function test_updating_a_resource_with_toggling_belongs_to_many_relation( ); } + public function test_updating_a_resource_with_toggling_multiple_belongs_to_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $belongsToManyToggled1 = BelongsToManyRelationFactory::new()->createOne(); + $belongsToManyToggled2 = BelongsToManyRelationFactory::new()->createOne(); + $belongsToManyNotToggled = BelongsToManyRelationFactory::new()->createOne(); + + $modelToUpdate->belongsToManyRelation() + ->attach([$belongsToManyToggled1, $belongsToManyToggled2]); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(BelongsToManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'belongsToManyRelation' => [ + [ + 'operation' => 'toggle', + 'keys' => [$belongsToManyToggled1->getKey(), $belongsToManyToggled2->getKey()], + ], + [ + 'operation' => 'toggle', + 'key' => $belongsToManyNotToggled->getKey(), + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->belongsToManyRelation()->count(), + 1 + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->belongsToManyRelation()->first()->getKey(), + $belongsToManyNotToggled->getKey() + ); + } + public function test_updating_a_resource_with_toggling_belongs_to_many_relation_and_updating_attributes_and_pivot(): void { $modelToUpdate = ModelFactory::new()->createOne(); @@ -1393,6 +1621,80 @@ public function test_updating_a_resource_with_syncing_belongs_to_many_relation() ); } + public function test_updating_a_resource_with_syncing_multiple_belongs_to_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $belongsToManySynced = BelongsToManyRelationFactory::new()->createOne(); + $belongsToManyNotSynced1 = BelongsToManyRelationFactory::new()->createOne(); + $belongsToManyNotSynced2 = BelongsToManyRelationFactory::new()->createOne(); + + $modelToUpdate->belongsToManyRelation() + ->attach([$belongsToManySynced]); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(BelongsToManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'belongsToManyRelation' => [ + [ + 'operation' => 'sync', + 'keys' => [$belongsToManyNotSynced1->getKey(), $belongsToManyNotSynced2->getKey()], + 'attributes' => ['number' => 5001], // 5001 because with factory it can't exceed 5000 + 'pivot' => [ + 'number' => 20, + ], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->belongsToManyRelation()->count(), + 2 + ); + + $this->assertDatabaseHas( + $belongsToManySynced->getTable(), + ['number' => 5001] + ); + $this->assertDatabaseHas( + $modelToUpdate->belongsToManyRelation()->getTable(), + ['number' => 20] + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->belongsToManyRelation()->first()->getKey(), + $belongsToManyNotSynced1->getKey() + ); + $this->assertEquals( + Model::find($response->json('updated.0'))->belongsToManyRelation()->get()->last()->getKey(), + $belongsToManyNotSynced2->getKey() + ); + } + public function test_updating_a_resource_with_syncing_belongs_to_many_relation_without_detaching(): void { $modelToUpdate = ModelFactory::new()->createOne(); @@ -1740,6 +2042,65 @@ public function test_updating_a_resource_with_updating_belongs_to_many_relation_ ); } + public function test_updating_a_resource_with_updating_multiple_belongs_to_many_relation_pivot_fields(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + + $belongsToManyToUpdate1 = BelongsToManyRelationFactory::new()->createOne(); + $belongsToManyToUpdate2 = BelongsToManyRelationFactory::new()->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(BelongsToManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'belongsToManyRelation' => [ + [ + 'operation' => 'update', + 'keys' => [$belongsToManyToUpdate1->getKey(), $belongsToManyToUpdate2->getKey()], + 'pivot' => [ + 'number' => 20, + ], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate] + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->belongsToManyRelation()->count(), + 2 + ); + $this->assertEquals( + Model::find($response->json('updated.0'))->belongsToManyRelation[0]->belongs_to_many_pivot->number, + 20 + ); + $this->assertEquals( + Model::find($response->json('updated.0'))->belongsToManyRelation[1]->belongs_to_many_pivot->number, + 20 + ); + } + public function test_updating_a_resource_with_creating_multiple_belongs_to_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); @@ -1839,6 +2200,61 @@ public function test_updating_a_resource_with_attaching_belongs_to_many_relation ); } + public function test_updating_a_resource_with_attaching_multiple_belongs_to_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $belongsToManyRelationToAttach1 = BelongsToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + $belongsToManyRelationToAttach2 = BelongsToManyRelationFactory::new() + ->has( + ModelFactory::new()->count(1) + ) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(BelongsToManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'belongsToManyRelation' => [ + [ + 'operation' => 'attach', + 'keys' => [$belongsToManyRelationToAttach1->getKey(), $belongsToManyRelationToAttach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->belongsToManyRelation()->count(), + 2 + ); + } + public function test_updating_a_resource_with_detaching_belongs_to_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne(); @@ -1887,6 +2303,57 @@ public function test_updating_a_resource_with_detaching_belongs_to_many_relation ); } + public function test_updating_a_resource_with_detaching_multiple_belongs_to_many_relation(): void + { + $modelToUpdate = ModelFactory::new()->createOne(); + $belongsToManyRelationToDetach1 = BelongsToManyRelationFactory::new() + ->recycle($modelToUpdate) + ->createOne(); + $belongsToManyRelationToDetach2 = BelongsToManyRelationFactory::new() + ->recycle($modelToUpdate) + ->createOne(); + + Gate::policy(Model::class, GreenPolicy::class); + Gate::policy(BelongsToManyRelation::class, GreenPolicy::class); + + $response = $this->post( + '/api/models/mutate', + [ + 'mutate' => [ + [ + 'operation' => 'update', + 'key' => $modelToUpdate->getKey(), + 'attributes' => [ + 'name' => 'new name', + 'number' => 5001, + ], + 'relations' => [ + 'belongsToManyRelation' => [ + [ + 'operation' => 'detach', + 'keys' => [$belongsToManyRelationToDetach1->getKey(), $belongsToManyRelationToDetach2->getKey()], + ], + ], + ], + ], + ], + ], + ['Accept' => 'application/json'] + ); + + $this->assertMutatedResponse( + $response, + [], + [$modelToUpdate], + ); + + // Here we test that the relation is correctly linked + $this->assertEquals( + Model::find($response->json('updated.0'))->belongsToManyRelation()->count(), + 0 + ); + } + public function test_updating_a_resource_with_updating_belongs_to_many_relation(): void { $modelToUpdate = ModelFactory::new()->createOne();