Skip to content

Commit 20f950d

Browse files
committed
Add documentation for Invokable Engine annotations
- Created detailed markdown files for various annotations including #[Cast], #[DefaultValue], #[Explode], #[In], #[MapValue], #[Regex], #[Required], #[Sanitize], #[Scope], #[SkipIf], and #[Trim]. - Each annotation includes sections for parameters, usage examples, behavior, and combining with other attributes. - Updated index.md to include an overview of the Invokable Engine and its features, along with a detailed explanation of the attribute pipeline and lifecycle.
1 parent d081281 commit 20f950d

16 files changed

Lines changed: 1609 additions & 1 deletion

File tree

docs/.vuepress/config.js

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,75 @@ export default defineUserConfig({
7575
children: [
7676
{
7777
text: "Invokable",
78-
link: "engines/invokable",
78+
collapsible: true,
79+
children: [
80+
{
81+
text: "Overview",
82+
link: "engines/invokable/",
83+
},
84+
{
85+
text: "Annotations",
86+
collapsible: true,
87+
children: [
88+
{
89+
text: "Overview",
90+
link: "engines/invokable/annotations/",
91+
},
92+
{
93+
text: "Authorize",
94+
link: "engines/invokable/annotations/authorize",
95+
},
96+
{
97+
text: "SkipIf",
98+
link: "engines/invokable/annotations/skip-if",
99+
},
100+
{
101+
text: "Trim",
102+
link: "engines/invokable/annotations/trim",
103+
},
104+
{
105+
text: "Sanitize",
106+
link: "engines/invokable/annotations/sanitize",
107+
},
108+
{
109+
text: "Cast",
110+
link: "engines/invokable/annotations/cast",
111+
},
112+
{
113+
text: "DefaultValue",
114+
link: "engines/invokable/annotations/default-value",
115+
},
116+
{
117+
text: "MapValue",
118+
link: "engines/invokable/annotations/map-value",
119+
},
120+
{
121+
text: "Explode",
122+
link: "engines/invokable/annotations/explode",
123+
},
124+
{
125+
text: "Required",
126+
link: "engines/invokable/annotations/required",
127+
},
128+
{
129+
text: "In",
130+
link: "engines/invokable/annotations/in",
131+
},
132+
{
133+
text: "Between",
134+
link: "engines/invokable/annotations/between",
135+
},
136+
{
137+
text: "Regex",
138+
link: "engines/invokable/annotations/regex",
139+
},
140+
{
141+
text: "Scope",
142+
link: "engines/invokable/annotations/scope",
143+
},
144+
],
145+
},
146+
],
79147
},
80148
{
81149
text: "Tree",
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
sidebarDepth: 1
3+
---
4+
5+
# #[Authorize]
6+
7+
**Stage:** `CONTROL` (1)
8+
9+
Requires authorization before the filter method executes. If authorization fails, the filter is skipped entirely.
10+
11+
---
12+
13+
## Parameters
14+
15+
| Parameter | Type | Required | Description |
16+
| ------------ | -------- | -------- | ------------------------------------------------------------------- |
17+
| `$authorize` | `string` || Fully qualified class name implementing the `Authorizable` contract |
18+
19+
---
20+
21+
## Usage
22+
23+
```php
24+
use Kettasoft\Filterable\Engines\Foundation\Attributes\Annotations\Authorize;
25+
26+
#[Authorize(AdminOnly::class)]
27+
protected function secretField(Payload $payload)
28+
{
29+
return $this->builder->where('secret_field', $payload->value);
30+
}
31+
```
32+
33+
---
34+
35+
## Authorizable Contract
36+
37+
The class passed to `#[Authorize]` must implement `Kettasoft\Filterable\Contracts\Authorizable`:
38+
39+
```php
40+
<?php
41+
42+
namespace App\Filters\Authorizations;
43+
44+
use Kettasoft\Filterable\Contracts\Authorizable;
45+
46+
class AdminOnly implements Authorizable
47+
{
48+
public function authorize(): bool
49+
{
50+
return auth()->user()?->is_admin ?? false;
51+
}
52+
}
53+
```
54+
55+
---
56+
57+
## Behavior
58+
59+
| Scenario | Result |
60+
| -------------------------------------- | ------------------------------------------------- |
61+
| `authorize()` returns `true` | Filter method executes normally |
62+
| `authorize()` returns `false` | Filter is **skipped** (SkipExecution is thrown) |
63+
| Class doesn't implement `Authorizable` | `InvalidArgumentException` is thrown |
64+
65+
---
66+
67+
## Example: Role-Based Filter Access
68+
69+
```php
70+
class RoleFilter implements Authorizable
71+
{
72+
public function authorize(): bool
73+
{
74+
return auth()->user()?->hasRole('manager');
75+
}
76+
}
77+
78+
// In your filter class:
79+
#[Authorize(RoleFilter::class)]
80+
protected function salary(Payload $payload)
81+
{
82+
return $this->builder->where('salary', '>=', $payload->value);
83+
}
84+
```
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
sidebarDepth: 1
3+
---
4+
5+
# #[Between]
6+
7+
**Stage:** `VALIDATE` (3)
8+
9+
Validates that the payload value falls within a specified numeric range. If the value is outside the range or not numeric, the filter is **skipped**.
10+
11+
---
12+
13+
## Parameters
14+
15+
| Parameter | Type | Required | Description |
16+
| --------- | ------------- | -------- | --------------------- |
17+
| `$min` | `float\|int` || Minimum allowed value |
18+
| `$max` | `float\|int` || Maximum allowed value |
19+
20+
---
21+
22+
## Usage
23+
24+
### Integer Range
25+
26+
```php
27+
use Kettasoft\Filterable\Engines\Foundation\Attributes\Annotations\Between;
28+
29+
#[Between(min: 1, max: 100)]
30+
protected function views(Payload $payload)
31+
{
32+
return $this->builder->where('views', '>=', $payload->value);
33+
}
34+
```
35+
36+
### Float Range
37+
38+
```php
39+
#[Between(min: 0.0, max: 5.0)]
40+
protected function rating(Payload $payload)
41+
{
42+
return $this->builder->where('rating', '>=', $payload->value);
43+
}
44+
```
45+
46+
---
47+
48+
## Behavior
49+
50+
| Scenario | Result |
51+
| ----------------------------------- | ------------------------------------ |
52+
| Value is numeric and within range | Filter executes normally |
53+
| Value is at the minimum boundary | Filter executes normally (**inclusive**) |
54+
| Value is at the maximum boundary | Filter executes normally (**inclusive**) |
55+
| Value is below the range | Filter is **skipped** |
56+
| Value is above the range | Filter is **skipped** |
57+
| Value is not numeric | Filter is **skipped** |
58+
59+
---
60+
61+
## Boundary Behavior
62+
63+
The check is **inclusive** on both ends:
64+
65+
```php
66+
#[Between(min: 1, max: 100)]
67+
// 1 → ✅ passes
68+
// 50 → ✅ passes
69+
// 100 → ✅ passes
70+
// 0 → ❌ skipped
71+
// 101 → ❌ skipped
72+
```
73+
74+
---
75+
76+
## Combining with Other Attributes
77+
78+
```php
79+
#[SkipIf('empty')]
80+
#[Trim]
81+
#[Between(min: 1, max: 1000)]
82+
protected function price(Payload $payload)
83+
{
84+
return $this->builder->where('price', '>=', $payload->value);
85+
}
86+
```
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
sidebarDepth: 1
3+
---
4+
5+
# #[Cast]
6+
7+
**Stage:** `TRANSFORM` (2)
8+
9+
Casts the payload value to a specific type using the Payload's `as*` methods.
10+
11+
---
12+
13+
## Parameters
14+
15+
| Parameter | Type | Required | Description |
16+
| --------- | -------- | -------- | ---------------------------------------------------- |
17+
| `$type` | `string` || The target type name (maps to `Payload::as{Type}()`) |
18+
19+
---
20+
21+
## Supported Types
22+
23+
| Type | Maps To | Description |
24+
| --------- | ----------------------- | ------------------------------------ |
25+
| `int` | `$payload->asInt()` | Cast to integer |
26+
| `boolean` | `$payload->asBoolean()` | Cast to boolean |
27+
| `array` | `$payload->asArray()` | Decode JSON or return existing array |
28+
| `carbon` | `$payload->asCarbon()` | Parse to Carbon date instance |
29+
| `slug` | `$payload->asSlug()` | Convert to URL-friendly slug |
30+
| `like` | `$payload->asLike()` | Wrap with `%` for LIKE queries |
31+
32+
---
33+
34+
## Usage
35+
36+
### Cast to Integer
37+
38+
```php
39+
use Kettasoft\Filterable\Engines\Foundation\Attributes\Annotations\Cast;
40+
41+
#[Cast('int')]
42+
protected function views(Payload $payload)
43+
{
44+
// "42" → 42
45+
return $this->builder->where('views', '>=', $payload->value);
46+
}
47+
```
48+
49+
### Cast to Boolean
50+
51+
```php
52+
#[Cast('boolean')]
53+
protected function isFeatured(Payload $payload)
54+
{
55+
// "true" → true, "false" → false
56+
return $this->builder->where('is_featured', $payload->value);
57+
}
58+
```
59+
60+
### Cast to Array (from JSON)
61+
62+
```php
63+
#[Cast('array')]
64+
protected function tags(Payload $payload)
65+
{
66+
// '["php","laravel"]' → ['php', 'laravel']
67+
$tags = $payload->value;
68+
return $this->builder->whereIn('tag', $tags);
69+
}
70+
```
71+
72+
---
73+
74+
## Behavior
75+
76+
| Scenario | Result |
77+
| -------------------------- | ------------------------------- |
78+
| Cast type is supported | Value is cast and returned |
79+
| Cast type is not supported | `StrictnessException` is thrown |
80+
| Cast fails (invalid value) | `StrictnessException` is thrown |
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
sidebarDepth: 1
3+
---
4+
5+
# #[DefaultValue]
6+
7+
**Stage:** `TRANSFORM` (2)
8+
9+
Sets a fallback value when the payload value is empty or null. The filter method still executes, but with the default value instead of the empty input.
10+
11+
---
12+
13+
## Parameters
14+
15+
| Parameter | Type | Required | Description |
16+
| --------- | ------- | -------- | ------------------------------------ |
17+
| `$value` | `mixed` || The default value to use as fallback |
18+
19+
---
20+
21+
## Usage
22+
23+
```php
24+
use Kettasoft\Filterable\Engines\Foundation\Attributes\Annotations\DefaultValue;
25+
26+
#[DefaultValue('active')]
27+
protected function status(Payload $payload)
28+
{
29+
// If status is empty → uses "active"
30+
return $this->builder->where('status', $payload->value);
31+
}
32+
```
33+
34+
### With Numeric Default
35+
36+
```php
37+
#[DefaultValue(10)]
38+
protected function perPage(Payload $payload)
39+
{
40+
// If perPage is empty → uses 10
41+
return $this->builder->limit($payload->value);
42+
}
43+
```
44+
45+
---
46+
47+
## Behavior
48+
49+
| Scenario | Result |
50+
| ------------------------------ | ----------------------------------------- |
51+
| Value is empty or null | Payload value is set to the default |
52+
| Value is provided (non-empty) | Default is **not** applied, original kept |
53+
54+
---
55+
56+
## Combining with Other Attributes
57+
58+
```php
59+
#[DefaultValue('pending')]
60+
#[In('active', 'pending', 'archived')]
61+
protected function status(Payload $payload)
62+
{
63+
// Empty input → "pending" → passes In validation
64+
return $this->builder->where('status', $payload->value);
65+
}
66+
```

0 commit comments

Comments
 (0)