10000 [Tests] Add extra routing tests · martynling/laravel-json-api@e289b55 · GitHub
[go: up one dir, main page]

Skip to content

Commit e289b55

Browse files
committed
[Tests] Add extra routing tests
1 parent ca8ee29 commit e289b55

File tree

8 files changed

+444
-52
lines changed

8 files changed

+444
-52
lines changed

docs/api.md

Lines changed: 109 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,89 @@ This uses the name `default` for your API and generates a config file called `js
1212

1313
### Multiple APIs
1414

15-
If your application has multiple APIs - e.g. if you've version controlled a public API - you must generate a config file for each API. For example:
15+
If your application has multiple APIs - e.g. if you have a version controlled API - you must generate a config file for
16+
each API. For example:
1617

1718
```bash
1819
$ php artisan make:json-api v1
1920
```
2021

2122
Will create the `json-api-v1.php` file.
2223

24+
## Namespacing
25+
26+
### Root Namespace
27+
28+
Your API's config file contains a `namespace` option that controls the namespace in which JSON API classes are held.
29+
30+
If `namespace` is `null`, the `JsonApi` namespace in your application's namespace will be used. E.g. in a default
31+
Laravel installation, the namespace will be `App\JsonApi`. If you have renamed your application namespace to
32+
`MyApp`, then `MyApp\JsonApi` will be used by default.
33+
34+
If you want to use a different namespace, set the `namespace` option accordingly. E.g. for our `v1` API, we might
35+
want to set it to `App\JsonApi\V1`.
36+
37+
### Organising Resource Classes
38+
39+
The `by-resource` setting controls how you want to organise classes within this namespace. If this setting is `true`,
40+
there will be a sub-namespace for each resource type. For example:
41+
42+
```text
43+
App\JsonApi
44+
- Posts
45+
- Adapter
46+
- Schema
47+
- Comments
48+
- Adapter
49+
- Schema
50+
```
51+
52+
If `by-resource` is `false`, the sub-namespace will be the class type (e.g. `Adapters`). For example:
53+
54+
```text
55+
App\JsonApi
57AE 56+
- Adapters
57+
- Post
58+
- Comment
59+
- Schemas
60+
- Post
61+
- Comment
62+
```
63+
64+
You must stick to whatever pattern you choose to use. This is because we use the structure to automatically detect
65+
JSON API classes.
66+
67+
### Eloquent
68+
69+
The config also contains a `use-eloquent` option. Set this to `true` if the majority of your resources relate to
70+
Eloquent models.
71+
72+
This option is used by the package's generators, so that they know to generate Eloquent JSON API classes or not. This
73+
saves you having to specify the type whenever generating JSON API classes.
74+
75+
The `use-eloquent` option is effectively a default, and can be overridden when using a generator. For example, if
76+
`use-eloquent` is `true`:
77+
78+
```bash
79+
# will generate Eloquent classes
80+
$ php artisan make:resource posts
81+
# will generate non-Eloquent classes
82+
$ php artisan make:resource posts -N
83+
```
84+
85+
If `use-eloquent` is `false`:
86+
87+
```bash
88+
# will generate non-Eloquent classes
89+
$ php artisan make:resource posts
90+
# will generate Eloquent classes
91+
$ php artisan make:resource posts -e
92+
```
93+
2394
## Defining Resources
2495

25-
Your API must be configured to understand how a JSON API resource type maps to a PHP class within your application. This is defined in the `resources` setting in the API's configuration file.
96+
Your API must be configured to understand how a JSON API resource type maps to a PHP class within your application.
97+
This is defined in the `resources` setting in the API's configuration file.
2698

2799
For example, if your application had two Eloquent models - `Post` and `Comment` - your resource configuration would be:
28100

@@ -35,6 +107,41 @@ For example, if your application had two Eloquent models - `Post` and `Comment`
35107
]
36108
```
37109

110+
## URL
111+
112+
Each JSON API is expected to have a root URL under which all its routes are nested. This is configured in your API's
113+
configuration file under the `url` setting, that looks like this:
114+
115+
```php
116+
'url' => [
117+
'host' => null,
118+
'namespace' => '/api/v1',
119+
'name' => 'api:v1:',
120+
],
121+
```
122+
123+
These settings control the links that appear in JSON API documents. We also automatically apply them when you
124+
register routes for your API.
125+
126+
### Host
127+
128+
When processing inbound HTTP requests, the current server host will always be used when encoding JSON API documents.
129+
130+
When encoding JSON API documents outside of HTTP requests, we use the `url.host` option from your API's configuration.
131+
If the value is `null`, we default to Laravel's `app.url` config setting. Otherwise, we'll use the value you've
132+
provided.
133+
134+
### Namespace
135+
136+
The URL namespace is the URL under which all resources for the API are nested. For example, if the namespace is
137+
`/api/v1`, then the `posts` resource routes will exists at `/api/v1/posts`.
138+
139+
### Name
140+
141+
The `name` setting applies the specified prefix to all route names that are registered for JSON API resources. For
142+
example, if the `name` is `api:v1:`, then the route name for the index of the `posts` resource will be
143+
`api:v1:posts.index`.
144+
38145
## Content Negotiation
39146

