Skip to content

Commit

Permalink
Add support for when; (#22)
Browse files Browse the repository at this point in the history
Co-authored-by: david_smith <[email protected]>
  • Loading branch information
zero-to-prod and david_smith authored Nov 19, 2024
1 parent ecab046 commit 076e226
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 0 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,35 @@ class DataModelHelper

## Helper Methods

- [when](#when): Create a map of any type by using
- [mapOf](#mapof): Create a map of any type by using
- [pregReplace](#pregreplace): Perform a regular expression search and replace.
- [pregMatch](#pregmatch): Perform a regular expression match.
- [isUrl](#isurl): Validates a url.
- [isEmail](#isemail): Validates an email.
- [isMultiple](#ismultiple): Validate a value is a multiple of another.

### `when`

Use `when` to call a function based on a condition.

```php
class User
{
use \Zerotoprod\DataModel\DataModel;
use \Zerotoprod\DataModelHelper\DataModelHelper;

#[Describe([
'cast' => [self::class, 'when'],
'eval' => '$value >= $context["value_2"]' // The expression to evaluate.
'true' => [MyAction::class, 'passed'], // Optional. Invoked when condition is true.
'false' => [MyAction::class, 'failed'], // Optional. Invoked when condition is true.
'required', // Throws PropertyRequiredException when value not present.
])]
public string $value;
}
```

### `mapOf`

Create a map of any type by using the `DataModelHelper::mapOf()` method.
Expand Down
36 changes: 36 additions & 0 deletions src/DataModelHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -314,4 +314,40 @@ public static function isMultiple(mixed $value, array $context, ?ReflectionAttri

return $value;
}

/**
* Determine if a given value is a valid URL.
* ```
* #[Describe([
* 'cast' => [self::class, 'when'],
* 'eval' => '$value >= $context["value_2"]' // The expression to evaluate.
* 'true' => [MyAction::class, 'passed'], // Optional. Invoked when condition is true.
* 'false' => [MyAction::class, 'failed'], // Optional. Invoked when condition is true.
* 'required', // Throws PropertyRequiredException when value not present.
* ])]
* ```
*/
public static function when(mixed $value, array $context, ?ReflectionAttribute $Attribute, ReflectionProperty $Property): ?string
{
$args = $Attribute?->getArguments()[0];
if ((!empty($args['required']) || in_array('required', $args, true))
&& !isset($context[$Property->getName()])
) {
throw new PropertyRequiredException("Property `\${$Property->getName()}` is required.");
}

if (!$value && $Property->getType()?->allowsNull()) {
return null;
}

if (eval("return {$args['eval']};")) {
if (isset($args['true'])) {
return call_user_func($args['true'], $value, $context, $Attribute, $Property);
}
} elseif (isset($args['false'])) {
return call_user_func($args['false'], $value, $context, $Attribute, $Property);
}

return $value;
}
}
27 changes: 27 additions & 0 deletions tests/Unit/When/Eval/EvalTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Tests\Unit\When\Eval;

use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

class EvalTest extends TestCase
{
#[Test] public function true(): void
{
$User = User::from([
User::value => 100,
]);

self::assertEquals(1, $User->value);
}

#[Test] public function false(): void
{
$User = User::from([
User::value => 2,
]);

self::assertEquals(0, $User->value);
}
}
32 changes: 32 additions & 0 deletions tests/Unit/When/Eval/User.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Tests\Unit\When\Eval;

use Zerotoprod\DataModel\DataModel;
use Zerotoprod\DataModel\Describe;
use Zerotoprod\DataModelHelper\DataModelHelper;

class User
{
use DataModel;
use DataModelHelper;

public const value = 'value';

#[Describe([
'cast' => [self::class, 'when'],
'eval' => '$value >= 10',
'true' => [self::class, 'true'],
'false' => [self::class, 'false'],
])]
public int $value;

public static function true(): int
{
return 1;
}
public static function false(): int
{
return 0;
}
}
18 changes: 18 additions & 0 deletions tests/Unit/When/Nullable/NullTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Tests\Unit\When\Nullable;

use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

class NullTest extends TestCase
{
#[Test] public function valid(): void
{
$User = User::from([
User::value => null,
]);

self::assertNull($User->value);
}
}
25 changes: 25 additions & 0 deletions tests/Unit/When/Nullable/User.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Tests\Unit\When\Nullable;

use Zerotoprod\DataModel\DataModel;
use Zerotoprod\DataModel\Describe;
use Zerotoprod\DataModelHelper\DataModelHelper;

class User
{
use DataModel;
use DataModelHelper;

public const value = 'value';

#[Describe([
'cast' => [self::class, 'when'],
])]
public ?int $value;

public static function false(): int
{
return 0;
}
}
17 changes: 17 additions & 0 deletions tests/Unit/When/Required/RequiredTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Tests\Unit\When\Required;

use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
use Zerotoprod\DataModel\PropertyRequiredException;

class RequiredTest extends TestCase
{
#[Test] public function required(): void
{
$this->expectException(PropertyRequiredException::class);
$this->expectExceptionMessage('Property `$value` is required.');
User::from();
}
}
21 changes: 21 additions & 0 deletions tests/Unit/When/Required/User.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Tests\Unit\When\Required;

use Zerotoprod\DataModel\DataModel;
use Zerotoprod\DataModel\Describe;
use Zerotoprod\DataModelHelper\DataModelHelper;

class User
{
use DataModel;
use DataModelHelper;

public const value = 'value';

#[Describe([
'cast' => [self::class, 'when'],
'required'
])]
public string $value;
}
31 changes: 31 additions & 0 deletions tests/Unit/When/Value/User.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Tests\Unit\When\Value;

use Zerotoprod\DataModel\DataModel;
use Zerotoprod\DataModel\Describe;
use Zerotoprod\DataModelHelper\DataModelHelper;

class User
{
use DataModel;
use DataModelHelper;

public const value = 'value';

#[Describe([
'cast' => [self::class, 'when'],
'eval' => '$value >= 10',
'false' => [self::class, 'false'],
])]
public int $value;

public static function true(): int
{
return 1;
}
public static function false(): int
{
return 0;
}
}
18 changes: 18 additions & 0 deletions tests/Unit/When/Value/ValueTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Tests\Unit\When\Value;

use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

class ValueTest extends TestCase
{
#[Test] public function value(): void
{
$User = User::from([
User::value => 100,
]);

self::assertEquals(100, $User->value);
}
}

0 comments on commit 076e226

Please sign in to comment.