Using Enums in Laravel

Using Enums in Laravel

Today I would like to discuss with you several ways in which a type like enums in Laravel can be used. Especially from the point of view that there is no such type as Enum in PHP.

So what exactly is an enum?

Enum is a data type in programming, whose set of values ​​is a limited list of identifiers.

In short, Enum is a datatype for categorizing named values. They can be used in a wide variety of locations. For example, instead of hard-coding blog post statuses, they can be moved into a separate Enum class.

QuickAdminPanel is a Laravel admin generator service written by Povilas Korop. And if you created fields that contain drop-down lists, then it will display them in constants as follows:

 

<?php

 

namespace App\Models;

 

 

class User extends Authenticatable

{

    use SoftDeletes, Notifiable, HasFactory;

 

    public $table = 'users';

 

 

    const LANGUAGE_SELECT = [

        'ru_ru' => 'Russian',

        'en_us' => 'English',

    ];

 

 

    const SMS_DAYS_BEFORE_SELECT = [

        '0'  => 'The same day',

        '1'  => 'Before 1 day',

        '2'  => 'Before 2 days',

        '3'  => 'Before 3 days',

        '7'  => 'Before week',

        '31' => 'Before month',

    ];

 

    const MAIL_DAYS_BEFORE_SELECT = [

        '0'  => 'The same day',

        '1'  => 'Before 1 day',

        '2'  => 'Before 2 days',

        '3'  => 'Before 3 days',

        '7'  => 'Before week',

        '31' => 'Before month',

    ];

 

}

In this example, the User class has three drop-down lists, the values ​​of which are written in constants. This violates a number of clean code principles. On the one hand, we load the models with unnecessary information, violate the principle of single responsibility, and also work with arrays that cannot be strict typed, and we can't use additional methods, or get the benefits of syntax highlighting.

PHP offers a very simple SPL implementation, but in reality it doesn't help much. On the other hand, there is the very popular bensampo/laravel-enum package that I often use in my projects. It's really cool.

Now we will try it in action, replacing the usual arrays in the drop-down lists and transferring them to enums.

So, following the principles of DDD and Porto, we will create a parent abstract class, from which we will further inherit:

<?php

 

 

namespace Parents\Enums;

 

 

class Enum extends \BenSampo\Enum\Enum

{

 

}

 

Now let's try to make our first simple enum - a drop-down list of supported languages:

 

    const LANGUAGE_SELECT = [

        'ru_ru' => 'Russian',

        'en_us' => 'English',

    ];

 

As you can see, we have an array at the input, where the key is the value of the drop-down list, and the value of the array is text. Let's create our first class for this drop-down list:

 

<?php

 

 

namespace Domains\Users\Enums;

 

 

use BenSampo\Enum\Contracts\LocalizedEnum;

 

class LanguageEnum extends \Parents\Enums\Enum implements LocalizedEnum

{

    const Russian = 'ru_ru';

    const English = 'en_us';

}

 

As you can see, we did the opposite here, we use the text of the drop-down list in the name of the contant. This is done in order to bypass the limitations in datatype support. The text is always a string. And we cannot use the INT type in the name of the contant. And the value of the list can also store an integer type.

So how can we use this class. For example, when creating a user in blade, we can use the following:

 

                <select class="form-control {{ $errors->has('language') ? 'is-invalid' : '' }}" name="language" id="language">

                    <option value disabled {{ old('language', null) === null ? 'selected' : '' }}>{{ trans('global.pleaseSelect') }}</option>

                    @foreach(\Domains\Users\Enums\LanguageEnum::getInstances() as $key => $label)

                        <option value="{{ $label->value }}" {{ old('language', 'ru_ru') === (string) $label->value ? 'selected' : '' }}>{{$label->description}}</option>

                    @endforeach

                </select>

 

We use the \Domains\Users\Enums\LanguageEnum::getInstances() method to get all the available values. At the output, we get an array of objects. These objects have three important properties: value, key, description.