40147
The JSON API spec defines [content negotiation](http://jsonapi.org/format/#content-negotiation) that must occur
@@ -66,7 +173,3 @@ In the example, the config tells the codec matcher that the `application/vnd.api
66173
In the example, the config tells the codec matcher that the `application/vnd.api+json` is the only acceptable
67174
`Content-Type` that a client can submit. If a different media type is received, a `415 Unsupported Media Type`
68175
response will be sent.
69-
70-
## Other Configuration Settings
71-
72-
The generated API configuration file contains descriptions of each setting. The wiki will cover these settings in the relevant chapter.

docs/index.md

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,6 @@ Optionally you can also add an **Authorizer** instance to authorize incoming JSO
3737

3838
This may sound like a lot of units, but we believe the single purpose approach makes these highly testable and easy to reason about!
3939

40-
### Namespacing
41-
42-
JSON API units are expected to be stored in a root namespace, which defaults to the `JsonApi` namespace in your application - e.g. `App\JsonApi`.
43-
44-
We expect you to store units within this namespace in one of two ways:
45-
46-
1. **By Resource**: You have a namespace per resource type and the units for the type are stored in this namespace. E.g. for a `posts` resource you would have `App\JsonApi\Posts\{Adapter,Schema...}`
47-
48-
2. **By Unit**: You have a namespace per JSON API unit, with the classes named according to the the resource type. E.g. for a `posts` resource you would have `App\JsonApi\{Adapters,Schemas...}\Post`
49-
50-
For both, note that the namespace is plural, and the class name is singular. The package includes generator Artisan commands to keep this simple.
51-
5240
### Why *Records* not *Models*?
5341

5442
In Laravel the phrase *model* is potentially confusing with Eloquent models. While some applications might solely encode Eloquent models to JSON API resources, others will use a mixture of Eloquent models and other PHP classes, or might not even be using Eloquent models.

docs/routing.md

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,26 @@
55
To define the routes available in an API, register the API in your `routes/api.php` file as follows:
66

77
```php
8-
JsonApi::api('default', ['namespace' => 'Api'], function ($api, $router) {
8+
JsonApi::register('default', ['namespace' => 'Api'], function ($api, $router) {
99
$api->resource('posts');
1010
$api->resource('comments');
1111
});
1212
```
13+
> If you are not using the `JsonApi` facade, use `app("json-api")->register()` instead.
1314
1415
This is similar to registering a Laravel route group, except the first argument is the name of your API that must
1516
match the name used for the API's config. (So the above example uses `config/json-api-default.php`.) The other
1617
difference is that the `Closure` receives an API object as its first argument (and the Laravel router as its second).
1718
This API object is a helper object for registering JSON API resources.
1819

19-
> If you are not using the `JsonApi` facade, resolve `CloudCreativity\LaravelJsonApi\Routing\ResourceRegistrar` from
20-
the service container instead.
20+
When registering a JSON API, we automatically read the URL prefix and route name prefix from your
21+
[API's URL configuration](./api#url) and apply this to the route group for your API. The URL prefix in your JSON API
22+
config is **always** relative to the root URL on a host, i.e. from `/`. This means when registering your routes,
23+
you need to ensure that no prefix has already been applied.
24+
25+
> The default Laravel installation has an `api` prefix for API routes. If you are registering a JSON API in your
26+
`routes/api.php` file, you will need to remove the prefix from the `mapApiRoutes()` method in your
27+
`RouteServiceProvider`.
2128

2229
## Controller
2330

@@ -41,9 +48,9 @@ register the following routes:
4148
| :-- | :-- | :-- |
4249
| `GET /posts` | `posts.index` | `index` |
4350
| `POST /posts` | `posts.create` | `create` |
44-
| `GET /posts/{resource_id}` | `posts.read` | `read` |
45-
| `PATCH /posts/{resource_id}` | `posts.update` | `update` |
46-
| `DELETE /posts/{resource_id}` | `posts.delete` | `delete` |
51+
| `GET /posts/{resource}` | `posts.read` | `read` |
52+
| `PATCH /posts/{resource}` | `posts.update` | `update` |
53+
| `DELETE /posts/{resource}` | `posts.delete` | `delete` |
4754

4855
To register only some of these routes, use the `only` or `except` options as follows:
4956

@@ -90,9 +97,9 @@ The following has-one routes are registered (using the `author` relationship on
9097

9198
| URL | Route Name | Controller Action |
9299
| :-- | :-- | :-- |
93-
| `GET /posts/{resource_id}/author` | `posts.relationships.author` | `readRelatedResource` |
94-
| `GET /posts/{resource_id}/relationships/author` | `posts.relationships.author.read` | `readRelationship` |
95-
| `PATCH /posts/{resource_id}/relationships/author` | `posts.relationships.author.replace` | `replaceRelationship` |
100+
| `GET /posts/{resource}/author` | `posts.relationships.author` | `readRelatedResource` |
101+
| `GET /posts/{resource}/relationships/author` | `posts.relationships.author.read` | `readRelationship` |
102+
| `PATCH /posts/{resource}/relationships/author` | `posts.relationships.author.replace` | `replaceRelationship` |
96103

97104
To register only some of these, use the `only` or `except` options with the relationship. E.g.
98105

@@ -113,11 +120,11 @@ The following has-one routes are registered (using the `comments` relationship o
113120

114121
| URL | Route Name | Controller Action |
115122
| :-- | :-- | :-- |
116-
| `GET /posts/{resource_id}/comments` | `posts.relationships.comments` | `readRelatedResource` |
117-
| `GET /posts/{resource_id}/relationships/comments` | `posts.relationships.comments.read` | `readRelationship` |
118-
| `PATCH /posts/{resource_id}/relationships/comments` | `posts.relationships.comments.replace` | `replaceRelationship` |
119-
| `POST /posts/{resource_id}/relationships/comments` | `posts.relationships.comments.add` | `addToRelationship` |
120-
| `DELETE /posts/{resource_id}/relationships/comments` | `posts.relationships.comments.remove` | `removeFromRelationship` |
123+
| `GET /posts/{resource}/comments` | `posts.relationships.comments` | `readRelatedResource` |
124+
| `GET /posts/{resource}/relationships/comments` | `posts.relationships.comments.read` | `readRelationship` |
125+
| `PATCH /posts/{resource}/relationships/comments` | `posts.relationships.comments.replace` | `replaceRelationship` |
126+
| `POST /posts/{resource}/relationships/comments` | `posts.relationships.comments.add` | `addToRelationship` |
127+
| `DELETE /posts/{resource}/relationships/comments` | `posts.relationships.comments.remove` | `removeFromRelationship` |
121128

122129
To register only some of these, use the `only` or `except` options with the relationship. E.g.
123130

@@ -134,10 +141,28 @@ JsonApi::register('default', ['namespace' => 'Api'], function ($api, $router) {
134141

135142
## Id Constraints
136143

137-
To constrain the `{resource_id}` route parameter for a specific resource, use the `id` option as follows:
144+
To constrain the `{resource}` route parameter for a specific resource, use the `id` option as follows:
138145

139146
```php
140147
JsonApi::register('default', ['namespace' => 'Api'], function ($api, $router) {
141-
$api->resource('posts', ['id' => '[\d]+');
148+
$api->resource('posts', ['id' => '[\d]+']);
149+
});
150+
```
151+
152+
To apply an id constraint to every resource in your API, use the `id` option when registering the API as follows:
153+
154+
```php
155+
JsonApi::register('default', ['namespace' => 'Api', 'id' => '[\d]+'], function ($api, $router) {
156+
$api->resource('posts');
157+
});
158+
```
159+
160+
If using a constraint for the API, you can override it for a specific resource. For example:
161+
162+
```php
163+
JsonApi::register('default', ['namespace' => 'Api', 'id' => '[\d]+'], function ($api, $router) {
164+
$api->resource('posts'); // has the default constraint
165+
$api->resource('comments', ['id' => '[A-Z]+']); // has its own constraint
166+
$api->resource('tags', ['id' => null]); // has no constaint
142167
});
143168
```

docs/upgrade.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ In each JSON API config file you will need to remove the `url-prefix` option and
5151

5252
### Routing
5353

54+
#### Registering APIs
55+
5456
You now need to use `JsonApi::register()` to register routes for an API. This is because the `api()` method
5557
is now used to get an API instance.
5658

@@ -80,6 +82,11 @@ This means when registering your routes, you need to ensure that no prefix has a
8082
Laravel installation has an `api` prefix for API routes and you will need to remove this from your `mapApiRoutes()`
8183
method in your `RouteServiceProvider` if your JSON API routes are being registered in your `routes/api.php` file.
8284

85+
#### Route Parameters
86+
87+
The `resource_id` route parameter has been renamed to `resource`. This is because we are now substituting bindings
88+
so that the `$resource` variable passed to controller actions is the actual record (object), rather than the id.
89+
8390
### Non-Eloquent Controllers
8491

8592
The `ReplyTrait` has been renamed to:

phpunit.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<phpunit bootstrap="vendor/autoload.php"
3-
colors="true">
3+
colors="true"
4+
verbose="true">
45
<testsuites>
56
<testsuite name="Unit">
67
<directory>./tests/Unit/</directory>

src/Api/Repository.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public function createApi($apiName, $host = null)
8080
$rootNamespace,
8181
$resources,
8282
(array) array_get($config, 'codecs'),
83-
$this->normalizeUrl((array) array_get($config, 'url')),
83+
$this->normalizeUrl((array) array_get($config, 'url'), $host),
8484
$byResource,
8585
(bool) array_get($config, 'use-eloquent', true),
8686
array_get($config, 'supported-ext'),

src/Routing/ApiGroup.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ protected function resourceDefaults()
101101
return [
102102
'default-authorizer' => $this->options->get('authorizer'),
103103
'prefix' => $this->api->getUrl()->getNamespace(),
104+
'id' => $this->options->get('id'),
104105
];
105106
}
106107

0 commit comments

Comments
 (0)
0