Laravel Model Enumeration Trait
The Enum
trait is a really useful way to allow you to pre-define all of the valid values for a given field on a model and enforce that their values are set appropriately. This basically allows you to treat a field as a menu without the database overhead of dealing with true enum fields or lookup tables.
Enum
trait to your modelnamespace App;
use App\Traits\Enums;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use Enums;
...
}
Model App\Post
has an enumerated field called status
that we want to enforce specific values for.
class Post extends Model
{
use Enums;
// Define all of the valid options in an array as a protected property that
// starts with 'enum' followed by the plural studly cased field name
protected $enumStatuses = [
'Draft',
'Scheduled',
'Published',
'Archived'
];
// Alternately, if you use an associative array, the keys can be used
// to set the value as well. The full string will still be stored in the database.
/* protected $enumStatuses = [
'dr' => 'Draft',
'sc' => 'Scheduled',
'pu' => 'Published',
'ar' => 'Archived'
]; */
...
Once you've defined this $enum
property on the model, any time that field is set on any instance, a validation process will run to enforce that the value is being set properly:
$post = new App\Post;
$post->status = 'Something Invalid';
// Throws an InvalidEnumException
$post = App\Post::first();
$post->status = 'Draft';
// Sets the value to Draft as expected
// Key values will always work to set the value as well,
// so using the non-associative array example, this will set status to 'Draft'
$post = App\Post::create([
'status' => 0
]);
// Using the associative array example, this will set status to 'Published'
$post = App\Post::create([
'status' => 'pu'
]);
Enumerations work really well in blade files too. Simply use the getEnum
static helper:
@foreach( App\Post::getEnum('status') as $key => $value)
{{ $key }}: {{ $value }}
@endforeach
Or use them with the LaravelCollective form builder:
{{ Form::select('status', App\Post::getEnum('status')) }}
<?php
namespace App\Exceptions;
use Exception;
class InvalidEnumException extends Exception
{
//
}
<?php
namespace App\Traits;
use Illuminate\Support\Str;
use App\Exceptions\InvalidEnumException;
trait Enums
{
/**
* Enum property getter
*
* @param string $field
* @return mixed|false
*/
public static function getEnum(string $field)
{
$instance = new static;
if ($instance->hasEnumProperty($field)) {
$property = $instance->getEnumProperty($field);
return $instance->$property;
}
return false;
}
/**
* Check for the presence of a property that starts
* with enum for the provided attribute
*
* @param string $field
* @param mixed $value
* @return $this
* @throws InvalidEnumException
*/
public function setAttribute($field, $value)
{
if ($this->hasEnumProperty($field)) {
if (!$this->isValidEnum($field, $value)) {
throw new InvalidEnumException("Invalid value for " . static::class . "::$field ($value)");
}
if ($this->isKeyedEnum($field, $value)) {
$value = $this->getKeyedEnum($field, $value);
}
}
return parent::setAttribute($field, $value);
}
/**
* Gets the expected enum property
*
* @param string $field
* @return string
*/
protected function getEnumProperty(string $field)
{
return 'enum' . Str::plural(Str::studly($field));
}
/**
* Gets the enum value by key
*
* @param string $field
* @param mixed $key
* @return mixed
*/
protected function getKeyedEnum(string $field, $key)
{
return static::getEnum($field)[$key];
}
/**
* Is an enum property defined for the provided field
*
* @param string $field
* @return boolean
*/
protected function hasEnumProperty(string $field)
{
$property = $this->getEnumProperty($field);
return isset($this->$property) && is_array($this->$property);
}
/**
* Is the provided value a key in the enum
*
* @param string $field
* @param mixed $key
* @return bool
*/
protected function isKeyedEnum(string $field, $key)
{
return in_array($key, array_keys(static::getEnum($field)), true);
}
/**
* Is the value a valid enum in any way
*
* @param string $field
* @param mixed $value
* @return bool
*/
protected function isValidEnum(string $field, $value)
{
return $this->isValueEnum($field, $value) ||
$this->isKeyedEnum($field, $value);
}
/**
* Is the provided value in the enum
*
* @param string $field
* @param mixed $value
* @return bool
*/
protected function isValueEnum(string $field, $value)
{
return in_array($value, static::getEnum($field));
}
}