In our case, value is the value of the dropdown list. And description is the translated text for display on the screen. How do we translate the meanings? It's very simple. To do this, create an enums.php file in the resources/lang/en/enums.php folder with translations:

<?php

use Domains\Users\Enums\LanguageEnum;

use Domains\Users\Enums\SmsDayBefore;

 

return [

    LanguageEnum::class => [

        LanguageEnum::Russian => 'Russian',

        LanguageEnum::English => 'English',

    ],

    SmsDayBefore::class => [

        SmsDayBefore::SAME_DAY => 'The same day',

        SmsDayBefore::BEFORE_ONE => 'Before 1 day',

        SmsDayBefore::BEFORE_TWO => 'Before 2 days',

        SmsDayBefore::BEFORE_THREE => 'Before 3 days',

        SmsDayBefore::BEFORE_WEEK => 'Before week',

        SmsDayBefore::BEFORE_MONTH => 'Before month'

    ],

];

 

Also in the blade file, if we need to get the list value, we use the following construction:

 

{{ \Domains\Users\Enums\LanguageEnum::fromValue($user->language)?->description ?? '' }}

 

But this is just the tip of the iceberg of the benefits we get. When accessing the language field of the User model, we can immediately receive an object of the LanguageEnum class. To do this, we need to write the following in User.php:

 

    protected $casts = [

        'is_admin' => 'boolean',     // Example standard laravel cast

        'user_type' => LanguageEnum::class, // Example enum cast

    ];

 

Further, somewhere in the controller, we get the following:

 

$user = User::first();

$user->language // Instance of LanguageEnum

 

What about migrations? And there is a very handy tool here too:

 

use \Domains\Users\Enums\LanguageEnum;

use Illuminate\Support\Facades\Schema;

use Illuminate\Database\Schema\Blueprint;

use Illuminate\Database\Migrations\Migration;

 

class UpdateUsersTable extends Migration

{

    /**

     * Run the migrations.

     *

     * @return void

     */

    public function up(): void

    {

        Schema::table('users', function (Blueprint $table): void {            

            $table->enum('type', LanguageEnum::getValues())

                ->default(LanguageEnum::Russian);

        });

    }

}

 

Last but not least, we get additional validation benefits:

 

$this->validate($request, [

        'language' => ['required', new EnumValue(LanguageEnum::class)],

    ]);

 

Remember that array with int keys?

 

    const SMS_DAYS_BEFORE_SELECT = [

        '0'  => 'The same day',

        '1'  => 'Before 1 day',

        '2'  => 'Before 2 days',

        '3'  => 'Before 3 days',

        '7'  => 'Before week',

        '31' => 'Before month',

    ];

 

Let's make an enum out of it:

<?php

 

 

namespace Domains\Users\Enums;

 

 

class SmsDayBefore extends \Parents\Enums\Enum implements \BenSampo\Enum\Contracts\LocalizedEnum

{

    const SAME_DAY = 0;

    

    const BEFORE_ONE = 1;

    

    const BEFORE_TWO = 2;

    

    const BEFORE_THREE = 3;

    

    const BEFORE_WEEK = 7;

    

    const BEFORE_MONTH = 31;

 

    public static function parseDatabase($value)

    {

        return (int) $value;

    }

}

 

As you can see, we are not storing a string in values, but int! And we want database operations to be performed in int format as well. This is where the parseDatabase method will help us.

This approach generally eliminates the need for code duplication and provides more advantages in working with constants compared to regular arrays. I know this is not perfect and I hope that in the future we will see enum support in php.

Hire Professional CRM developer for $25 per hour

I will make time for your project. Knowledge of Vtiger CRM, SuiteCRM, Laravel, and Vue.js. I offer cooperation options that will help you take advantage of external experience, optimize costs and reduce risks. Full transparency of all stages of work and accounting for time costs. Pay only development working hours after accepting the task. Accept PayPal and Payoneer payment systems. How to hire professional developer? Just fill in the form

Telegram
@sergeyem
Telephone
+4915211100235