Skip to content

Commit 4f1e7ce

Browse files
committed
wip
1 parent f7911cc commit 4f1e7ce

16 files changed

+406
-42
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ namespace App\Actions;
5353

5454
use App\Models\User;
5555
use Statix\FormAction\FormAction;
56-
use Statix\FormAction\Validation\Rule;
56+
use Statix\FormAction\Validation\Rules;
5757

5858
class ActionName extends FormAction
5959
{
60-
#[Rule(['required', 'string', 'min:3', 'max:255'])]
60+
#[Rules(['required', 'string', 'min:3', 'max:255'])]
6161
public $name;
6262

63-
#[Rule(['email', 'unique:users,email'])]
63+
#[Rules(['email', 'unique:users,email'])]
6464
public string $email;
6565

6666
public ?string $timezone;

src/Attributes/Computed.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Statix\FormAction\Attributes;
4+
5+
use Attribute;
6+
use Statix\FormAction\FormAction;
7+
use Statix\FormAction\Inspector;
8+
9+
#[Attribute(Attribute::TARGET_PROPERTY)]
10+
class Computed
11+
{
12+
public function __construct(protected ?string $method = null, protected array $parameters = [])
13+
{
14+
//
15+
}
16+
17+
public function getResult(FormAction $action, string $property): mixed
18+
{
19+
$inspector = Inspector::make($action);
20+
21+
if(is_null($this->method)) {
22+
$this->method = 'get'.ucfirst($property).'Property';
23+
}
24+
25+
if (! $inspector->hasMethod($this->method)) {
26+
throw new \Exception("The method {$this->method} does not exist on the action.");
27+
}
28+
29+
$method = $inspector->getMethod($this->method);
30+
31+
$method->setAccessible(true);
32+
33+
if (! $inspector->doesMethodHaveArguments($this->method)) {
34+
$result = $method->invoke($action);
35+
} else {
36+
$result = $method->invoke($action, ...$this->parameters);
37+
}
38+
39+
$method->setAccessible(false);
40+
41+
return $result;
42+
}
43+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace Statix\FormAction\Concerns;
4+
5+
use Statix\FormAction\Attributes\Computed;
6+
use Statix\FormAction\FormAction;
7+
use Statix\FormAction\Inspector;
8+
9+
trait SupportsComputedAttributeFeatures
10+
{
11+
protected $supportComputedProperties = true;
12+
13+
public function dontSupportComputedProperties(): static
14+
{
15+
$this->supportComputedProperties = false;
16+
17+
return $this;
18+
}
19+
20+
public function supportComputedProperties(): static
21+
{
22+
$this->supportComputedProperties = true;
23+
24+
return $this;
25+
}
26+
27+
public function bootSupportsComputedAttributeFeatures(): void
28+
{
29+
if (! $this->supportComputedProperties) {
30+
return;
31+
}
32+
33+
// hook into the beforeAuthorization method
34+
/** @var FormAction $this */
35+
$this->afterValidation(function (FormAction $action) {
36+
37+
// get the inspector
38+
$inspector = Inspector::make($action);
39+
40+
// get the public properties with the Computed attribute
41+
$publicProperties = $inspector->getPublicPropertiesWithAttribute(Computed::class);
42+
43+
// loop through the public properties
44+
foreach ($publicProperties as $property) {
45+
46+
// get the Computed attribute, it does not allow multiple so we can just get the first one
47+
48+
/** @var ReflectionAttribute $computed */
49+
$computed = $inspector->getPropertyAttributes($property, Computed::class)[0];
50+
51+
$result = $computed->newInstance()->getResult($action, $inspector->getPropertyName($property));
52+
53+
// TODO: need to check that the result of the computed attribute is of the correct type and passes validation
54+
$inspector->setPropertyValue($property, $result);
55+
56+
}
57+
58+
});
59+
}
60+
}

src/Concerns/SupportsPublicPropetyMappingFeatures.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Statix\FormAction\Concerns;
44

55
use ReflectionProperty;
6+
use Statix\FormAction\Attributes\Computed;
67
use Statix\FormAction\Inspector;
78

89
trait SupportsPublicPropetyMappingFeatures
@@ -41,6 +42,11 @@ public function attemptToMapValidatedDataToPublicProperties(): static
4142

4243
$propertyName = $inspector->getPropertyName($property);
4344

45+
// skip the property if it has a Computed attribute
46+
if ($inspector->doesPropertyHaveAttributes($property, Computed::class)) {
47+
continue;
48+
}
49+
4450
if (! array_key_exists($propertyName, $validated)) {
4551
continue;
4652
}

src/Concerns/SupportsValidationFeatures.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
use ReflectionProperty;
1010
use Statix\FormAction\FormAction;
1111
use Statix\FormAction\Inspector;
12-
use Statix\FormAction\Validation\Rule;
12+
use Statix\FormAction\Validation\Rules;
1313

1414
trait SupportsValidationFeatures
1515
{
@@ -237,7 +237,7 @@ private function getRulesFromRuleAttributesOnPublicProperties(): array
237237

238238
$inspector = Inspector::make($this);
239239

240-
$properties = $inspector->getPublicPropertiesWithAttribute(Rule::class);
240+
$properties = $inspector->getPublicPropertiesWithAttribute(Rules::class);
241241

242242
foreach ($properties as $property) {
243243

@@ -248,7 +248,7 @@ private function getRulesFromRuleAttributesOnPublicProperties(): array
248248
$types = $inspector->getPropertyTypeHints($property);
249249
}
250250

251-
$attributes = $inspector->getPropertyAttributes($property, Rule::class);
251+
$attributes = $inspector->getPropertyAttributes($property, Rules::class);
252252

253253
foreach ($attributes as $attribute) {
254254
/** @var ReflectionAttribute $attribute */

src/FormAction.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Illuminate\Http\Request;
77
use Statix\FormAction\Concerns\InteractsWithTheRequest;
88
use Statix\FormAction\Concerns\SupportsAuthorizationFeatures;
9+
use Statix\FormAction\Concerns\SupportsComputedAttributeFeatures;
910
use Statix\FormAction\Concerns\SupportsPublicPropetyMappingFeatures;
1011
use Statix\FormAction\Concerns\SupportsValidationFeatures;
1112
use Statix\FormAction\Concerns\SuppportAutomaticAuthorizationValidationOnResolve;
@@ -15,6 +16,7 @@ class FormAction
1516
use InteractsWithTheRequest,
1617
SupportsAuthorizationFeatures,
1718
SupportsValidationFeatures,
19+
SupportsComputedAttributeFeatures,
1820
SupportsPublicPropetyMappingFeatures,
1921
SuppportAutomaticAuthorizationValidationOnResolve;
2022

@@ -25,10 +27,10 @@ public function __construct(protected ?Container $app = null, protected ?Request
2527
}
2628

2729
if (! $request) {
28-
$this->request = request();
30+
$request = request();
2931
}
3032

31-
$this->setRequest($this->request);
33+
$this->setRequest($request);
3234

3335
$this->configure();
3436

src/Inspector.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use ReflectionClass;
66
use ReflectionIntersectionType;
7+
use ReflectionMethod;
78
use ReflectionProperty;
89
use ReflectionUnionType;
910

@@ -218,4 +219,28 @@ public function propertyHasTypehints(ReflectionProperty $property): bool
218219

219220
return true;
220221
}
222+
223+
public function hasMethod(string $method): bool
224+
{
225+
return $this->reflector->hasMethod($method);
226+
}
227+
228+
public function getMethod(string $method): ReflectionMethod
229+
{
230+
return $this->reflector->getMethod($method);
231+
}
232+
233+
public function doesMethodHaveArguments(ReflectionMethod|string $method): bool
234+
{
235+
if (is_string($method) && ! $this->hasMethod($method)) {
236+
return false;
237+
}
238+
239+
if (is_string($method)) {
240+
$method = $this->getMethod($method);
241+
}
242+
243+
/** @var ReflectionMethod $method */
244+
return $method->getNumberOfParameters() > 0;
245+
}
221246
}

src/Validation/BaseRule.php

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/Validation/Rule.php renamed to src/Validation/Rules.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
use Attribute;
66

77
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_PROPERTY)]
8-
class Rule
8+
class Rules
99
{
1010
public function __construct(
11-
public $rule,
11+
public $rules,
1212
protected $message = null,
1313
) {
1414
//
@@ -17,16 +17,16 @@ public function __construct(
1717
public function getRules(): array
1818
{
1919
// if the rule is already an array, we'll assume it's a list of rules and return it as is
20-
if (is_array($this->rule)) {
21-
return $this->rule;
20+
if (is_array($this->rules)) {
21+
return $this->rules;
2222
}
2323

2424
// if the rule is a string, we'll assume it's a single rule and return it as is
25-
if (is_string($this->rule)) {
26-
return [$this->rule];
25+
if (is_string($this->rules)) {
26+
return [$this->rules];
2727
}
2828

2929
// if the rule is an object, we'll assume it's a rule object and return it as is
30-
return [$this->rule];
30+
return [$this->rules];
3131
}
3232
}

tests/ArchTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?php
22

3-
// it('will not use debugging functions')
4-
// ->expect(['dd', 'dump', 'ray'])
5-
// ->each->not->toBeUsed();
3+
it('will not use debugging functions')
4+
->expect(['dd', 'dump', 'ray'])
5+
->each->not->toBeUsed();
66

77
it('will not use final classes')
88
->expect('src\\')

0 commit comments

Comments
 (0)