Skip to content

Commit f1aafde

Browse files
committed
docs: Update advanced user guide section
1 parent 0ed04ef commit f1aafde

File tree

12 files changed

+278
-42
lines changed

12 files changed

+278
-42
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ wp-content/
44
*.cache
55
/tests/coverage
66
/coverage.xml
7+
docs/site/
78

89
# Created by https://www.toptal.com/developers/gitignore/api/composer,sublimetext,linux
910
# Edit at https://www.toptal.com/developers/gitignore?templates=composer,sublimetext,linux

docs/docs/advanced-user-guide/dependency-injection.md renamed to docs/docs/advanced-user-guide/dependency-injection/index.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ With dependency injection our endpoints do look much cleaner ✨🧹
88
=== "With dependency injection"
99

1010
```php
11+
<?php
1112
// We only need the ID. So we type $ID
12-
$router->get('/posts/(?P<ID>[\d]+)', function ($ID) {
13+
$router->get('/posts/(?P<ID>[\d]+)', function (int $ID) {
1314
return get_post($ID);
1415
});
1516

@@ -23,6 +24,7 @@ With dependency injection our endpoints do look much cleaner ✨🧹
2324
=== "No dependency injection"
2425

2526
```php
27+
<?php
2628
// Unable to fetch a dynamic parameter. Have to work with the $request argument
2729
$router->get('/posts/(?P<ID>[\d]+)', function ($request) {
2830
return get_post($request->get('ID'));
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
A common scenario while creating API's is the need to share resources or logic across multiple endpoints or handlers.
2+
This is where *injectables* can be useful.
3+
4+
```php hl_lines="5 12"
5+
<?php
6+
use Attributes\Validation\Validatable;
7+
use Attributes\Wp\FastEndpoints\Options\Inject;
8+
9+
$router->inject('user', function (): WP_User {
10+
if (! is_user_logged_in()) {
11+
return new WpError(401, "Ups! You need to login first");
12+
}
13+
return wp_get_current_user();
14+
});
15+
16+
$router->get('/posts', function (#[Inject] WP_User $user) {
17+
// Your logic
18+
}));
19+
20+
$router->get('/user', function (#[Inject] WP_User $user) {
21+
// Your logic
22+
});
23+
```
24+
25+
#### Function names
26+
27+
If an injectable is not found, FastEndpoints will try to look-up for a function with the same property name or specified
28+
injectable name.
29+
30+
```php hl_lines="4"
31+
<?php
32+
function hello_world() { return "Hello world!"; }
33+
34+
$router->get('/hello', function (#[Inject('hello_world')] string $hello) {
35+
return $hello;
36+
}));
37+
```
38+
39+
#### How it resolves injectables?
40+
41+
Each injectable is only resolved once while handling a request. For instance, if the same injectable is used in both
42+
the permission callback and the endpoint, it will first be resolved during the permission callback and then re-use the
43+
cached value for subsequent calls.
44+
45+
```php hl_lines="9 12"
46+
<?php
47+
$router->inject('nextNumber', function (): int {
48+
global $nextNumber;
49+
$randomNumber = ($randomNumber ?: 0) + 1;
50+
return $randomNumber;
51+
});
52+
53+
$router->get('/posts', function (#[Inject] int $nextNumber) {
54+
// $nextNumber will be 1
55+
}))
56+
->permission(function(#[Inject] int $nextNumber) {
57+
// $nextNumber will be 1
58+
return true;
59+
});
60+
```
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
As you might be already familiar WP-FastEndpoints allows you to fetch request data via function arguments, and optionally
2+
you can validate that data via type-hinting.
3+
4+
### Built-in type-hints
5+
6+
By default, when you rely on built-in PHP types, DateTime, DateTimeInterface or when no type-hint is used,
7+
WP-FastEndpoints will look for the data in the url or in the query parameters, in this order. However, if desired this
8+
behaviour can be changed via *#[Attributes]*.
9+
10+
=== "Looks-up for URL or query parameter"
11+
12+
```php
13+
<?php
14+
$router->get('/posts', function (int $ID) {
15+
// Your logic
16+
});
17+
```
18+
19+
=== "Looks-up for a header"
20+
21+
```php hl_lines="4"
22+
<?php
23+
use Attributes\Wp\FastEndpoints\Options\Header;
24+
25+
$router->get('/user', function (#[Header('Authorization')] string $token) {
26+
// Your logic
27+
});
28+
```
29+
30+
The following is a list of all those type-hints:
31+
32+
- bool
33+
- int
34+
- float
35+
- string
36+
- callable
37+
- array
38+
- object
39+
- [*DateTime*](https://www.php.net/manual/en/class.datetime.php)
40+
- [*DateTimeInterface*](https://www.php.net/manual/en/class.datetimeinterface.php)
41+
42+
### Custom classes
43+
44+
For more complex types of data structures we can rely on custom classes. If you know how to write a class you know how
45+
to validate a request in WP-FastEndpoints.
46+
47+
By default, when you rely on type-hints from custom classes WP-FastEndpoints will look for the data either in the
48+
JSON or body of the current request. However, as with built-in type-hints, this behaviour can be changed via *#[Attributes]*.
49+
50+
```php
51+
<?php
52+
class HelloWorld {
53+
public string $name;
54+
}
55+
```
56+
57+
=== "Looks-up for JSON or body parameters"
58+
59+
```php
60+
<?php
61+
$router->post('/hello', function (HelloWorld $hello) {
62+
// Your logic
63+
});
64+
```
65+
66+
=== "Looks-up for URL or query parameters"
67+
68+
```php hl_lines="5"
69+
<?php
70+
use Attributes\Wp\FastEndpoints\Options\Url;
71+
use Attributes\Wp\FastEndpoints\Options\Query;
72+
73+
$router->post('/hello', function (#[Url, Query] HelloWorld $hello) {
74+
// Your logic
75+
});
76+
```
77+
78+
#### How to type-hint arrays?
79+
80+
Unfortunately, PHP doesn't provide a way to properly type-hint array's. Dictionaries aren't an issue because custom classes
81+
can be used, but for sequenced arrays is a bit more tricky.
82+
83+
To solve this issue, WP-FastEndpoints assumes that any [*ArrayObject*](https://www.php.net/manual/en/class.arrayobject.php)
84+
child class should be considered a *typed-hint* array. However, to actually type-hint that array a property name `$type`
85+
with a type-hint needs to be specified in the class. Otherwise, an array of `mixed` is assumed.
86+
87+
```php hl_lines="3"
88+
<?php
89+
class HelloArr extends ArrayObject {
90+
public Hello $type;
91+
}
92+
93+
$router->get('/say-hello', function (HelloArr $allHellos) {
94+
foreach ($allHellos as $hello) {
95+
echo "Hello $hello->name!";
96+
}
97+
});
98+
```
99+
100+
!!! tip
101+
[attributes-php/validation](https://packagist.org/packages/Attributes-PHP/validation) provides some typed-arrays,
102+
like: 1) [*BoolArr*](https://github.com/Attributes-PHP/validation/blob/main/src/Types/BoolArr.php),
103+
2) [*IntArr*](https://github.com/Attributes-PHP/validation/blob/main/src/Types/IntArr.php),
104+
3) [*StrArr*](https://github.com/Attributes-PHP/validation/blob/main/src/Types/StrArr.php) and
105+
[others](https://github.com/Attributes-PHP/validation/tree/main/src/Types).
106+
107+
### Special classes
108+
109+
When an argument has a type-hint it usually means that the request payload should follow that class structure. However,
110+
there are three special classes which don't follow this pattern:
111+
112+
- [*WP_REST_Request*](https://developer.wordpress.org/reference/classes/wp_rest_request/) - Can be used to retrieve the
113+
current request
114+
- [*WP_REST_Response*](https://developer.wordpress.org/reference/classes/wp_rest_response/) - Can be used to retrieve
115+
the response to be sent to the client, in case of a success. You could change the response HTTP status code or the data
116+
which is sent to the client (wouldn't recommend the last one though).
117+
- [*Endpoint*](https://github.com/Attributes-PHP/wp-fastendpoints/blob/main/src/Endpoint.php#L44) - The current endpoint
118+
instance. You shouldn't ever need this one.
119+
120+
=== "Get current request"
121+
122+
```php
123+
<?php
124+
$router->get('/request', function (WP_REST_Request $request) {
125+
return $request->get_params();
126+
});
127+
```
128+
129+
=== "Change HTTP status code of a response"
130+
131+
```php
132+
<?php
133+
$router->get('/response/204', function (WP_REST_Response $response) {
134+
$response->set_status(204);
135+
});
136+
```
137+
138+
### Required vs optional properties
139+
140+
Until now, we have seen most how to specify required properties from a request payload. In case your property is optional
141+
you can rely on default values for that, like the following.
142+
143+
=== "Required property"
144+
145+
```php hl_lines="2"
146+
<?php
147+
$router->get('/required', function (int $id) {
148+
return $id;
149+
});
150+
```
151+
152+
=== "Optional property"
153+
154+
```php hl_lines="2"
155+
<?php
156+
$router->get('/optional', function (int $id = 0) {
157+
return $id;
158+
});
159+
```
160+
161+
=== "Optional payload"
162+
163+
```php hl_lines="2"
164+
<?php
165+
$router->get('/payload/optional', function (?HelloWorld $hello = null) {
166+
return $hello ? $hello->name : "No payload";
167+
});
168+
```
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
The [Quick Start](/wp-fastendpoints/quick-start/) should be able to get you a feel of
2-
the main features of WP-FastEndpoints.
1+
The [Quick Start](/wp-fastendpoints/quick-start/) should be able to get you a feel of the main features of WP-FastEndpoints.
32

4-
However, it's possible that the solution for your use case might not be in the Quick Start
5-
tutorial. In the next sections we will take a look at further functionalities that
6-
WP-FastEndpoints provides.
3+
However, it's possible what you are looking for might not be available in the Quick Start guide.
4+
In the next sections we will take a look at further functionalities that WP-FastEndpoints provides.

docs/docs/advanced-user-guide/middlewares.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,5 @@ $router->get('/test', function () {
4343
## Responses
4444

4545
If you need you can also take advantage of either WP_Error and WP_REST_Response to send
46-
a direct response to the client. See [Responses page](/wp-fastendpoints/advanced-user-guide/responses)
46+
a direct response to the client. See [Responses page](/advanced-user-guide/responses)
4747
for more info
Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
In WP-FastEndpoints an endpoint can have multiple optional handlers attached to:
22

33
1. Permission handlers via `hasCap(...)` or `permission(...)` - Used to check for user permissions
4-
2. Middlewares
5-
1. Request Payload Schema Middleware via `schema(...)` - Validates the request payload
6-
2. Response Schema Middleware via `returns(...)` - Makes sure that the proper response is sent to the client
7-
3. Custom middlewares via `middleware(...)` - Any other custom logic that you might want to run
4+
2. Middlewares via `middleware(...)`
5+
1. Running before the handler being called and/or
6+
2. Running after the handler being called
87

98
## Permission handlers
109

1110
When a request is received the first handlers to run are the permissions handlers. Permission handlers are called
12-
by WordPress via [`permission_callback`](https://developer.wordpress.org/rest-api/extending-the-rest-api/routes-and-endpoints/#callbacks).
11+
by WordPress via [*`permission_callback`*](https://developer.wordpress.org/rest-api/extending-the-rest-api/routes-and-endpoints/#callbacks).
1312

1413
In contrast to WordPress, you can have one or multiple permission handlers attached to the same endpoint.
1514

1615
???+ note
17-
In the background all permission handlers are wrapped into one callable which is later on used as
16+
In the background all permission handlers are wrapped into one callable which is later on used as the
1817
`permission_callback` by the endpoint
1918

2019
These handlers will then be called in the same order as they were attached. For instance:
2120

2221
```php
22+
<?php
2323
$router->get('/test', function () {return true;})
24-
->hasCap('read') # Called first
25-
->hasCap('edit_posts') # Called second if the first one was successful
26-
->permission('__return_true') # Called last if both the first and second were successful
24+
->hasCap('read') # Called first
25+
->hasCap('edit_posts') # Called second if the first one was successful
26+
->permission('__return_true') # Called last if both the first and second were successful
2727
```
2828

2929
## Middlewares
@@ -32,7 +32,7 @@ If all the permission handlers are successful the next set of handlers that run
3232
implement the `onRequest` function.
3333

3434
Remember that a middleware can implement `onRequest` and/or `onResponse` functions. The first one, runs before
35-
the main endpoint handler and the later one should run after the main endpoint handler.
35+
the main endpoint handler and the later one runs after the main endpoint handler.
3636

3737
!!! warning
3838
Please bear in mind that if either a [WP_Error](https://developer.wordpress.org/reference/classes/wp_error/) or
@@ -45,6 +45,7 @@ the main endpoint handler and the later one should run after the main endpoint h
4545
Same as with the permission handlers, middlewares are called with the same order that they were attached.
4646

4747
```php
48+
<?php
4849
class OnRequestMiddleware extends \Attributes\Wp\FastEndpoints\Contracts\Middleware
4950
{
5051
public function onRequest(/* Type what you need */){
@@ -53,23 +54,26 @@ class OnRequestMiddleware extends \Attributes\Wp\FastEndpoints\Contracts\Middlew
5354
}
5455

5556
$router->post('/test', function () {return true;})
56-
->middleware(OnRequestMiddleware()) # Called first
57-
->schema('Basics/Bool'); # Called second
57+
->middleware(OnRequestMiddleware()) # Called first
58+
->middleware(DoSomethingMiddleware()); # Called second
5859
```
5960

6061
### onResponse
6162

62-
Likewise, middlewares implementing onResponse functions will be triggered in the same order as they were attached.
63+
Likewise, middlewares implementing `onResponse` functions will be triggered in the same order as they were attached.
6364

6465
```php
66+
<?php
67+
use Attributes\Validation\Types\IntArr;
68+
6569
class OnResponseMiddleware extends \Attributes\Wp\FastEndpoints\Contracts\Middleware
6670
{
6771
public function onResponse(/* Type what you need */){
6872
return;
6973
}
7074
}
7175

72-
$router->post('/test', function () {return true;})
73-
->returns('Basics/Bool') # Called first
74-
->middleware(OnResponseMiddleware()); # Called second
76+
$router->post('/test', function () {return [1,2,3];})
77+
->returns(IntArr::class) # Called first
78+
->middleware(OnResponseMiddleware()); # Called second
7579
```

0 commit comments

Comments
 (0)