8000 Merge branch 'release/1.0.0-alpha.5' into main · siberfx/laravel-json-api@c82f217 · GitHub
[go: up one dir, main page]

Skip to content

Commit c82f217

Browse files
committed
Merge branch 'release/1.0.0-alpha.5' into main
2 parents f534fb2 + e0bdffc commit c82f217

File tree

27 files changed

+1038
-68
lines changed

27 files changed

+1038
-68
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@
33
All notable changes to this project will be documented in this file. This project adheres to
44
[Semantic Versioning](http://semver.org/) and [this changelog format](http://keepachangelog.com/).
55

6+
## [1.0.0-alpha.5] - 2021-03-12
7+
8+
### Added
9+
10+
- [#43](https://github.com/laravel-json-api/laravel/issues/43) The package now supports soft-deleting resources. For
11+
full details on how to apply this to resource schemas, refer to the new *Soft Deleting* chapter in the documentation.
12+
- Multi-resource models are now supported. This allows developers to represent a single model class as multiple
13+
different JSON:API resource types within an API. Refer to documentation for details of how to implement.
14+
- [#8](https://github.com/laravel-json-api/laravel/issues/8) The new `MorphToMany` relation field can now be used to add
15+
polymorphic to-many relations to a schema. Refer to documentation for details.
16+
- Developers can now type-hint dependencies in their server's `serving()` method.
17+
- Can now manually register request, query and collection query classes using the `RequestResolver::registerRequest()`,
18+
`RequestResolver::registerQuery()` and `RequestResolver::registerCollectionQuery()` static methods.
19+
620
## [1.0.0-alpha.4] - 2021-02-27
721

822
### Added

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
"require": {
2626
"php": "^7.4|^8.0",
2727
"ext-json": "*",
28-
"laravel-json-api/core": "^1.0.0-alpha.4",
29-
"laravel-json-api/eloquent": "^1.0.0-alpha.4",
28+
"laravel-json-api/core": "^1.0.0-alpha.5",
29+
"laravel-json-api/eloquent": "^1.0.0-alpha.5",
3030
"laravel-json-api/encoder-neomerx": "^1.0.0-alpha.4",
3131
"laravel-json-api/exceptions": "^1.0.0-alpha.2",
3232
"laravel-json-api/spec": "^1.0.0-alpha.4",
33-
"laravel-json-api/validation": "^1.0.0-alpha.4",
33+
"laravel-json-api/validation": "^1.0.0-alpha.5",
3434
"laravel/framework": "^8.0"
3535
},
3636
"require-dev": {

src/Console/MakeSchema.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,15 @@ protected function replaceModel(string $stub, string $model): string
8484
}
8585

8686
$model = class_basename($model);
87+
$schema = $this->option('proxy') ? 'ProxySchema' : 'Schema';
8788

8889
$replace = [
8990
'{{ namespacedModel }}' => $namespacedModel,
9091
'{{namespacedModel}}' => $namespacedModel,
9192
'{{ model }}' => $model,
9293
'{{model}}' => $model,
94+
'{{ schema }}' => $schema,
95+
'{{schema}}' => $schema,
9396
];
9497

9598
return str_replace(
@@ -105,6 +108,7 @@ protected function getOptions()
105108
return [
106109
['force', null, InputOption::VALUE_NONE, 'Create the class even if the schema already exists'],
107110
['model', 'm', InputOption::VALUE_REQUIRED, 'The model that the schema applies to.'],
111+
['proxy', 'p', InputOption::VALUE_NONE, 'Create a schema for an Eloquent model proxy.'],
108112
['server', 's', InputOption::VALUE_REQUIRED, 'The JSON:API server the schema exists in.'],
109113
];
110114
}

src/Http/Middleware/BootJsonApi.php

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
use Closure;
2323
use Illuminate\Contracts\Container\Container as IlluminateContainer;
2424
use Illuminate\Http\Request;
25-
use Illuminate\Pagination\AbstractPaginator;
2625
use LaravelJsonApi\Contracts\Routing\Route as RouteContract;
2726
use LaravelJsonApi\Contracts\Server\Repository;
2827
use LaravelJsonApi\Contracts\Server\Server;
28+
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
2929
use LaravelJsonApi\Laravel\Routing\Route;
3030

3131
class BootJsonApi
@@ -63,6 +63,10 @@ public function __construct(IlluminateContainer $container, Repository $servers)
6363
*/
6464
public function handle($request, Closure $next, string $name)
6565
{
66+
/**
67+
* When handling a HTTP request, both the JSON:API server and
68+
* request classes can be singletons bound into the container.
69+
*/
6670
$this->container->instance(
6771
Server::class,
6872
$server = $this->servers->server($name)
@@ -73,25 +77,29 @@ public function handle($request, Closure $next, string $name)
7377
$route = new Route($this->container, $server, $request->route())
7478
);
7579

76-
$server->serving();
80+
/**
81+
* Before we do anything, we must ensure the server is set up to
82+
* handle a HTTP request. We do that by invoking the `serving()`
83+
* hook on the server instance.
84+
*/
85+
if (method_exists($server, 'serving')) {
86+
$this->container->call([$server, 'serving']);
87+
}
88+
89+
/**
90+
* Once the server is set up, we can substitute bindings. This must
91+
* happen after the `serving` hook, in case that hook has added any
92+
* Eloquent scopes.
93+
*/
7794
$route->substituteBindings();
78-
$this->bindPageResolver();
7995

80-
return $next($request);
81-
}
96+
/**
97+
* We will also override the Laravel page resolver, as we know this is
98+
* a JSON:API request, and the specification would have the page number
99+
* nested under the `page` query parameter.
100+
*/
101+
PagePagination::bindPageResolver();
82102

83-
/**
84-
* Override the page resolver to read the page parameter from the JSON API request.
85-
*
86-
* @return void
87-
*/
88-
protected function bindPageResolver(): void
89-
{
90-
/** Override the current page resolution */
91-
AbstractPaginator::currentPageResolver(static function ($pageName) {
92-
$pagination = \request()->query($pageName);
93-
94-
return $pagination['number'] ?? null;
95-
});
103+
return $next($request);
96104
}
97105
}

src/Http/Requests/RequestResolver.php

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,62 @@
3030
class RequestResolver
3131
{
3232

33+
/**
34+
* @var array
35+
*/
36+
private static array $custom = [];
37+
3338
/**
3439
* @var string
3540
*/
3641
private string $type;
3742

43+
/**
44+
* Register a custom binding for a query.
45+
*
46+
* @param string $resourceType
47+
* @param string $class
48+
*/
49+
public static function registerQuery(string $resourceType, string $class): void
50+
{
51+
self::register('Query', $resourceType, $class);
52+
}
53+
54+
/**
55+
* Register a custom binding for a collection query.
56+
*
57+
* @param string $resourceType
58+
* @param string $class
59+
*/
60+
public static function registerCollectionQuery(string $resourceType, string $class): void
61+
{
62+
self::register('CollectionQuery', $resourceType, $class);
63+
}
64+
65+
/**
66+
* Register a custom binding for a resource request.
67+
*
68+
* @param string $resourceType
69+
* @param string $class
70+
*/
71+
public static function registerRequest(string $resourceType, string $class): void
72+
{
73+
self::register('Request', $resourceType, $class);
74+
}
75+
76+
/**
77+
* Register a custom binding.
78+
*
79+
* @param string $type
80+
* @param string $resourceType
81+
* @param string $class
82+
*/
83+
private static function register(string $type, string $resourceType, string $class): void
84+
{
85+
self::$custom[$type] = self::$custom[$type] ?? [];
86+
self::$custom[$type][$resourceType] = $class;
87+
}
88+
3889
/**
3990
* ResourceRequest constructor.
4091
*
@@ -55,7 +106,7 @@ public function __invoke(string $resourceType, bool $allowNull = false): ?FormRe
55106
$app = app();
56107

57108
try {
58-
$fqn = Str::replaceLast('Schema', $this->type, get_class(
109+
$fqn = $this->custom($resourceType) ?: Str::replaceLast('Schema', $this->type, get_class(
59110
$app->make(SchemaContainer::class)->schemaFor($resourceType)
60111
));
61112

@@ -78,4 +129,17 @@ public function __invoke(string $resourceType, bool $allowNull = false): ?FormRe
78129
), 0, $ex);
79130
}
80131
}
132+
133+
/**
134+
* Check whether a custom class has been registered for the resource type.
135+
*
136+
* @param string $resourceType
137+
* @return string|null
138+
*/
139+
private function custom(string $resourceType): ?string
140+
{
141+
$values = self::$custom[$this->type] ?? [];
142+
143+
return $values[$resourceType] ?? null;
144+
}
81145
}

stubs/schema.stub

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ use LaravelJsonApi\Eloquent\Fields\DateTime;
88
use LaravelJsonApi\Eloquent\Fields\ID;
99
use LaravelJsonApi\Eloquent\Filters\WhereIn;
1010
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
11-
use LaravelJsonApi\Eloquent\Schema;
11+
use LaravelJsonApi\Eloquent\{{ schema }};
1212

13-
class {{ class }} extends Schema
13+
class {{ class }} extends {{ schema }}
1414
{
1515

1616
/**

tests/dummy/app/Http/Controllers/Api/V1/PostController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function purge(): Response
5050
{
5151
$this->authorize('deleteAll', Post::class);
5252

53-
Post::query()->delete();
53+
Post::query()->forceDelete();
5454

5555
return response('', 204);
5656
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
/*
3+
* Copyright 2021 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace App\JsonApi\V1\Images;
21+
22+
use App\Models\Image;
23+
use LaravelJsonApi\Eloquent\Contracts\Paginator;
24+
use LaravelJsonApi\Eloquent\Fields\DateTime;
25+
use LaravelJsonApi\Eloquent\Fields\ID;
26+
use LaravelJsonApi\Eloquent\Fields\Str;
27+
use LaravelJsonApi\Eloquent\Filters\WhereIn;
28+
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
29+
use LaravelJsonApi\Eloquent\Schema;
30+
31+
class ImageSchema extends Schema
32+
{
33+
34+
/**
35+
* The model the schema corresponds to.
36+
*
37+
* @var string
38+
*/
39+
public static string $model = Image::class;
40+
41+
/**
42+
* @inheritDoc
43+
*/
44+
public function fields(): array
45+
{
46+
return [
47+
ID::make()->uuid(),
48+
DateTime::make('createdAt')->sortable()->readOnly(),
49+
Str::make('url'),
50+
DateTime::make('updatedAt')->sortable()->readOnly(),
51+
];
52+
}
53+
54+
/**
55+
* @inheritDoc
56+
*/
57+
public function filters(): array
58+
{
59+
return [
60+
WhereIn::make('id', $this->idColumn())->delimiter(','),
61+
];
62+
}
63+
64+
/**
65+
* @inheritDoc
66+
*/
67+
public function pagination(): ?Paginator
68+
{
69+
return PagePagination::make()->withoutNestedMeta();
70+
}
71+
72+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
/*
3+
* Copyright 2021 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace App\JsonApi\V1\Media;
21+
22+
use LaravelJsonApi\Laravel\Http\Requests\ResourceQuery;
23+
use LaravelJsonApi\Validation\Rule as JsonApiRule;
24+
25+
class MediaCollectionQuery extends ResourceQuery
26+
{
27+
28+
/**
29+
* Get the validation rules that apply to the request.
30+
*
31+
* @return array
32+
*/
33+
public function rules(): array
34+
{
35+
return [
36+
'fields' => [
37+
'nullable',
38+
'array',
39+
JsonApiRule::fieldSets(),
40+
],
41+
'filter' => [
42+
'nullable',
43+
'array',
44+
JsonApiRule::filter(['id']),
45+
],
46+
'include' => [
47+
'nullable',
48+
'string',
49+
JsonApiRule::includePathsForPolymorph(),
50+
],
51+
'page' => [
52+
'nullable',
53+
'array',
54+
JsonApiRule::notSupported(),
55+
],
56+
'sort' => [
57+
'nullable',
58+
'string',
59+
JsonApiRule::notSupported(),
60+
],
61+
];
62+
}
63+
}

tests/dummy/app/JsonApi/V1/Posts/PostRequest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public function rules(): array
4040

4141
return [
4242
'content' => ['required', 'string'],
43+
'deletedAt' => ['nullable', JsonApiRule::dateTime()],
44+
'media' => JsonApiRule::toMany(),
4345
'slug' => ['required', 'string', $unique],
4446
'synopsis' => ['required', 'string'],
4547
'tags' => JsonApiRule::toMany(),

0 commit comments

Comments
 (0)
0