Skip to content

Commit 18e37d3

Browse files
committed
action 2.1
1 parent 3878642 commit 18e37d3

1 file changed

Lines changed: 163 additions & 65 deletions

File tree

packages/action.md

Lines changed: 163 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Summary
44

5-
This software provides an object oriented convention around [Parameter](https://chevere.org/packages/parameter).
5+
Action implements the Action Design Pattern (a variant of the Command Pattern) that encapsulates operations as reusable, self-validating objects. Built on the [Parameter](https://github.com/chevere/parameter) library, it provides a robust framework for defining business logic with strict input/output validation, promoting type safety and reducing boilerplate code across your application.
66

77
## Installing
88

@@ -14,13 +14,11 @@ composer require chevere/action
1414

1515
## Quick start
1616

17-
Implement ActionInterface by using the [Action trait](#use-actiontrait) or by extending [Action abstract](#extend-action).
18-
19-
## Creating actions
17+
To create an Action class implement the [ActionInterface](https://github.com/chevere/action/blob/2.1/src/Interfaces/ActionInterface.php) either with [use ActionTrait](#use-actiontrait) or by [extends Action](#extends-action).
2018

2119
### Use ActionTrait
2220

23-
Create an action by using ActionTrait.
21+
To create an action by using [ActionTrait](https://github.com/chevere/action/blob/2.1/src/Traits/ActionTrait.php):
2422

2523
```php
2624
use Chevere\Action\Interfaces\ActionInterface;
@@ -33,9 +31,9 @@ class MyAction implements ActionInterface
3331
}
3432
```
3533

36-
### Extend Action
34+
### Extends Action
3735

38-
Create an Action by extending Action.
36+
To create an Action by extending [Action](https://github.com/chevere/action/blob/2.1/src/Action.php):
3937

4038
```php
4139
use Chevere\Action\Action;
@@ -46,16 +44,16 @@ class MyAction extends Action
4644
}
4745
```
4846

49-
## Main method
47+
### Invoke method
5048

51-
Use the `main` method to determine your action's main logic. Use **attributes** from [chevere/parameter](https://github.com/chevere/parameter) on arguments and method return to add validation rules.
49+
Use the `__invoke()` method to determine action main logic. Use **attributes** from [chevere/parameter](https://github.com/chevere/parameter) on both parameters and return to add assertion rules.
5250

53-
* Before validation rules:
51+
* Before assertion rules:
5452

5553
```php
5654
class MyAction
5755
{
58-
protected function main(
56+
public function __invoke(
5957
string $value
6058
): int
6159
{
@@ -64,7 +62,7 @@ class MyAction
6462
}
6563
```
6664

67-
* After validation rules:
65+
* After assertion rules:
6866

6967
```php
7068
use Chevere\Action\Action;
@@ -77,123 +75,223 @@ class MyAction extends Action
7775
#[ReturnAttr(
7876
new IntAttr(min: 0, max: 100)
7977
)]
80-
protected function main(
78+
public function __invoke(
8179
#[StringAttr('/^ok/')]
8280
string $value
8381
): int {
84-
return mb_strlen($value) * 5;
82+
$this->assertArguments($value);
83+
return $this->assertReturn(
84+
mb_strlen($value) * 5
85+
);
8586
}
8687
}
8788
```
8889

89-
## Using actions
90+
The code above demonstrates how to create an Action class with input validation and output assertion. The `$value` argument must match the regular expression `/^ok/` and the return value must be an integer between 0 and 100. See Advanced use for alternative approaches.
91+
92+
## Using Action
9093

91-
Invoke action's main logic passing the arguments you would pass to `main`. Action internal runtime will validate arguments and return against all defined rules.
94+
### Invoking Actions
9295

93-
💡 You can toy with this by running `php demo/demo.php`
96+
Invoke action's `__invoke()` method, same as a function. Action internal runtime will assert arguments and return against your expectations.
97+
98+
💡 You can try by running `php demo/demo.php`
9499

95100
```php
96101
$action = new MyAction();
97-
$result = $action('ok muy bueno');
102+
$result = $action->__invoke('ok muy bueno');
103+
$result = $action('ok muy bueno'); // same thing
104+
```
105+
106+
### ActionName
107+
108+
Actions can use the `public function setUp(...)` method to define logic which must be executed before the Action is invoked.
109+
110+
For example, an Action may define to require setup location and code arguments:
111+
112+
```php
113+
use Chevere\Action\Action;
114+
115+
class Redirect extends Action
116+
{
117+
public function setUp(string $location, int $code): void
118+
{
119+
$this->location = $location;
120+
$this->code = $code;
121+
}
122+
}
123+
```
124+
125+
With `ActionName` you can store the Action name and its `setUp()` arguments:
126+
127+
```php
128+
use Chevere\Action\ActionName;
129+
130+
$actionName = new ActionName(Redirect::class, $location, $code);
131+
```
132+
133+
Which you can later use directly:
134+
135+
```php
136+
$className = (string) $actionName;
137+
$action = new $className();
138+
$action->setUp(...$actionName->arguments());
139+
```
140+
141+
Or craft your own `ActionName` accessors directly on your Actions:
142+
143+
```php
144+
use Chevere\Action\Action;
145+
use Chevere\Action\ActionName;
146+
use Chevere\Action\Interfaces\ActionNameInterface;
147+
148+
class Redirect extends Action
149+
{
150+
// setUp(...)
151+
152+
public static function with(string $location, int $code): ActionNameInterface
153+
{
154+
return new ActionName(static::class, ...get_defined_vars());
155+
}
156+
}
157+
```
158+
159+
Which you can later use like this:
160+
161+
```php
162+
$redirect = Redirect::with('some/location', 302);
98163
```
99164

100165
## Advanced use
101166

102-
### Return method
167+
This library offers flexible validation strategies to match your application's architecture. While embedding assertions within the `__invoke()` method provides maximum portability, you can also implement centralized validation logic or delegate validation responsibilities to callers. The following methods enable fine-grained control over where and how validations are performed across.
168+
169+
### Accept return method
103170

104-
For validating `return` beyond the limitations of PHP's attributes you can define Action's `return()` method. In this context you can use and remix any [Parameter function](https://github.com/chevere/parameter#function-reference).
171+
Use method `acceptReturn()` to define return value assertion rules. In this context you can use and remix any [Parameter function](https://github.com/chevere/parameter#function-reference).
172+
173+
**Note:** Attribute notation `#[ReturnAttr]` has greater precedence than `acceptReturn()`.
105174

106175
```php
107176
use Chevere\Action\Interfaces\ParameterInterface;
108177
use function Chevere\Parameter\string;
109178

110-
public static function return(): ParameterInterface
179+
public static function acceptReturn(): ParameterInterface
111180
{
112181
return string();
113182
}
114183
```
115184

116-
You can also forward parameter resolution to a callable by using `CallableAttr`:
185+
### Assert arguments method
186+
187+
Use method `assertArguments()` to assert Action's `__invoke()` arguments against your expectations. When `assertArguments()` method is called without arguments, it will magic take the arguments from the function caller context.
188+
189+
```php
190+
// magic
191+
$action->assertArguments();
192+
// explicit
193+
$action->assertArguments(...$args);
194+
// explicit, all defined vars
195+
$action->assertArguments(...get_defined_vars());
196+
```
197+
198+
All the following sample definitions are equivalent in results and will evaluate the same arguments.
117199

118200
```php
119-
use Chevere\Action\Attributes\CallableAttr;
120-
use Chevere\Action\Attributes\ReturnAttr;
121-
use function Chevere\Parameter\string;
201+
public function __invoke($foo, $bar): void {
202+
$this->assertArguments();
203+
}
122204

123-
function myCallable(): StringParameterInterface
124-
{
125-
return string();
205+
public function __invoke($foo, $bar): void {
206+
$this->assertArguments($foo, $bar);
126207
}
127208

128-
#[ReturnAttr(
129-
new CallableAttr('myCallable')
130-
)]
131-
protected function main(): string
132-
{
133-
return 'chevere';
209+
public function __invoke($foo, $bar): void {
210+
$this->assertArguments(...get_defined_vars());
134211
}
135212
```
136213

137-
### Custom main method
214+
### Assert return method
138215

139-
Override Action's `mainMethod` to define a custom `main` method to use.
216+
Use method `assertReturn()` to assert Action's `__invoke()` return value against your expectations.
140217

141218
```php
142-
public static function mainMethod(): string
143-
{
144-
return 'altMain';
145-
}
219+
$action->assertReturn($result);
146220
```
147221

148-
## Controller
222+
### Assert method
149223

150-
The Controller component is a special type of Action in charge of handling incoming instructions. Its `main` method only takes parameters of type `string`.
224+
Use method `assert()` to assert runtime rules coherence.
151225

152-
Controller is intended to use them wired to:
226+
```php
227+
$action->assert();
228+
```
153229

154-
* Web Servers
155-
* CLI applications
156-
* Application runners
230+
### Reflection method
157231

158-
### Defining a Controller
232+
Use method `reflection()` to access ReflectionAction instance. It enables to read Action's parameters and return assertion rules.
159233

160-
A Controller implements the `ControllerInterface`. You can extend `Controller` to quick create a compliant Controller:
234+
```php
235+
$action::reflection()->parameters();
236+
$action::reflection()->return();
237+
```
238+
239+
### Accept rules static method
240+
241+
This method is called before `assert*()` calls.
242+
243+
Use method `acceptRulesStatic()` to define additional static assertion rules to constrain your custom Action design. The purpose of this method is demonstrated at the [Controller class](https://github.com/chevere/action/blob/2.1/src/Controller.php) to constrain the design by restricting `__invoke()` parameters to type string.
244+
245+
This is very flexible and allows you to enforce advanced design rules at the class level and for very specific cases, for example to forbid certain parameter names:
161246

162247
```php
163-
use Chevere\Action\Controller;
248+
public static function acceptRulesStatic(): void {
249+
if(static::reflection()->parameters()->has('lucho')) {
250+
throw new LogicException('Parameter $lucho is forbidden');
251+
}
252+
}
253+
```
164254

165-
class SomeController extends Controller
255+
### Accept rules runtime method
256+
257+
This method is called before `assert*()` calls.
258+
259+
Use method `acceptRulesRuntime()` to define additional runtime assertion rules. The purpose of this method is demonstrated at the [HTTP Controller](https://github.com/chevere/http) to constrain the design by ensuring the existence of HTTP participants.
260+
261+
```php
262+
public function acceptRulesRuntime(): void
166263
{
167-
// ...
264+
if (! isset($this->_query, $this->_bodyParsed, $this->_files)) {
265+
throw new LogicException('Server request not set.');
266+
}
168267
}
169268
```
170269

171-
### Main Parameters
270+
## Controller
172271

173-
Parameters are defined in the `main` method but it just takes strings.
272+
The Controller is a special type of Action in charge of handling command-like instructions. Its `__invoke()` method only takes parameters of type `string|int|float`.
273+
274+
### Defining a Controller
275+
276+
A Controller implements the `ControllerInterface`. You can extend `Controller` to quick create a compliant Controller:
174277

175278
```php
176-
public function main(
177-
string $pepito,
178-
string $paysTwice
179-
): array
279+
use Chevere\Controller\Controller;
280+
281+
class SomeController extends Controller
180282
{
181283
// ...
182284
}
183285
```
184286

185-
## Parameter Attributes
287+
### Invoke parameters
186288

187-
Use `StringAttr` to validate string:
289+
Parameters are defined in the `__invoke()` method but it just takes type `string`, `int`, or `float`.
188290

189291
```php
190-
use Chevere\Parameter\Attributes\StringAttr;
191-
192-
public function main(
193-
#[StringAttr('/^[a-z]$/')]
292+
public function __invoke(
194293
string $pepito,
195-
#[StringAttr('/^[a-zA-Z]+$/')]
196-
string $paysTwice
294+
int $paysTwice
197295
): array
198296
{
199297
// ...

0 commit comments

Comments
 (0)