feature: Add enum support for Enum column casting

This commit is contained in:
BadJacky
2024-10-28 11:38:24 +08:00
parent 508158cab9
commit 10f601f0df
6 changed files with 93 additions and 5 deletions

View File

@@ -4,10 +4,12 @@ declare(strict_types=1);
namespace WendellAdriel\Lift\Concerns;
use BackedEnum;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use UnitEnum;
use WendellAdriel\Lift\Attributes\Config;
use WendellAdriel\Lift\Attributes\CreateRules;
use WendellAdriel\Lift\Attributes\Rules;
@@ -82,6 +84,32 @@ trait RulesValidation
return self::formatValidationMessages(self::$modelUpdateMessages);
}
/**
* Return a scalar value for the given value that might be an enum.
*
* @internal
*
* @template TValue
* @template TDefault
*
* @param TValue $value
* @param TDefault|callable(TValue): TDefault $default
* @return ($value is empty ? TDefault : mixed)
*/
private static function enumValue($value, $default = null)
{
if (function_exists('Illuminate\Support\enum_value')) {
return \Illuminate\Support\enum_value($value, $default);
}
return transform($value, fn ($value) => match (true) {
$value instanceof BackedEnum => $value->value,
$value instanceof UnitEnum => $value->name,
default => $value,
}, $default ?? $value);
}
/**
* @param Collection<PropertyInfo> $properties
*
@@ -90,10 +118,10 @@ trait RulesValidation
private static function applyValidations(Collection $properties): void
{
$validatedProperties = self::getPropertiesForAttributes($properties, [Rules::class]);
$data = $validatedProperties->mapWithKeys(fn ($property) => [$property->name => $property->value]);
$data = $validatedProperties->mapWithKeys(fn ($property) => [$property->name => static::enumValue($property->value)]);
$configProperties = self::getPropertiesForAttributes($properties, [Config::class]);
$data = $data->merge($configProperties->mapWithKeys(fn ($property) => [$property->name => $property->value]));
$data = $data->merge($configProperties->mapWithKeys(fn ($property) => [$property->name => static::enumValue($property->value)]));
$validator = Validator::make(
data: $data->toArray(),
@@ -114,7 +142,7 @@ trait RulesValidation
private static function applyCreateValidations(Collection $properties): void
{
$validatedProperties = self::getPropertiesForAttributes($properties, [CreateRules::class]);
$data = $validatedProperties->mapWithKeys(fn ($property) => [$property->name => $property->value]);
$data = $validatedProperties->mapWithKeys(fn ($property) => [$property->name => static::enumValue($property->value)]);
$validator = Validator::make(
data: $data->toArray(),
@@ -135,7 +163,7 @@ trait RulesValidation
private static function applyUpdateValidations(Collection $properties): void
{
$validatedProperties = self::getPropertiesForAttributes($properties, [UpdateRules::class]);
$data = $validatedProperties->mapWithKeys(fn ($property) => [$property->name => $property->value]);
$data = $validatedProperties->mapWithKeys(fn (PropertyInfo $property) => [$property->name => static::enumValue($property->value)]);
$validator = Validator::make(
data: $data->toArray(),

View File

@@ -340,7 +340,7 @@ trait Lift
{
return $properties->filter(
fn ($property) => $property->attributes->contains(
fn ($attribute) => in_array($attribute->getName(), $attributes)
fn ($attribute) => in_array($attribute->getName(), $attributes, true)
)
);
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Tests\Datasets;
use Illuminate\Database\Eloquent\Model;
use Tests\Datasets\Enums\ArticleStatusEnum;
use WendellAdriel\Lift\Attributes\Cast;
use WendellAdriel\Lift\Attributes\PrimaryKey;
use WendellAdriel\Lift\Attributes\Rules;
use WendellAdriel\Lift\Lift;
class Article extends Model
{
use Lift;
#[PrimaryKey]
public int $id;
#[Cast(ArticleStatusEnum::class)]
#[Rules(['required', 'string', 'in:draft.published,archived'], ['required' => 'The book name cannot be empty', 'in' => 'The status must be draft, published or archived'])]
public ArticleStatusEnum $status;
protected $fillable = [
'status',
];
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Tests\Datasets\Enums;
enum ArticleStatusEnum: string
{
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
}

View File

@@ -158,3 +158,17 @@ it('casts values when retrieving model with custom columns', function () {
->and($product->expires_at->format('Y-m-d H:i:s'))->toBe('2023-12-31 23:59:59')
->and($product->json_column)->toBe(['foo' => 'bar']);
});
it('casts values when creating model with enum cast', closure: function () {
$article = \Tests\Datasets\Article::castAndCreate([
'status' => \Tests\Datasets\Enums\ArticleStatusEnum::ARCHIVED,
]);
expect($article->status)->toBe(\Tests\Datasets\Enums\ArticleStatusEnum::ARCHIVED);
$this->assertDatabaseHas(\Tests\Datasets\Article::class, [
'status' => \Tests\Datasets\Enums\ArticleStatusEnum::ARCHIVED,
'id' => $article->id,
]);
$this->assertSame(\Tests\Datasets\Enums\ArticleStatusEnum::ARCHIVED, $article->status);
});

View File

@@ -210,6 +210,12 @@ abstract class TestCase extends BaseTestCase
$table->string('title');
$table->timestamps();
});
Schema::create('articles', function (Blueprint $table) {
$table->id('id');
$table->enum('status', ['draft', 'published', 'archived']);
$table->timestamps();
});
}
protected function getPackageProviders($app)