Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where to put/how to handle enums in Laravel?

Tags:

php

enums

laravel

Laravel has a <select> form helper which takes as input a dictionary. I like to keep the values for all of these in a central place. For example, I might have an enum that looks like this:

$phoneTypes = [
    'CELL' => "Cellular",
    'HOME' => "Home",
    'WORK' => "Work",
];

Which I want to use in both my view/template, and in the database:

Schema::create('customers', function (Blueprint $table) {
    $table->increments('id');
    $table->enum('pri_phone_type',array_keys($phoneTypes));
    ...
});
  1. Is there a recommended place to put these?
  2. Can I make them global so I can access them easily in all my views?
like image 268
mpen Avatar asked Sep 07 '14 00:09

mpen


People also ask

Where do you declare enums?

The best way to define the enum is to declare it in header file. So, that you can use it anywhere you want by including that header file during compilation.

Should enums be stored in database?

By keeping the enum in your database, and adding a foreign key on the table that contains an enum value you ensure that no code ever enters incorrect values for that column. This helps your data integrity and is the most obvious reason IMO you should have tables for enums.

What is enums in laravel?

Laravel Enum is a package by Ben Sampson that adds support for creating enums in PHP and includes a generator for Laravel. Here's an example of what an Enum class looks like using this package: 3namespace App\Enums; 5use BenSampo\Enum\Enum; 7final class UserType extends Enum.


6 Answers

🚨 Update: PHP 8.1 has finally brought native support for enums! 🥳🎉

See more here:

https://stitcher.io/blog/php-enums https://php.watch/versions/8.1/enums https://www.php.net/manual/en/language.enumerations.php

My original answer below no longer applies, but if you're working with an older version of PHP...


Original answer

You have several options for handling enums. Before we look at a few though, I would first strongly encourage you not to use the DB enum column type.

Database enums are problematic for a number of reasons. I suggest reading this article for example:

http://komlenic.com/244/8-reasons-why-mysqls-enum-data-type-is-evil/

So with that let's look at a few other options.

Using Laravel config

Since you're using Laravel, one very simple option is to stick an array of options in a config file.

Say you create a new file config/enums.php with the following:

return [
    'phone_types' => [
        'CELL' => "Cellular",
        'HOME' => "Home",
        'WORK' => "Work",
    ]
];

You can now access config('enums.phone_types') anywhere in your code, including your Blade template.

Using a PHP package

@Banford's answer shows how to do basic enum-type behavior with class constants. If you like that approach, I recommend looking at this article and package which builds on this concept to provide strongly type enums:

https://stitcher.io/blog/php-enums

https://github.com/spatie/enum

You would create a class like this:

/**
 * @method static self cell()
 * @method static self home()
 * @method static self work()
 */
class PhoneTypes extends Enum
{
}

And now you can call PhoneTypes::home() in your app. Check out the documentation for that package to see how you can create a map of values, if you want.

Using DB relationships

If you really want to manage your options in the database, I'd create a separate phone_types database table and create a relationship with your customers table. This is still a much better option than using enum column type.

like image 135
jszobody Avatar answered Oct 11 '22 06:10

jszobody


I disagree with the accepted answer here. I feel that enums can be very useful for this kind of thing. I prefer to treat enums as types, and implement the methods you need on the Enum base class to give you the functionality you need such as getting a dictionary.

My simple example below:

abstract class PhoneType extends Enum {
    const Cell = "Cellular";
    const Home = "Home";
    const Work = "Work";
}

abstract class Enum {
    static function getKeys(){
        $class = new ReflectionClass(get_called_class());
        return array_keys($class->getConstants());
    }
}

Example usage:

PhoneType::getKeys();

See PHP and Enumerations for further details and a more in depth example.

like image 22
Banford Avatar answered Oct 11 '22 04:10

Banford


Building on @Banfords answer, with PHP7 constants can now be arrays:

class User extends Authenticatable
{
    /**
     * The possible genders a user can be.
     */
    const GENDER = [
        'Male',
        'Female',
        'Unspecified'
    ];

...
like image 38
Kirill Fuchs Avatar answered Oct 11 '22 06:10

Kirill Fuchs


In addition to @Banford's answer:

I have recently put together a package which makes working with enums in Laravel much nicer. It's a combination of various implementations I had found while researching how to do the very same thing (hence why I'm here).

https://github.com/BenSampo/laravel-enum

In this case, you could do something like the following:

final class PhoneTypes extends Enum
{
    const Cellular = 0;
    const Work = 1;
    const Home = 2;
}

The values can then be accessed using:

PhoneTypes::Work // 1

I would recommend always setting the values to integers and subsequently storing them in the DB as ints.

The base Enum class has methods for getting all keys and values as arrays. The package also features a couple of other benefits which may be useful in this case such as validation - so that a user couldn't add a non-existent value to the DB.

There's also a generator which is pretty handy.

I hope this comes in useful for someone.

like image 42
BenSampo Avatar answered Oct 11 '22 05:10

BenSampo


Just had similar issue, for me Eloquent Accessors and mutators worked the best. For this question it would go like:

namespace App;

use Illuminate\Database\Eloquent\Model;

class Customer extends Model
{
    /**
    * @var array
    */
    protected $phoneTypes = [
        'Cellular',
        'Home',
        'Work'
    ];

   /**
    * @param int $value
    * @return string|null
    */
    public function getPhoneTypeAttribute($value)
    {
        return Arr::get($this->phoneTypes, $value);
    }
}

Please note that in database you should save numeric values, where 0 is cell, 1 is home and 2 is work. Secondly it would be wise to use translations here instead protected property.

like image 44
lchachurski Avatar answered Oct 11 '22 04:10

lchachurski


You should not use enum at all.

The official Laravel 5.1 documentation states:

Note: Renaming columns in a table with a enum column is not currently supported.

It happens when you have a enum column in your database table. Whether you are trying to rename another column, or change another column to nullable, the bug will appear. It's an issue with Doctrine\DBAL.

Unknown database type enum requested

Even with laravel 5.8, problem is not resolved.

I need to add that you will have the same problem when adding available options into enum column declaration.

It brings me to a conclusion that You should use enum with care. or even You should not use enum at all.

Here is an example of how difficult would it be adding available options into enum column declaration

say you have this:

Schema::create('blogs', function (Blueprint $table) {
    $table->enum('type', [BlogType::KEY_PAYMENTS]);
    $table->index(['type', 'created_at']);
...

and you need to make more types available

public function up(): void
{
    Schema::table('blogs', function (Blueprint $table) {
        $table->dropIndex(['type', 'created_at']);
        $table->enum('type_tmp', [
            BlogType::KEY_PAYMENTS,
            BlogType::KEY_CATS,
            BlogType::KEY_DOGS,
        ])->after('type');
    });

    DB::statement('update `blogs` as te set te.`type_tmp` = te.`type` ');

    Schema::table('blogs', function (Blueprint $table) {
        $table->dropColumn('type');
    });

    Schema::table('blogs', function (Blueprint $table) {
        $table->enum('type', [
            BlogType::KEY_PAYMENTS,
            BlogType::KEY_CATS,
            BlogType::KEY_DOGS,
        ])->after('type_tmp');
    });

    DB::statement('update `blogs` as te set te.`type` = te.`type_tmp` ');

    Schema::table('blogs', function (Blueprint $table) {
        $table->dropColumn('type_tmp');
        $table->index(['type', 'created_at']);
    });
}
like image 25
Yevgeniy Afanasyev Avatar answered Oct 11 '22 05:10

Yevgeniy Afanasyev