Skip to content

Commit

Permalink
feat: add update relationship command
Browse files Browse the repository at this point in the history
  • Loading branch information
lindyhopchris committed Aug 19, 2023
1 parent 99a681d commit 16f8179
Show file tree
Hide file tree
Showing 22 changed files with 2,007 additions and 9 deletions.
59 changes: 59 additions & 0 deletions src/Contracts/Http/Hooks/UpdateRelationshipImplementation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
/*
* Copyright 2023 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Contracts\Http\Hooks;

use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\Request;
use LaravelJsonApi\Contracts\Query\QueryParameters;

interface UpdateRelationshipImplementation
{
/**
* @param object $model
* @param string $fieldName
* @param Request $request
* @param QueryParameters $query
* @return void
* @throws HttpResponseException
*/
public function updatingRelationship(
object $model,
string $fieldName,
Request $request,
QueryParameters $query,
): void;

/**
* @param object $model
* @param string $fieldName
* @param mixed $related
* @param Request $request
* @param QueryParameters $query
* @return void
* @throws HttpResponseException
*/
public function updatedRelationship(
object $model,
string $fieldName,
mixed $related,
Request $request,
QueryParameters $query,
): void;
}
16 changes: 12 additions & 4 deletions src/Contracts/Store/Store.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,22 +125,30 @@ public function delete(ResourceType|string $resourceType, $modelOrResourceId): v
/**
* Modify a to-one relation.
*
* @param string $resourceType
* @param ResourceType|string $resourceType
* @param object|string $modelOrResourceId
* @param string $fieldName
* @return ToOneBuilder
*/
public function modifyToOne(string $resourceType, $modelOrResourceId, string $fieldName): ToOneBuilder;
public function modifyToOne(
ResourceType|string $resourceType,
$modelOrResourceId,
string $fieldName,
): ToOneBuilder;

/**
* Modify a to-many relation.
*
* @param string $resourceType
* @param ResourceType|string $resourceType
* @param object|string $modelOrResourceId
* @param string $fieldName
* @return ToManyBuilder
*/
public function modifyToMany(string $resourceType, $modelOrResourceId, string $fieldName): ToManyBuilder;
public function modifyToMany(
ResourceType|string $resourceType,
$modelOrResourceId,
string $fieldName,
): ToManyBuilder;

/**
* Access a resource repository by its JSON:API resource type.
Expand Down
5 changes: 5 additions & 0 deletions src/Contracts/Validation/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,9 @@ public function update(): UpdateValidator;
* @return DestroyValidator|null
*/
public function destroy(): ?DestroyValidator;

/**
* @return RelationshipValidator
*/
public function relation(): RelationshipValidator;
}
47 changes: 47 additions & 0 deletions src/Contracts/Validation/RelationshipValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
/*
* Copyright 2023 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Contracts\Validation;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Request;
use LaravelJsonApi\Core\Extensions\Atomic\Operations\UpdateToMany;
use LaravelJsonApi\Core\Extensions\Atomic\Operations\UpdateToOne;

interface RelationshipValidator
{
/**
* Extract validation data from the update relationship operation.
*
* @param object $model
* @param UpdateToOne|UpdateToMany $operation
* @return array
*/
public function extract(object $model, UpdateToOne|UpdateToMany $operation): array;

/**
* Make a validator for the update relationship operation.
*
* @param Request|null $request
* @param object $model
* @param UpdateToOne|UpdateToMany $operation
* @return Validator
*/
public function make(?Request $request, object $model, UpdateToOne|UpdateToMany $operation): Validator;
}
2 changes: 1 addition & 1 deletion src/Contracts/Validation/UpdateValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ interface UpdateValidator
public function extract(object $model, Update $operation): array;

/**
* Make a validator for the store operation.
* Make a validator for the update operation.
*
* @param Request|null $request
* @param object $model
Expand Down
40 changes: 40 additions & 0 deletions src/Core/Auth/ResourceAuthorizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,46 @@ public function showRelationshipOrFail(?Request $request, object $model, string
}
}

/**
* Authorize a JSON:API update relationship command.
*
* @param Request|null $request
* @param object $model
* @param string $fieldName
* @return ErrorList|null
* @throws AuthorizationException
* @throws AuthenticationException
* @throws HttpExceptionInterface
*/
public function updateRelationship(?Request $request, object $model, string $fieldName): ?ErrorList
{
$passes = $this->authorizer->updateRelationship(
$request,
$model,
$fieldName,
);

return $passes ? null : $this->failed();
}

