Skip to content

Commit 13663d7

Browse files
committed
[Feature] Support countable relationships
Adds a query parameter that is used by the Eloquent implementation to load relationship counts for to-many relations, and adds the count value to the relationship's meta. Closes #18
1 parent ddb4574 commit 13663d7

21 files changed

+353
-82
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,24 @@
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+
## Unreleased
7+
8+
### Added
9+
10+
- [#18](https://github.com/laravel-json-api/laravel/issues/18) Added a `withCount` query parameter. For Eloquent
11+
resources, this allows a client to request the relationship count for the primary data's relationships. Refer to
12+
documentation for implementation details.
13+
- There is now a `Core\Reponses\RelatedResponse` class for returning the result for a related resources endpoint. For
14+
example, the `/api/v1/posts/1/comments` endpoint. Previously the `DataResponse` class was used. While this class can
15+
still be used, the new `RelatedResponse` class merges relationship meta into the top-level `meta` member of the
16+
response document. For *to-many* relationships that are countable, this will mean the top-level `meta` member will
17+
contain the count of the relationship.
18+
19+
### Fixed
20+
21+
- Relationship endpoints that return resource identifiers now correctly include page meta in the top-level meta member
22+
of the document, if the results are paginated. Previously the page meta was incorrectly omitted.
23+
624
## [1.0.0-alpha.5] - 2021-03-12
725

826
### Added

composer.json

Lines changed: 6 additions & 6 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.5",
29-
"laravel-json-api/eloquent": "^1.0.0-alpha.5",
30-
"laravel-json-api/encoder-neomerx": "^1.0.0-alpha.4",
31-
"laravel-json-api/exceptions": "^1.0.0-alpha.2",
32-
"laravel-json-api/spec": "^1.0.0-alpha.4",
33-
"laravel-json-api/validation": "^1.0.0-alpha.5",
28+
"laravel-json-api/core": "^1.0.0-beta.1",
29+
"laravel-json-api/eloquent": "^1.0.0-beta.1",
30+
"laravel-json-api/encoder-neomerx": "^1.0.0-beta.1",
31+
"laravel-json-api/exceptions": "^1.0.0-beta.1",
32+
"laravel-json-api/spec": "^1.0.0-beta.1",
33+
"laravel-json-api/validation": "^1.0.0-beta.1",
3434
"laravel/framework": "^8.0"
3535
},
3636
"require-dev": {

src/Http/Controllers/Actions/FetchRelated.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
use Illuminate\Http\Response;
2424
use LaravelJsonApi\Contracts\Routing\Route;
2525
use LaravelJsonApi\Contracts\Store\Store as StoreContract;
26-
use LaravelJsonApi\Core\Responses\DataResponse;
26+
use LaravelJsonApi\Core\Responses\RelatedResponse;
2727
use LaravelJsonApi\Core\Support\Str;
2828
use LaravelJsonApi\Laravel\Http\Requests\ResourceQuery;
2929

@@ -76,6 +76,10 @@ public function showRelated(Route $route, StoreContract $store)
7676
$response = $this->{$hook}($model, $data, $request);
7777
}
7878

79-
return $response ?: new DataResponse($data);
79+
return $response ?: new RelatedResponse(
80+
$model,
81+
$relation->name(),
82+
$data,
83+
);
8084
}
8185
}

src/Http/Requests/AnonymousCollectionQuery.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
namespace LaravelJsonApi\Laravel\Http\Requests;
1919

20+
use LaravelJsonApi\Core\Query\Custom\ExtendedQueryParameters;
2021
use LaravelJsonApi\Validation\Rule as JsonApiRule;
2122

2223
class AnonymousCollectionQuery extends ResourceQuery
@@ -55,6 +56,11 @@ public function rules()
5556
'string',
5657
JsonApiRule::sort(),
5758
],
59+
ExtendedQueryParameters::withCount() => [
60+
'nullable',
61+
'string',
62+
JsonApiRule::countable(),
63+
],
5864
];
5965
}
6066
}

src/Http/Requests/AnonymousQuery.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
namespace LaravelJsonApi\Laravel\Http\Requests;
1919

20+
use LaravelJsonApi\Core\Query\Custom\ExtendedQueryParameters;
2021
use LaravelJsonApi\Validation\Rule as JsonApiRule;
2122

