Skip to content

Commit 9e4fc3d

Browse files
committed
Merge branch 'value_objects' into 'master'
Value objects base class for consistant re-usability See merge request composer/Laravel-Helpers!17
2 parents 80133b3 + 1e05dbb commit 9e4fc3d

File tree

10 files changed

+318
-31
lines changed

10 files changed

+318
-31
lines changed

.DS_Store

6 KB
Binary file not shown.

.gitlab-ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ include:
33
file: "php-packages.latest.yml"
44

55
variables:
6+
TEST_PHP_8_3: "true"
67
TEST_PHP_8_2: "true"
78
TEST_PHP_8_1: "true"
8-
TEST_PHP_8_0: "true"
9-
9+
TEST_PHP_8_0: "false"

.lando.yml

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ config:
55
php: "8.2"
66

77
services:
8+
9+
# php83:
10+
# type: php:8.3
11+
# via: "cli"
12+
813
php81:
914
type: php:8.1
1015
via: "cli"
1116

12-
php80:
13-
type: php:8.0
14-
via: "cli"
1517

1618
tooling:
1719
cleanup:
@@ -21,60 +23,73 @@ tooling:
2123
service: php81
2224
cmd: rm -rf vendor composer.lock && composer install
2325

24-
setup80:
25-
service: php80
26+
setup82:
27+
service: php82
2628
cmd: rm -rf vendor composer.lock && composer install
2729

28-
setup82:
30+
setup83:
2931
service: appserver
3032
cmd: rm -rf vendor composer.lock && composer install
3133

32-
test82:
33-
service: appserver
34-
cmd: composer phpunit
34+
3535
test81:
3636
service: php81
3737
cmd: composer phpunit
38-
test80:
39-
service: php80
38+
test82:
39+
service: php82
40+
cmd: composer phpunit
41+
test83:
42+
service: appserver
4043
cmd: composer phpunit
4144

42-
stan82:
45+
stan83:
4346
service: appserver
4447
cmd: composer phpstan
48+
stan82:
49+
service: php82
50+
cmd: composer phpstan
4551
stan81:
4652
service: php81
4753
cmd: composer phpstan
48-
stan80:
49-
service: php80
50-
cmd: composer phpstan
5154

52-
all82:
55+
all83:
5356
service: appserver
5457
cmd:
5558
- rm -rf vendor composer.lock && composer install
5659
- composer phpunit
5760
- composer phpstan
5861

62+
all82:
63+
service: php82
64+
cmd:
65+
- rm -rf vendor composer.lock && composer install
66+
- composer phpunit
67+
- composer phpstan
68+
5969
all81:
6070
service: php81
6171
cmd:
6272
- rm -rf vendor composer.lock && composer install
6373
- composer phpunit
6474
- composer phpstan
6575

66-
all80:
67-
service: php80
76+
77+
lara11:
78+
service: php83
6879
cmd:
69-
- rm -rf vendor composer.lock && composer install
80+
- rm -rf vendor composer.lock
81+
- composer require laravel/laravel:^11
82+
- composer install
7083
- composer phpunit
7184
- composer phpstan
85+
# this is for removing a specific version
86+
- composer remove laravel/laravel
7287

73-
lara8:
74-
service: php81
88+
lara10:
89+
service: php82
7590
cmd:
7691
- rm -rf vendor composer.lock
77-
- composer require laravel/laravel:^8
92+
- composer require laravel/laravel:^10
7893
- composer install
7994
- composer phpunit
8095
- composer phpstan