/**
* Authorize a JSON:API update relationship command, or fail.
*
* @param Request|null $request
* @param object $model
* @param string $fieldName
* @return void
* @throws AuthorizationException
* @throws AuthenticationException
* @throws HttpExceptionInterface
*/
public function updateRelationshipOrFail(?Request $request, object $model, string $fieldName): void
{
if ($errors = $this->updateRelationship($request, $model, $fieldName)) {
throw new JsonApiException($errors);
}
}

/**
* @return ErrorList
* @throws AuthorizationException
Expand Down
30 changes: 30 additions & 0 deletions src/Core/Bus/Commands/Command/IsRelatable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
/*
* Copyright 2023 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Core\Bus\Commands\Command;

interface IsRelatable extends IsIdentifiable
{
/**
* Get the JSON:API field name for the relationship the command is targeting.
*
* @return string
*/
public function fieldName(): string;
}
3 changes: 3 additions & 0 deletions src/Core/Bus/Commands/Dispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
use LaravelJsonApi\Core\Bus\Commands\Store\StoreCommandHandler;
use LaravelJsonApi\Core\Bus\Commands\Update\UpdateCommand;
use LaravelJsonApi\Core\Bus\Commands\Update\UpdateCommandHandler;
use LaravelJsonApi\Core\Bus\Commands\UpdateRelationship\UpdateRelationshipCommand;
use LaravelJsonApi\Core\Bus\Commands\UpdateRelationship\UpdateRelationshipCommandHandler;
use RuntimeException;

class Dispatcher implements DispatcherContract
Expand Down Expand Up @@ -72,6 +74,7 @@ private function handlerFor(string $commandClass): string
StoreCommand::class => StoreCommandHandler::class,
UpdateCommand::class => UpdateCommandHandler::class,
DestroyCommand::class => DestroyCommandHandler::class,
UpdateRelationshipCommand::class => UpdateRelationshipCommandHandler::class,
default => throw new RuntimeException('Unexpected command class: ' . $commandClass),
};
}
Expand Down
95 changes: 95 additions & 0 deletions src/Core/Bus/Commands/Middleware/ValidateRelationshipCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php
/*
* Copyright 2023 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Core\Bus\Commands\Middleware;

use Closure;
use LaravelJsonApi\Contracts\Schema\Container as SchemaContainer;
use LaravelJsonApi\Contracts\Validation\Container as ValidatorContainer;
use LaravelJsonApi\Contracts\Validation\RelationshipValidator;
use LaravelJsonApi\Contracts\Validation\ResourceErrorFactory;
use LaravelJsonApi\Core\Bus\Commands\Result;
use LaravelJsonApi\Core\Bus\Commands\UpdateRelationship\HandlesUpdateRelationshipCommands;
use LaravelJsonApi\Core\Bus\Commands\UpdateRelationship\UpdateRelationshipCommand;
use LaravelJsonApi\Core\Document\Input\Values\ResourceType;

class ValidateRelationshipCommand implements HandlesUpdateRelationshipCommands
{
/**
* ValidateRelationshipCommand constructor
*
* @param ValidatorContainer $validatorContainer
* @param SchemaContainer $schemaContainer
* @param ResourceErrorFactory $errorFactory
*/
public function __construct(
private readonly ValidatorContainer $validatorContainer,
private readonly SchemaContainer $schemaContainer,
private readonly ResourceErrorFactory $errorFactory,
) {
}

/**
* @inheritDoc
*/
public function handle(UpdateRelationshipCommand $command, Closure $next): Result
{
if ($command->mustValidate()) {
$validator = $this
->validatorFor($command->type())
->make($command->request(), $command->modelOrFail(), $command->operation());

if ($validator->fails()) {
return Result::failed(
$this->errorFactory->make(
$this->schemaContainer->schemaFor($command->type()),
$validator,
),
);
}

$command = $command->withValidated(
$validator->validated(),
);
}

if ($command->isNotValidated()) {
$data = $this
->validatorFor($command->type())
->extract($command->modelOrFail(), $command->operation());

$command = $command->withValidated($data);
}

return $next($command);
}

/**
* Make an update relationship validator.
*
* @param ResourceType $type
* @return RelationshipValidator
*/
private function validatorFor(ResourceType $type): RelationshipValidator
{
return $this->validatorContainer
->validatorsFor($type)
->relation();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
/*
* Copyright 2023 Cloud Creativity Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace LaravelJsonApi\Core\Bus\Commands\UpdateRelationship;

use Closure;
use LaravelJsonApi\Core\Bus\Commands\Result;

interface HandlesUpdateRelationshipCommands
{
/**
* @param UpdateRelationshipCommand $command
* @param Closure $next
* @return Result
*/
public function handle(UpdateRelationshipCommand $command, Closure $next): Result;
}
Loading

0 comments on commit 16f8179

Please sign in to comment.