2223
class AnonymousQuery extends ResourceQuery
@@ -47,6 +48,11 @@ public function rules()
4748
],
4849
'page' => JsonApiRule::notSupported(),
4950
'sort' => JsonApiRule::notSupported(),
51+
ExtendedQueryParameters::withCount() => [
52+
'nullable',
53+
'string',
54+
JsonApiRule::countable(),
55+
],
5056
];
5157
}
5258
}

src/Http/Requests/ResourceQuery.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use LaravelJsonApi\Contracts\Query\QueryParameters;
2626
use LaravelJsonApi\Core\Exceptions\JsonApiException;
2727
use LaravelJsonApi\Core\Query\FieldSets;
28+
use LaravelJsonApi\Core\Query\FilterParameters;
2829
use LaravelJsonApi\Core\Query\IncludePaths;
2930
use LaravelJsonApi\Core\Query\SortFields;
3031
use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -196,17 +197,31 @@ public function page(): ?array
196197
/**
197198
* @inheritDoc
198199
*/
199-
public function filter(): ?array
200+
public function filter(): ?FilterParameters
200201
{
201202
$data = $this->validated();
202203

203204
if (array_key_exists('filter', $data)) {
204-
return $data['filter'];
205+
return FilterParameters::fromArray($data['filter'] ?? []);
205206
}
206207

207208
return null;
208209
}
209210

211+
/**
212+
* @inheritDoc
213+
*/
214+
public function unrecognisedParameters(): array
215+
{
216+
return collect($this->validated())->forget([
217+
'include',
218+
'fields',
219+
'sort',
220+
'page',
221+
'filter',
222+
])->all();
223+
}
224+
210225
/**
211226
* @return void
212227
*/

src/LaravelJsonApi.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
namespace LaravelJsonApi\Laravel;
2121

2222
use Illuminate\Support\Arr;
23+
use InvalidArgumentException;
2324
use LaravelJsonApi\Core\Auth\AuthorizerResolver;
25+
use LaravelJsonApi\Core\Query\Custom\ExtendedQueryParameters;
26+
use LaravelJsonApi\Eloquent\Resources\Relation;
2427

2528
final class LaravelJsonApi
2629
{
@@ -53,4 +56,36 @@ public static function defaultAuthorizer(string $authorizerClass): self
5356

5457
return new self();
5558
}
59+
60+
/**
61+
* Set the query parameter name for the countable implementation.
62+
*
63+
* @param string $parameter
64+
* @return static
65+
*/
66+
public static function withCountQueryParameter(string $parameter): self
67+
{
68+
if (!empty($parameter)) {
69+
ExtendedQueryParameters::withCount($parameter);
70+
return new self();
71+
}
72+
73+
throw new InvalidArgumentException('Expecting a non-empty string for the countable query parameter.');
74+
}
75+
76+
/**
77+
* Set the relationship meta key for the countable implementation.
78+
*
79+
* @param string $key
80+
* @return static
81+
*/
82+
public static function withCountMetaKey(string $key): self
83+
{
84+
if (!empty($key)) {
85+
Relation::withCount($key);
86+
return new self();
87+
}
88+
89+
throw new InvalidArgumentException('Expecting a non-empty string for the countable meta key.');
90+
}
5691
}

tests/dummy/app/JsonApi/V1/Comments/CommentSchema.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function filters(): array
6969
*/
7070
public function pagination(): ?Paginator
7171
{
72-
return PagePagination::make()->withoutNestedMeta();
72+
return PagePagination::make();
7373
}
7474

7575
}

tests/dummy/app/JsonApi/V1/Media/MediaCollectionQuery.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,12 @@ public function rules(): array
5656
'sort' => [
5757
'nullable',
5858
'string',
59-
JsonApiRule::notSupported(),
59+
JsonApiRule::sort(['id']),
60+
],
61+
'withCount' => [
62+
'nullable',
63+
'string',
64+
JsonApiRule::countableForPolymorph(),
6065
],
6166
];
6267
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ public function rules(): array
5858
'string',
5959
JsonApiRule::sort(),
6060
],
61+
'withCount' => [
62+
'nullable',
63+
'string',
64+
JsonApiRule::countable(),
65+
],
6166
];
6267
}
6368
}

0 commit comments

Comments
 (0)