Skip to content

Commit f28d762

Browse files
Fix for potential bug with non-backed enums (laravel#43842)
* Abstract enum case and value retrieval to methods, and fix potential bug with addCastAttributesToArray * Make sure relation dictionaries support non-backed enums too * Fix bug with leftover ::class * Fix styling * More styling fixes * formatting Co-authored-by: Taylor Otwell <[email protected]>
1 parent d2b1446 commit f28d762

File tree

2 files changed

+38
-19
lines changed

2 files changed

+38
-19
lines changed

src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Illuminate\Database\Eloquent\Concerns;
44

5+
use BackedEnum;
56
use Carbon\CarbonImmutable;
67
use Carbon\CarbonInterface;
78
use DateTimeImmutable;
@@ -307,7 +308,7 @@ protected function addCastAttributesToArray(array $attributes, array $mutatedAtt
307308
}
308309

309310
if ($this->isEnumCastable($key) && (! ($attributes[$key] ?? null) instanceof Arrayable)) {
310-
$attributes[$key] = isset($attributes[$key]) ? $attributes[$key]->value : null;
311+
$attributes[$key] = isset($attributes[$key]) ? $this->getStorableEnumValue($attributes[$key]) : null;
311312
}
312313

313314
if ($attributes[$key] instanceof Arrayable) {
@@ -814,11 +815,7 @@ protected function getEnumCastableAttributeValue($key, $value)
814815
return $value;
815816
}
816817

817-
if (is_subclass_of($castType, \BackedEnum::class)) {
818-
return $castType::from($value);
819-
}
820-
821-
return constant($castType.'::'.$value);
818+
return $this->getEnumCaseFromValue($castType, $value);
822819
}
823820

824821
/**
@@ -1123,21 +1120,42 @@ protected function setEnumCastableAttribute($key, $value)
11231120

11241121
if (! isset($value)) {
11251122
$this->attributes[$key] = null;
1126-
} elseif (is_subclass_of($enumClass, \BackedEnum::class)) {
1127-
if ($value instanceof $enumClass) {
1128-
$this->attributes[$key] = $value->value;
1129-
} else {
1130-
$this->attributes[$key] = $enumClass::from($value)->value;
1131-
}
1123+
} elseif (is_object($value)) {
1124+
$this->attributes[$key] = $this->getStorableEnumValue($value);
11321125
} else {
1133-
if ($value instanceof $enumClass) {
1134-
$this->attributes[$key] = $value->name;
1135-
} else {
1136-
$this->attributes[$key] = constant($enumClass.'::'.$value)->name;
1137-
}
1126+
$this->attributes[$key] = $this->getStorableEnumValue(
1127+
$this->getEnumCaseFromValue($enumClass, $value)
1128+
);
11381129
}
11391130
}
11401131

1132+
/**
1133+
* Get an enum case instance from a given class and value.
1134+
*
1135+
* @param string $enumClass
1136+
* @param string|int $value
1137+
* @return \UnitEnum|\BackedEnum
1138+
*/
1139+
protected function getEnumCaseFromValue($enumClass, $value)
1140+
{
1141+
return is_subclass_of($enumClass, BackedEnum::class)
1142+
? $enumClass::from($value)
1143+
: constant($enumClass.'::'.$value);
1144+
}
1145+
1146+
/**
1147+
* Get the storable value from the given enum.
1148+
*
1149+
* @param \UnitEnum|\BackedEnum $value
1150+
* @return string|int
1151+
*/
1152+
protected function getStorableEnumValue($value)
1153+
{
1154+
return $value instanceof BackedEnum
1155+
? $value->value
1156+
: $value->name;
1157+
}
1158+
11411159
/**
11421160
* Get an array attribute with the given key and value set.
11431161
*

src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithDictionary.php

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

55
use BackedEnum;
66
use Doctrine\Instantiator\Exception\InvalidArgumentException;
7+
use UnitEnum;
78

89
trait InteractsWithDictionary
910
{
@@ -23,8 +24,8 @@ protected function getDictionaryKey($attribute)
2324
}
2425

2526
if (function_exists('enum_exists') &&
26-
$attribute instanceof BackedEnum) {
27-
return $attribute->value;
27+
$attribute instanceof UnitEnum) {
28+
return $attribute instanceof BackedEnum ? $attribute->value : $attribute->name;
2829
}
2930

3031
throw new InvalidArgumentException('Model attribute value is an object but does not have a __toString method.');

0 commit comments

Comments
 (0)