【问题标题】:Where to put/how to handle enums in Laravel?在哪里放置/如何在 Laravel 中处理枚举?
【发布时间】:2014-10-31 14:06:26
【问题描述】:

Laravel 有a <select> form helper,它接受一个字典作为输入。我喜欢将所有这些的价值观放在一个中心位置。例如,我可能有一个如下所示的枚举:

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

我想在我的视图/模板和数据库中使用它:

Schema::create('customers', function (Blueprint $table) {
    $table->increments('id');
    $table->enum('pri_phone_type',array_keys($phoneTypes));
    ...
});
  1. 有推荐的放置这些的地方吗?
  2. 能否将它们设为全局,以便在所有视图中轻松访问它们?

【问题讨论】:

    标签: php laravel enums


    【解决方案1】:

    ? 更新:PHP 8.1 终于带来了对枚举的原生支持! ??

    在此处查看更多信息:

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

    下面我的原始答案不再适用,但如果您使用的是旧版本的 PHP...


    原答案

    您有多种处理枚举的选项。在我们看一些之前,我首先强烈建议您不要使用 DB enum 列类型。

    由于多种原因,数据库枚举存在问题。例如,我建议阅读这篇文章:

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

    因此,让我们看看其他一些选项。

    使用 Laravel 配置

    由于您使用的是 Laravel,一个非常简单的选择是将一组选项粘贴到配置文件中。

    假设您使用以下内容创建了一个新文件config/enums.php

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

    您现在可以在代码中的任何位置访问config('enums.phone_types'),包括您的 Blade 模板。

    使用 PHP 包

    @Banford 的回答展示了如何使用类常量执行基本的枚举类型行为。如果您喜欢这种方法,我建议您查看这篇文章和基于此概念提供强类型枚举的包:

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

    https://github.com/spatie/enum

    你会像这样创建一个类:

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

    现在您可以在您的应用程序中调用PhoneTypes::home()。如果需要,请查看该包的文档,了解如何创建值映射。

    使用数据库关系

    如果您真的想在数据库中管理您的选项,我会创建一个单独的 phone_types 数据库表并与您的 customers 表建立关系。这仍然比使用enum 列类型更好。

    【讨论】:

    • 为什么要为十几个很少更改的非常小的列表中的每一个创建单独的表?
    • 除其他外,我可以查询有手机存档的客户?而当我确信某些事情不会改变的那一刻......它会改变。我更喜欢提前设计,这样个人就可以轻松进行更改。
    • 我应该澄清一下:有时我有一个 varchar 列类型,其中包含我的应用程序控制的一组可能值。但是,如果我希望数据库调节可能的值,我使用关系表。我非常不喜欢在数据库级别使用enum,这是一篇文章为什么:komlenic.com/244/8-reasons-why-mysqls-enum-data-type-is-evil
    • 基于名称可能是最糟糕的想法。我已经不喜欢“蜂窝”;我可能想把它改成“Mobile”或者只是“Cell”——这会破坏我的所有代码。无论如何,我很欣赏你的建议。我想我现在会选择Config::get,但我会记住你所说的。
    • Laravel 由 symfony 提供支持,而 symfony 由数据库管理原则提供支持。
    【解决方案2】:

    假设您需要一个下拉列表

    试试这个:

    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Customer extends Model
    {
        const PHONE_TYPES = [
            [
                'label' => 'Cellular',
                'value' => 'Cellular',
            ],
            [
                'label' => 'Home',
                'value' => 'Home',
            ],
            [
                'label' => 'Work',
                'value' => 'Work',
            ],
        ];
    
    
        public function getPhoneTypesLabelAttribute()
        {
            return collect(static::PHONE_TYPES)->firstWhere('value', $this->phone_types)['label'] ?? '';
        }
    }
    

    在您的控制器中,假设在 create 方法下执行以下操作:

    public function create(Customer $customer)
    {
        return response([
            'meta' => [
                'customer'   => Customer::get(['id', 'first_name']),
                'phone_types' => Customer::PHONE_TYPES,
            ],
        ]);
    }
    

    然后,在你的刀片模板中

    dd($phone_types);
    

    【讨论】:

    • 嗯.. 模型似乎不是枚举数据的正确位置,尤其是在电话类型不是客户专有的情况下。
    • @mpen。是的,你是对的。但我认为在下拉仅适用于该模型的情况下,我认为可以使用它。我发现很容易将每个下拉列表附加到模型的视图刀片模板,例如 $customer['phone_types'] = Customer::PHONE_TYPES :).. 您可以随时返回并毫无顾虑地增加 phone_types 上的数据。
    【解决方案3】:

    你根本不应该使用枚举。

    官方 Laravel 5.1 documentation 声明:

    注意:目前不支持使用枚举列重命名表中的列。

    当您的数据库表中有 enum 列时,就会发生这种情况。无论您是尝试rename another 列,还是将another 列更改为nullable,都会出现该错误。这是 Doctrine\DBAL 的问题。

    请求的未知数据库类型枚举

    即使使用 laravel 5.8,问题也没有解决。

    我需要补充一点,当将可用选项添加到enum 列声明时,您也会遇到同样的问题。

    这让我得出一个结论,你应该小心使用枚举。甚至你根本不应该使用枚举。

    下面是一个例子,说明在enum 列声明中添加可用选项有多么困难

    说你有这个:

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

    您需要提供更多类型

    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']);
        });
    }
    

    【讨论】:

      【解决方案4】:

      除了@Banford 的回答:

      我最近整理了一个包,它使在 Laravel 中使用枚举变得更好。这是我在研究如何做同样的事情时发现的各种实现的组合(因此我在这里)。

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

      在这种情况下,您可以执行以下操作:

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

      然后可以使用以下方法访问这些值:

      PhoneTypes::Work // 1
      

      我建议始终将值设置为整数,然后将它们作为整数存储在数据库中。

      Enum 基类具有将所有键和值作为数组获取的方法。该软件包还具有其他一些在这种情况下可能有用的好处,例如验证 - 这样用户就无法向数据库添加不存在的值。

      还有一个非常方便的生成器。

      我希望这对某人有用。

      【讨论】:

      • 很好,但是这对 Laravel 有什么特殊意义?看起来像许多其他 php-enum 实现。
      • @mpen 枚举实现非常通用,你是对的。 Laravel 的细节在生成器和验证中。
      【解决方案5】:

      刚刚遇到类似问题,对我来说Eloquent Accessors and mutators 效果最好。对于这个问题,它会这样:

      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);
          }
      }
      

      请注意,您应该在数据库中保存数值,其中 0 是单元格,1 是家庭,2 是工作。其次,明智的做法是在此处使用翻译而不是受保护的属性。

      【讨论】:

      • 你是个超级明星。
      • 我同意你的回答,谢谢
      【解决方案6】:

      在@Banfords 答案的基础上,PHP7 常量现在可以是数组:

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

      【讨论】:

      • 你忘了提到你不应该将它们用于查找表。
      • 您能解释一下为什么不应该将它们用作查找表吗?
      【解决方案7】:

      我不同意这里接受的答案。我觉得枚举对于这类事情非常有用。我更喜欢将枚举视为类型,并在 Enum 基类上实现所需的方法,以提供所需的功能,例如获取字典。

      下面是我的简单示例:

      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());
          }
      }
      

      示例用法:

      PhoneType::getKeys();
      

      有关更多详细信息和更深入的示例,请参阅PHP and Enumerations

      【讨论】:

      • 一个很好的枚举用例是性别。您通常只会与男性、女性或未指定的人打交道。额外的表格没有意义,因为这三个值涵盖了所有可能的情况。当然,某些项目可能会有更多性别指定的用例,但这是个例外。
      • 最好将枚举更改为 tinyint,然后简单地将每个常量映射到一个整数!这样,您始终可以添加第四部手机(这很可能会发生)。 ALTER table 命令很昂贵,不值得冒险。此外,像教义这样的一些框架甚至不支持 Enum 来创建模式。
      • 嗨班福德。我已经尝试了这里解释的所有枚举解决方案。只有您的解决方案才能在 PHPStorm 中实现出色且简单的代码自动化。感谢您分享一个好的解决方案。
      猜你喜欢
      • 2021-05-26
      • 1970-01-01
      • 1970-01-01
      • 2012-09-09
      • 2010-12-27
      • 2015-05-09
      • 2020-04-28
      • 1970-01-01
      • 2011-08-01
      相关资源
      最近更新 更多