README.md

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ Collection of helpers for re-use accross a few of our projects
1717
- [Helpers](#helpers)
1818
- [DB Macros](#db-macros)
1919
- [Null Or Empty](#null-or-empty)
20+
- [Case insensitive statments](#case-insensitive-statments)
2021
- [Enforced Non Nullable Relations (orFail chain)](#enforced-non-nullable-relations-orfail-chain)
2122
- [DB Repositories](#db-repositories)
2223
- [String Macros](#string-macros)
24+
- [Observerable trait](#observerable-trait)
2325
- [Date Manipulation](#date-manipulation)
2426
- [Date(Carbon) Helpers attached to above:](#datecarbon-helpers-attached-to-above)
27+
- [Value Objects](#value-objects)
28+
- [Larastan Stubs](#larastan-stubs)
2529
- [Credits](#credits)
2630

2731
## Installation
@@ -124,10 +128,10 @@ example in the [UserRepository.stub.php](https://git.customd.com/composer/Larave
124128
## Observerable trait
125129
adding this trait to your models will automatically look for an observer in the app/Observers folder with the convension {model}Observer as the classname,
126130

127-
you can additionally/optionally add
131+
you can additionally/optionally add
128132
```php
129133
protected static $observers = [ ...arrayOfObservers]
130-
```
134+
```
131135
to add a additional ones if needed
132136

133137
## Date Manipulation
@@ -177,6 +181,40 @@ methods available:
177181

178182
You can also use the CDCarbonDate to create a few differnt date objects.
179183

184+
### Value Objects
185+
Example:
186+
```php
187+
<?php
188+
declare(strict_types=1);
189+
190+
namespace CustomD\LaravelHelpers\Tests\ValueObjects;
191+
192+
use CustomD\LaravelHelpers\ValueObjects\ValueObject;
193+
194+
class SimpleValue extends ValueObject
195+
{
196+
protected function __construct(
197+
readonly public string $value,
198+
readonly public int $count = 0
199+
) {
200+
}
201+
202+
/** optionally add some validation rules, leave out the method if the type sets are enough **/
203+
public function rules(): array
204+
{
205+
return [
206+
'value' => ['string', 'max:250'],
207+
'count' => ['max:99'],
208+
];
209+
}
210+
}
211+
212+
$simpleValue = SimpleValue::make(value: 'hello World', count: 33);
213+
214+
```
215+
216+
Best practice is to use the make option, which will validate, if you use a public constructor it will not.
217+
180218
### Larastan Stubs
181219
**these are temporary only till implemented by larastan**
182220

@@ -191,4 +229,3 @@ parameters:
191229
192230
- [](https://github.com/custom-d/laravel-helpers)
193231
- [All contributors](https://github.com/custom-d/laravel-helpers/graphs/contributors)
194-

composer.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
}
1919
],
2020
"require": {
21-
"php": "^8.0",
22-
"illuminate/notifications": "^8.5|^9.0|^10.0",
23-
"illuminate/support": "^8.5|^9.0|^10"
21+
"php": "^8.1",
22+
"illuminate/notifications": "^9.0|^10.0|^11.0",
23+
"illuminate/support": "^9.0|^10|^11.0"
2424
},
2525
"require-dev": {
26-
"orchestra/testbench": "^6.0|^7.0|^8.0",
26+
"orchestra/testbench": "^7.0|^8.0|^9.0",
2727
"nunomaduro/larastan": "^1.0|^2.0",
2828
"phpunit/phpunit": "^9.0|^10.0"
2929
},

src/ValueObjects/ValueObject.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
namespace CustomD\LaravelHelpers\ValueObjects;
3+
4+
use Illuminate\Contracts\Container\BindingResolutionException;
5+
use Illuminate\Contracts\Support\Arrayable;
6+
use ReflectionClass;
7+
use Illuminate\Support\Collection;
8+
use Illuminate\Foundation\Http\FormRequest;
9+
use ReflectionParameter;
10+
11+
/**
12+
* @implements Arrayable<string,mixed>
13+
*/
14+
abstract class ValueObject implements Arrayable
15+
{
16+
/**
17+
*
18+
* @param mixed $args
19+
* @return static
20+
* @throws BindingResolutionException
21+
*/
22+
public static function make(...$args): static
23+
{
24+
$instance = new static(...$args); //@phpstan-ignore-line -- meant to be static
25+
$instance->validate();
26+
return $instance;
27+
}
28+
29+
/**
30+
*
31+
* @param mixed $args
32+
* @return ?static
33+
* @throws BindingResolutionException
34+
*/
35+
public static function makeOrNull(...$args): ?static
36+
{
37+
try {
38+
return static::make(...$args);
39+
} catch (\Throwable $th) {
40+
return null;
41+
}
42+
}
43+
44+
45+
public static function fromRequest(FormRequest $request, bool $onlyValidated = true): static
46+
{
47+
/** @var array<string, mixed> */
48+
$data = $onlyValidated ? $request->validated() : $request->all();
49+
50+
$args = collect($data)->only(static::getConstructorArgs())->toArray();
51+
52+
return new static(...$args); //@phpstan-ignore-line -- meant to be static
53+
}
54+
55+
/**
56+
*
57+
* @return Collection<int, string>
58+
*/
59+
protected static function getConstructorArgs(): Collection
60+
{
61+
return collect((new ReflectionClass(static::class))->getConstructor()?->getParameters() ?? [])
62+
->map(fn(ReflectionParameter $parameter) => $parameter->getName());
63+
}
64+
65+
/**
66+
*
67+
* @return array<string, mixed>
68+
*/
69+
public function rules(): array
70+
{
71+
return [];
72+
}
73+
74+
protected function validate(): void
75+
{
76+
$validator = app('validator')->make($this->toArray(), $this->rules());
77+
$validator->validate();
78+
}
79+
80+
public function toArray(): array
81+
{
82+
return static::getConstructorArgs()
83+
->mapWithKeys(fn($property) => [$property => $this->{$property}])
84+
->toArray();
85+
}
86+
}

tests/ValueObjectTest.php

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
namespace CustomD\LaravelHelpers\Tests;
3+
4+
use Orchestra\Testbench\TestCase;
5+
use CustomD\LaravelHelpers\Tests\ValueObjects\SimpleValue;
6+
use CustomD\LaravelHelpers\Tests\ValueObjects\ComplexValue;
7+
use CustomD\LaravelHelpers\Tests\ValueObjects\SimpleValueFormRequest;
8+
9+
class ValueObjectTest extends TestCase
10+
{
11+
12+
public function test_it_can_create_a_value_object()
13+
{
14+
$value = SimpleValue::make('test', 10);
15+
$this->assertInstanceOf(SimpleValue::class, $value);
16+
17+
$this->assertEquals('test', $value->value);
18+
$this->assertEquals(10, $value->count);
19+
$this->assertIsInt($value->count);
20+
}
21+
22+
public function test_it_can_create_a_value_object_from_request()
23+
{
24+
$request = new SimpleValueFormRequest(query: ['value' => 'test','count' => '11','age' => 33]);
25+
26+
$validator = app('validator')->make(['value' => 'test','count' => '11', 'age' => 33], $request->rules());
27+
28+
$request->setValidator($validator);
29+
$request->validateResolved();
30+
31+
$value = SimpleValue::fromRequest($request, false);
32+
$this->assertInstanceOf(SimpleValue::class, $value);
33+
34+
$this->assertEquals('test', $value->value);
35+
}
36+
37+
public function test_it_validates_when_constructed()
38+
{
39+
$this->expectException(\Illuminate\Validation\ValidationException::class);
40+
$data = ['value' => 'test', 'count' => 9];
41+
$value = SimpleValue::make(...$data);
42+
}
43+
44+
public function test_it_can_be_passed_an_array()
45+
{
46+
$data = ['value' => 'test', 'count' => 11];
47+
$value = SimpleValue::make(...$data);
48+
$this->assertEquals('test', $value->value);
49+
$this->assertEquals(11, $value->count);
50+
$this->assertIsInt($value->count);
51+
}
52+
53+
public function test_a_complex_value()
54+
{
55+
$data = [
56+
'value' => 'test',
57+
'address' => [
58+
'street' => '123 Fake St'
59+
],
60+
'simpleValue' => SimpleValue::make(
61+
'test',
62+
11
63+
)
64+
];
65+
$value = ComplexValue::make(...$data);
66+
$this->assertEquals('test', $value->value);
67+
$this->assertEquals(11, $value->simpleValue->count);
68+
$this->assertEquals('123 Fake St', $value->address['street']);
69+
}
70+
71+
public function test_a_complex_value_construct()
72+
{
73+
$data = [
74+
'value' => 'test',
75+
'simpleValue' => SimpleValue::make(...[
76+
'count' => 11,
77+
'value' => 'test',
78+
]),
79+
'address' => [
80+
'street' => '123 Fake St'
81+
],
82+
];
83+
$value = ComplexValue::make(...$data);
84+
$this->assertEquals('test', $value->value);
85+
$this->assertEquals(11, $value->simpleValue->count);
86+
$this->assertEquals('123 Fake St', $value->address['street']);
87+
}
88+
}

0 commit comments

Comments
 (0)