Skip to content

Commit 0cb189e

Browse files
committed
feat: add new HTTP actions implementation
1 parent 217a3bf commit 0cb189e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+976
-554
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Tests
22

33
on:
44
push:
5-
branches: [ main, develop, 3.x ]
5+
branches: [ main, develop, 4.x ]
66
pull_request:
7-
branches: [ main, develop, 3.x ]
7+
branches: [ main, develop, 4.x ]
88

99
jobs:
1010
build:

composer.json

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
"require": {
2626
"php": "^8.1",
2727
"ext-json": "*",
28-
"laravel-json-api/core": "^3.2",
29-
"laravel-json-api/eloquent": "^3.0",
30-
"laravel-json-api/encoder-neomerx": "^3.0",
31-
"laravel-json-api/exceptions": "^2.0",
32-
"laravel-json-api/spec": "^2.0",
33-
"laravel-json-api/validation": "^3.0",
28+
"laravel-json-api/core": "^4.0",
29+
"laravel-json-api/eloquent": "^4.0",
30+
"laravel-json-api/encoder-neomerx": "^4.0",
31+
"laravel-json-api/exceptions": "^3.0",
32+
"laravel-json-api/spec": "^3.0",
33+
"laravel-json-api/validation": "^4.0",
3434
"laravel/framework": "^10.0"
3535
},
3636
"require-dev": {
@@ -53,7 +53,8 @@
5353
},
5454
"extra": {
5555
"branch-alias": {
56-
"dev-develop": "3.x-dev"
56+
"dev-develop": "3.x-dev",
57+
"dev-4.x": "4.x-dev"
5758
},
5859
"laravel": {
5960
"aliases": {
@@ -65,7 +66,7 @@
6566
]
6667
}
6768
},
68-
"minimum-stability": "stable",
69+
"minimum-stability": "dev",
6970
"prefer-stable": true,
7071
"config": {
7172
"sort-packages": true

phpunit.xml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false"
3-
beStrictAboutTestsThatDoNotTestAnything="true" bootstrap="vendor/autoload.php" colors="true"
4-
processIsolation="false" stopOnError="false" stopOnFailure="false"
5-
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.2/phpunit.xsd" cacheDirectory=".phpunit.cache"
6-
backupStaticProperties="false">
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
backupGlobals="false"
4+
beStrictAboutTestsThatDoNotTestAnything="true"
5+
bootstrap="vendor/autoload.php"
6+
colors="true"
7+
processIsolation="false"
8+
stopOnError="false"
9+
stopOnFailure="false"
10+
cacheDirectory=".phpunit.cache"
11+
backupStaticProperties="false"
12+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.2/phpunit.xsd"
13+
>
714
<coverage/>
815
<testsuites>
916
<testsuite name="Unit">

src/Http/Controllers/Actions/AttachRelationship.php

Lines changed: 14 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -19,61 +19,27 @@
1919

2020
namespace LaravelJsonApi\Laravel\Http\Controllers\Actions;
2121

22-
use Illuminate\Contracts\Support\Responsable;
23-
use Illuminate\Http\Response;
24-
use LaravelJsonApi\Contracts\Routing\Route;
25-
use LaravelJsonApi\Contracts\Store\Store as StoreContract;
26-
use LaravelJsonApi\Core\Support\Str;
27-
use LaravelJsonApi\Laravel\Http\Requests\ResourceQuery;
28-
use LaravelJsonApi\Laravel\Http\Requests\ResourceRequest;
29-
use LogicException;
22+
use LaravelJsonApi\Contracts\Http\Actions\AttachRelationship as AttachRelationshipContract;
23+
use LaravelJsonApi\Core\Responses\NoContentResponse;
24+
use LaravelJsonApi\Core\Responses\RelationshipResponse;
25+
use LaravelJsonApi\Laravel\Http\Requests\JsonApiRequest;
3026

3127
trait AttachRelationship
3228
{
33-
3429
/**
3530
* Attach records to a to-many relationship.
3631
*
37-
* @param Route $route
38-
* @param StoreContract $store
39-
* @return Response|Responsable
32+
* @param JsonApiRequest $request
33+
* @param AttachRelationshipContract $action
34+
* @return RelationshipResponse|NoContentResponse
4035
*/
41-
public function attachRelationship(Route $route, StoreContract $store)
36+
public function attachRelationship(
37+
JsonApiRequest $request,
38+
AttachRelationshipContract $action,
39+
): RelationshipResponse|NoContentResponse
4240
{
43-
$relation = $route
44-
->schema()
45-
->relationship($fieldName = $route->fieldName());
46-
47-
if (!$relation->toMany()) {
48-
throw new LogicException('Expecting a to-many relation for an attach action.');
49-
}
50-
51-
$request = ResourceRequest::forResource(
52-
$resourceType = $route->resourceType()
53-
);
54-
55-
$query = ResourceQuery::queryMany($relation->inverse());
56-
57-
$model = $route->model();
58-
$response = null;
59-
60-
if (method_exists($this, $hook = 'attaching' . Str::classify($fieldName))) {
61-
$response = $this->{$hook}($model, $request, $query);
62-
}
63-
64-
if ($response) {
65-
return $response;
66-
}
67-
68-
$result = $store
69-
->modifyToMany($resourceType, $model, $fieldName)
70-
->withRequest($query)
71-
->attach($request->validatedForRelation());
72-
73-
if (method_exists($this, $hook = 'attached' . Str::classify($fieldName))) {
74-
$response = $this->{$hook}($model, $result, $request, $query);
75-
}
76-
77-
return $response ?: response('', Response::HTTP_NO_CONTENT);
41+
return $action
42+
->withHooks($this)
43+
->execute($request);
7844
}
7945
}

src/Http/Controllers/Actions/Destroy.php

Lines changed: 9 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -19,87 +19,26 @@
1919

2020
namespace LaravelJsonApi\Laravel\Http\Controllers\Actions;
2121

22-
use Illuminate\Auth\Access\AuthorizationException;
23-
use Illuminate\Auth\AuthenticationException;
2422
use Illuminate\Contracts\Support\Responsable;
25-
use Illuminate\Http\Response;
26-
use Illuminate\Support\Facades\Auth;
27-
use LaravelJsonApi\Contracts\Routing\Route;
28-
use LaravelJsonApi\Contracts\Store\Store as StoreContract;
29-
use LaravelJsonApi\Laravel\Exceptions\HttpNotAcceptableException;
30-
use LaravelJsonApi\Laravel\Http\Requests\ResourceRequest;
23+
use LaravelJsonApi\Contracts\Http\Actions\Destroy as DestroyContract;
24+
use LaravelJsonApi\Laravel\Http\Requests\JsonApiRequest;
25+
use Symfony\Component\HttpFoundation\Response;
3126

3227
trait Destroy
3328
{
3429

3530
/**
3631
* Destroy a resource.
3732
*
38-
* @param Route $route
39-
* @param StoreContract $store
33+
* @param JsonApiRequest $request
34+
* @param DestroyContract $action
4035
* @return Response|Responsable
41-
* @throws AuthenticationException|AuthorizationException|HttpNotAcceptableException
4236
*/
43-
public function destroy(Route $route, StoreContract $store)
37+
public function destroy(JsonApiRequest $request, DestroyContract $action): Responsable|Response
4438
{
45-
/**
46-
* As we do not have a query request class for a delete request,
47-
* we need to manually check that the request Accept header
48-
* is the JSON:API media type.
49-
*/
50-
$acceptable = false;
51-
52-
foreach (request()->getAcceptableContentTypes() as $contentType) {
53-
if ($contentType === ResourceRequest::JSON_API_MEDIA_TYPE) {
54-
$acceptable = true;
55-
break;
56-
}
57-
}
58-
59-
throw_unless($acceptable, new HttpNotAcceptableException());
60-
61-
$request = ResourceRequest::forResourceIfExists(
62-
$resourceType = $route->resourceType()
63-
);
64-
65-
$model = $route->model();
66-
67-
/**
68-
* The resource request class is optional for deleting,
69-
* as delete validation is optional. However, if we do not have
70-
* a resource request then the action will not have been authorized.
71-
* So we need to trigger authorization in this case.
72-
*/
73-
if (!$request) {
74-
$check = $route->authorizer()->destroy(
75-
$request = \request(),
76-
$model,
77-
);
78-
79-
throw_if(false === $check && Auth::guest(), new AuthenticationException());
80-
throw_if(false === $check, new AuthorizationException());
81-
}
82-
83-
$response = null;
84-
85-
if (method_exists($this, 'deleting')) {
86-
$response = $this->deleting($model, $request);
87-
}
88-
89-
if ($response) {
90-
return $response;
91-
}
92-
93-
$store->delete(
94-
$resourceType,
95-
$route->modelOrResourceId()
96-
);
97-
98-
if (method_exists($this, 'deleted')) {
99-
$response = $this->deleted($model, $request);
100-
}
101-
102-
return $response ?: response(null, Response::HTTP_NO_CONTENT);
39+
return $action
40+
->withHooks($this)
41+
->execute($request);
10342
}
10443

10544
}

src/Http/Controllers/Actions/DetachRelationship.php

Lines changed: 15 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -19,61 +19,27 @@
1919

2020
namespace LaravelJsonApi\Laravel\Http\Controllers\Actions;
2121

22-
use Illuminate\Contracts\Support\Responsable;
23-
use Illuminate\Http\Response;
24-
use LaravelJsonApi\Contracts\Routing\Route;
25-
use LaravelJsonApi\Contracts\Store\Store as StoreContract;
26-
use LaravelJsonApi\Core\Support\Str;
27-
use LaravelJsonApi\Laravel\Http\Requests\ResourceQuery;
28-
use LaravelJsonApi\Laravel\Http\Requests\ResourceRequest;
29-
use LogicException;
22+
use LaravelJsonApi\Contracts\Http\Actions\DetachRelationship as DetachRelationshipContract;
23+
use LaravelJsonApi\Core\Responses\NoContentResponse;
24+
use LaravelJsonApi\Core\Responses\RelationshipResponse;
25+
use LaravelJsonApi\Laravel\Http\Requests\JsonApiRequest;
3026

3127
trait DetachRelationship
3228
{
33-
3429
/**
35-
* Detach records to a has-many relationship.
30+
* Detach records from a to-many relationship.
3631
*
37-
* @param Route $route
38-
* @param StoreContract $store
39-
* @return Response|Responsable
32+
* @param JsonApiRequest $request
33+
* @param DetachRelationshipContract $action
34+
* @return RelationshipResponse|NoContentResponse
4035
*/
41-
public function detachRelationship(Route $route, StoreContract $store)
36+
public function detachRelationship(
37+
JsonApiRequest $request,
38+
DetachRelationshipContract $action,
39+
): RelationshipResponse|NoContentResponse
4240
{
43-
$relation = $route
44-
->schema()
45-
->relationship($fieldName = $route->fieldName());
46-
47-
if (!$relation->toMany()) {
48-
throw new LogicException('Expecting a to-many relation for an attach action.');
49-
}
50-
51-
$request = ResourceRequest::forResource(
52-
$resourceType = $route->resourceType()
53-
);
54-
55-
$query = ResourceQuery::queryMany($relation->inverse());
56-
57-
$model = $route->model();
58-
$response = null;
59-
60-
if (method_exists($this, $hook = 'detaching' . Str::classify($fieldName))) {
61-
$response = $this->{$hook}($model, $request, $query);
62-
}
63-
64-
if ($response) {
65-
return $response;
66-
}
67-
68-
$result = $store
69-
->modifyToMany($resourceType, $model, $fieldName)
70-
->withRequest($query)
71-
->detach($request->validatedForRelation());
72-
73-
if (method_exists($this, $hook = 'detached' . Str::classify($fieldName))) {
74-
$response = $this->{$hook}($model, $result, $request, $query);
75-
}
76-
77-
return $response ?: response('', Response::HTTP_NO_CONTENT);
41+
return $action
42+
->withHooks($this)
43+
->execute($request);
7844
}
7945
}

src/Http/Controllers/Actions/FetchMany.php

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,48 +19,23 @@
1919

2020
namespace LaravelJsonApi\Laravel\Http\Controllers\Actions;
2121

22-
use Illuminate\Contracts\Support\Responsable;
23-
use Illuminate\Http\Response;
24-
use LaravelJsonApi\Contracts\Routing\Route;
25-
use LaravelJsonApi\Contracts\Store\Store as StoreContract;
22+
use LaravelJsonApi\Contracts\Http\Actions\FetchMany as FetchManyContract;
2623
use LaravelJsonApi\Core\Responses\DataResponse;
27-
use LaravelJsonApi\Laravel\Http\Requests\ResourceQuery;
24+
use LaravelJsonApi\Laravel\Http\Requests\JsonApiRequest;
2825

2926
trait FetchMany
3027
{
31-
3228
/**
33-
* Fetch zero to many JSON API resources.
29+
* Fetch zero-to-many JSON:API resources.
3430
*
35-
* @param Route $route
36-
* @param StoreContract $store
37-
* @return Responsable|Response
31+
* @param JsonApiRequest $request
32+
* @param FetchManyContract $action
33+
* @return DataResponse
3834
*/
39-
public function index(Route $route, StoreContract $store)
35+
public function index(JsonApiRequest $request, FetchManyContract $action): DataResponse
4036
{
41-
$request = ResourceQuery::queryMany(
42-
$resourceType = $route->resourceType()
43-
);
44-
45-
$response = null;
46-
47-
if (method_exists($this, 'searching')) {
48-
$response = $this->searching($request);
49-
}
50-
51-
if ($response) {
52-
return $response;
53-
}
54-
55-
$data = $store
56-
->queryAll($resourceType)
57-
->withRequest($request)
58-
->firstOrPaginate($request->page());
59-
60-
if (method_exists($this, 'searched')) {
61-
$response = $this->searched($data, $request);
62-
}
63-
64-
return $response ?: DataResponse::make($data)->withQueryParameters($request);
37+
return $action
38+
->withHooks($this)
39+
->execute($request);
6540
}
6641
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy