Skip to content

Commit 13cffa2

Browse files
committed
feature: enum names and enum casting
Setting out deprecation plans for version 2.x
1 parent fac11f4 commit 13cffa2

24 files changed

+403
-105
lines changed

README.md

+62-29
Original file line numberDiff line numberDiff line change
@@ -37,34 +37,40 @@ php artisan migrate
3737

3838
### Concepts
3939

40-
> Preferences are defined by their name, and optionally grouped
41-
40+
> Preferences are defined by their name
41+
>
4242
> Each preference has at least a name and a caster. For additional validation you can add you custom Rule object
43-
>> The default caster supports all major primitives, including datetime/date and timestamp which get converted
44-
> > with `Carbon/Carbon`
43+
>> The default caster supports all major primitives, Enums, as well as time/datetime/date and timestamp which get converted
44+
>> with `Carbon/Carbon`
4545
4646
### Create a Preference
4747
#### single mode
4848
```php
49-
public function up(): void
49+
public function up(): void
5050
{
51-
PreferenceBuilder::init("language")
51+
PreferenceBuilder::init(Preferences::LANGUAGE)
5252
->withDefaultValue("en")
53-
// optional ->withGroup('general')
5453
->withRule(new InRule("en", "it", "de"))
5554
->create();
5655

57-
// Or
58-
PreferenceBuilder::init("language")->create()
59-
56+
57+
// Or
58+
PreferenceBuilder::init(Preferences::LANGUAGE)->create()
59+
// different enums with the same value do not conflict
60+
PreferenceBuilder::init(OtherPreferences::LANGUAGE)->create()
61+
62+
// update
63+
PreferenceBuilder::init(Preferences::LANGUAGE)
64+
->withRule(new InRule("en", "it", "de"))
65+
->updateOrCreate()
66+
67+
// Discouraged, consider using Enums
68+
PreferenceBuilder::init("language")->create()
6069
}
6170

62-
/**
63-
* Reverse the migrations.
64-
*/
6571
public function down(): void
6672
{
67-
PreferenceBuilder::delete("language");
73+
PreferenceBuilder::delete(Preferences::LANGUAGE);
6874
}
6975
```
7076
#### Bulk mode
@@ -98,9 +104,10 @@ return new class extends Migration {
98104
public function preferences(): array
99105
{
100106
return [
101-
['name' => 'language', 'cast' => Cast::STRING, 'default_value' => 'en', 'rule' => new InRule("en", "it", "de"), 'group' => 'general'],
102-
['name' => 'theme', 'cast' => Cast::STRING, 'default_value' => 'light'],
103-
['name' => 'configuration', 'cast' => Cast::ARRAY],
107+
['name' => Preferences::LANGUAGE, 'cast' => Cast::STRING, 'default_value' => 'en', 'rule' => new InRule("en", "it", "de")],
108+
['name' => Preferences::THEME, 'cast' => Cast::STRING, 'default_value' => 'light'],
109+
['name' => Preferences::CONFIGURATION, 'cast' => Cast::ARRAY],
110+
['name' => Preferences::CONFIGURATION, 'cast' => Cast::ARRAY],
104111
];
105112
}
106113
};
@@ -111,33 +118,43 @@ return new class extends Migration {
111118

112119
> use the trait `HasPreferences`
113120
121+
> string will be deprecated, consider sticking to `UnitEnum`
122+
114123
```php
115124
// remove a preference, reverting it to the default value if set.
116-
public function removePreference(string $name, string $group = 'general'): void
125+
public function removePreference(UnitEnum|string $name, string $group = null): void
117126

118127
// set / update a preference
119-
public function setPreference(string $name, mixed $value, string $group = 'general'): void
128+
public function setPreference(UnitEnum|string $name, mixed $value, string $group = null): void
120129

121130
// collection of UserPreferences | optional filter by group
122131
public function getPreferences(string $group = null): Collection
123132

124133
// get the value of the preference | if no value or default_value are found, returns $default
125-
public function getPreference(string $name, string $group = 'general', mixed $default = null): mixed
134+
public function getPreference(UnitEnum|string $name, string $group = null, mixed $default = null): mixed
126135
```
127136

128137
### Examples
129138

130139
```php
131-
$user->setPreference('language',"de");
132-
$user->getPreference('language'); // 'de' as string
140+
$user->setPreference(UserPreferences::LANGUAGE,"de");
141+
$user->getPreference(UserPreferences::LANGUAGE,); // 'de' as string
133142

134-
$user->setPreference('language',"fr");
143+
$user->setPreference(UserPreferences::LANGUAGE,,"fr");
135144
// ValidationException because of the rule: ->withRule(new InRule("en","it","de"))
136-
$user->setPreference('language',2);
145+
$user->setPreference(UserPreferences::LANGUAGE,,2);
137146
// ValidationException because of the cast: Cast::STRING
138147

139-
$user->removePreference('language');
140-
$user->getPreference('language'); // 'en' as string
148+
$user->removePreference(UserPreferences::LANGUAGE);
149+
$user->getPreference(UserPreferences::LANGUAGE,); // 'en' as string
150+
151+
// Or with Enums
152+
$user->setPreference(UserPreferences::LANGUAGE,"de");
153+
$user->setPreference(UserPreferences::THEME,"light");
154+
// get all of type UserPreferences
155+
$user->getPreferences(UserPreferences::class)
156+
//get all
157+
$user->getPreferences()
141158
```
142159

143160
## Casting
@@ -147,21 +164,21 @@ public function getPreference(string $name, string $group = 'general', mixed $de
147164
148165
### Available Casts
149166

150-
INT, FLOAT, STRING, BOOL, ARRAY, TIME, DATE, DATETIME, TIMESTAMP
167+
INT, FLOAT, STRING, BOOL, ARRAY, TIME, DATE, DATETIME, TIMESTAMP, BACKED_ENUM
151168

152169
### Custom Caster
153170

154171
create a `BackedEnum`, and implement `CastableEnum`
155172

156173
```php
157-
use Illuminate\Validation\Rule;
174+
use Illuminate\Contracts\Validation\Rule;
158175
use Matteoc99\LaravelPreference\Contracts\CastableEnum;
159176

160177
enum MyCast: string implements CastableEnum
161178
{
162179
case TIMEZONE = 'tz';
163180

164-
public function validation(): Rule|string
181+
public function validation(): Rule|array|string
165182
{
166183
return match ($this) {
167184
self::TIMEZONE => 'timezone:all',
@@ -214,10 +231,26 @@ class MyRule extends DataRule
214231
->withRule(new MyRule("Europe","Asia"))
215232
```
216233

234+
## Deprecation plans
235+
236+
### HasValidation for custom Rules
237+
`HasValidation` will be deprecated in version >2.x, since Laravel is Deprecating the Rule Contract
238+
239+
### string names
240+
string `name` for preferences, will be removed in version >2.x, consider using enums, as its the direction this package will take.
241+
242+
### groups
243+
for preferences is deprecated and creating groups will be removed with version 2.x
244+
> groups created with version 1.x will however be still supported.
245+
>
246+
> the intended use for groups is internal only in combination with enum names
247+
217248
## Test
218249

219250
`composer test <path>`
220251

252+
`composer coverage`
253+
221254
## Security Vulnerabilities
222255

223256
Please review [our security policy](SECURITY.md) on how to report security vulnerabilities.

src/Casts/EnumCaster.php

+5-25
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,16 @@
44

55
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
66
use Illuminate\Database\Eloquent\Model;
7-
use Illuminate\Support\Facades\App;
8-
use Illuminate\Support\Facades\Log;
97
use Matteoc99\LaravelPreference\Contracts\CastableEnum;
8+
use Matteoc99\LaravelPreference\Utils\SerializeHelper;
109

1110
class EnumCaster implements CastsAttributes
1211
{
1312

1413

1514
public function get(?Model $model, string $key, mixed $value, array $attributes): CastableEnum|null
1615
{
17-
return $this->deserializeEnum($value);
18-
}
19-
20-
protected function deserializeEnum($value)
21-
{
22-
if (empty($value)) {
23-
return null;
24-
}
25-
$value = json_decode($value, true);
26-
27-
$enumClass = $value['class']??null;
28-
29-
if (!class_exists($enumClass)) {
30-
throw new \InvalidArgumentException("Enum class $enumClass does not exist.");
31-
}
32-
33-
return $enumClass::tryFrom($value['value']);
16+
return SerializeHelper::deserializeEnum($value);
3417
}
3518

3619
public function set(?Model $model, string $key, mixed $value, array $attributes)
@@ -39,18 +22,15 @@ public function set(?Model $model, string $key, mixed $value, array $attributes)
3922
return null;
4023
}
4124

42-
return json_encode($this->serializeEnum($value));
25+
return $this->serializeEnum($value);
4326
}
4427

45-
protected function serializeEnum($enum): array
28+
protected function serializeEnum($enum): string
4629
{
4730
if (!$enum instanceof CastableEnum) {
4831
throw new \InvalidArgumentException("Invalid value for Castable attribute.");
4932
}
5033

51-
return [
52-
'class' => get_class($enum),
53-
'value' => $enum->value,
54-
];
34+
return SerializeHelper::serializeEnum($enum);
5535
}
5636
}

src/Casts/ValueCaster.php

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public function __construct(protected ?CastableEnum $caster = null) { }
1313

1414
public function get(?Model $model, string $key, mixed $value, array $attributes)
1515
{
16+
if (is_null($value)) return null;
17+
1618
$caster = $this->getCaster($model);
1719

1820
if ($caster) {
@@ -26,6 +28,8 @@ public function get(?Model $model, string $key, mixed $value, array $attributes)
2628

2729
public function set(?Model $model, string $key, mixed $value, array $attributes)
2830
{
31+
if (is_null($value)) return null;
32+
2933
$caster = $this->getCaster($model);
3034

3135
if ($caster) {

src/Contracts/CastableEnum.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
namespace Matteoc99\LaravelPreference\Contracts;
44

5-
use Illuminate\Validation\Rule;
5+
use Illuminate\Contracts\Validation\Rule;
66

77
interface CastableEnum extends \BackedEnum
88
{
99

10-
public function validation(): Rule|string;
10+
public function validation(): Rule|array|string;
1111

1212

1313
public function castToString(mixed $value): string;

src/Contracts/HasValidation.php

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Illuminate\Contracts\Validation\Rule;
66

7+
78
interface HasValidation extends Rule
89
{
910

src/Enums/Cast.php

+14-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
namespace Matteoc99\LaravelPreference\Enums;
44

55
use Carbon\Carbon;
6-
use Illuminate\Validation\Rule;
6+
use Illuminate\Contracts\Validation\Rule;
77
use Illuminate\Validation\ValidationException;
88
use Matteoc99\LaravelPreference\Contracts\CastableEnum;
9+
use Matteoc99\LaravelPreference\Rules\InstanceOfRule;
10+
use Matteoc99\LaravelPreference\Utils\SerializeHelper;
911

1012

1113
enum Cast: string implements CastableEnum
@@ -20,7 +22,9 @@ enum Cast: string implements CastableEnum
2022
case DATETIME = 'datetime';
2123
case TIMESTAMP = 'timestamp';
2224

23-
public function validation(): Rule|string
25+
case BACKED_ENUM = 'backed_enum';
26+
27+
public function validation(): Rule|array|string
2428
{
2529
return match ($this) {
2630
self::INT => 'integer',
@@ -31,6 +35,7 @@ public function validation(): Rule|string
3135
self::DATE, self::DATETIME => 'date',
3236
self::TIME => 'date_format:H:i',
3337
self::TIMESTAMP => 'date_format:U',
38+
self::BACKED_ENUM => new InstanceOfRule(\BackedEnum::class),
3439
};
3540
}
3641

@@ -45,6 +50,7 @@ public function castFromString(string $value): mixed
4550
self::DATE, self::DATETIME => new Carbon($value),
4651
self::TIME => Carbon::now()->setTimeFromTimeString($value),
4752
self::TIMESTAMP => Carbon::createFromTimestamp($value),
53+
self::BACKED_ENUM => SerializeHelper::deserializeEnum($value),
4854
};
4955
}
5056

@@ -59,6 +65,7 @@ public function castToString(mixed $value): string
5965
self::DATETIME => $value->toDateTimeString(),
6066
self::TIMESTAMP => $value->timestamp,
6167
self::TIME => $value->toTimeString(),
68+
self::BACKED_ENUM => SerializeHelper::serializeEnum($value),
6269
};
6370
}
6471

@@ -100,6 +107,11 @@ private function ensureType(mixed $value): mixed
100107
$value = Carbon::now()->setTimeFromTimeString($value);
101108
}
102109
break;
110+
case self::BACKED_ENUM:
111+
if (!($value instanceof \BackedEnum)) {
112+
throw ValidationException::withMessages(["Wrong type for Backed enum casting"]);
113+
}
114+
break;
103115
default:
104116
throw ValidationException::withMessages(["Unknown casting type"]);
105117
}

0 commit comments

Comments
 (0)