Laravel 9 Enum model casting not casting to string

huangapple go评论81阅读模式
英文:

Laravel 9 Enum model casting not casting to string

问题

我在我的模型的AppLoanPurpose字段上使用了一个枚举,它在数据库中存储一个整数。然后,该整数对应于某个值,例如,0 表示"Other",1 表示"Groceries"。

当我转换我的字段时,我仍然看到返回的是整数值而不是字符串。

我漏掉了什么?

<?php

namespace App\Enums\Applications\GB\Payday;

enum LoanPurpose: Int
{
    case OTHER = 0;
    case GROCERIES = 1;

    public function label()
    {
        return match($this) {
            self::OTHER => 'Other',
            self::GROCERIES => 'Groceries'
        };
    }
}

模型

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Casts\Json;
use App\Enums\Applications\GB\Payday\LoanPurpose;

class ApplicationGBPayday extends Model
{
    use HasFactory, SoftDeletes;

    /**
     * 与模型关联的表。
     *
     * @var string
     */
    protected $table = 'application_gb_paydays';

    /**
     * 不可批量赋值的属性。
     *
     * @var array
     */
    protected $guarded = [];

    /**
     * 应该被转换的属性。
     *
     * @var array<string, string>
     */
    protected $casts = [
        'AppLoanPurpose' => LoanPurpose::class,
    ];

    /**
     * 获取模型的申请
     */
    public function application()
    {
        return $this->morphOne(Application::class, 'modelable');
    }
}

你似乎忘记在LoanPurpose枚举中调用label()方法以获取字符串标签。在你的模型中,AppLoanPurpose字段已经被正确地转换为LoanPurpose枚举,但你需要调用label()方法来获取相应的字符串值。例如:

$loanPurpose = ApplicationGBPayday::find(1)->AppLoanPurpose;
$label = $loanPurpose->label(); // 这将返回"Other"或"Groceries"
英文:

I'm using an Enum on my model's AppLoanPurpose field which stores an integer in the database. The integer then corrosponds to something, for example, 0 means "Other" and 1 means "Groceries".

When I cast my field, I'm still seeing the integer value returned rather than my string.

What am I missing?

&lt;?php

namespace App\Enums\Applications\GB\Payday;

enum LoanPurpose: Int
{
    case OTHER = 0;
    case GROCERIES = 1;

    public function label()
    {
        return match($this) {
            self::OTHER =&gt; &#39;Other&#39;,
            self::GROCERIES =&gt; &#39;Groceries&#39;
        };
    }
}

Model

&lt;?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Casts\Json;
use App\Enums\Applications\GB\Payday\LoanPurpose;

class ApplicationGBPayday extends Model
{
    use HasFactory, SoftDeletes;

    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = &#39;application_gb_paydays&#39;;

    /**
     * The attributes that aren&#39;t mass assignable.
     *
     * @var array
     */
    protected $guarded = [];

    /**
     * The attributes that should be cast.
     *
     * @var array&lt;string, string&gt;
     */
    protected $casts = [
        &#39;AppLoanPurpose&#39; =&gt; LoanPurpose::class,
    ];

    /**
     * Get the model&#39;s application
     */
    public function application()
    {
        return $this-&gt;morphOne(Application::class, &#39;modelable&#39;);
    }
}

答案1

得分: 1

如果你真的想要使用 enum,你必须将枚举视为常规对象。

enum LoanPurpose: Int {
    case OTHER = 0;
    case GROCERIES = 1;

    public function label() {
        return match($this) {
            self::OTHER => 'Other',
            self::GROCERIES => 'Groceries'
        };
    }
}

$enum = LoanPurpose::GROCERIES;

上面的代码将创建一个包含两个属性 namevalueLoanPurpose 实例。

= LoanPurpose {#xxxx
    +name: "GROCERIES",
    +value: 1,
}

调用这些属性:

// 输出:GROCERIES
$enum->name;
// 输出:1
$enum->value;

如果你向 LoanPurpose 添加一个方法,你仍然需要调用该方法以执行实际操作。在你的设置中,你只需调用 label 方法,它执行与你正在匹配的内容的 match 操作。

// 输出:Groceries
$enum->label();

如果你不想调用枚举实例的 namevalue,你可以使用属性访问器,比如 getAppLoanPurposeAttribute 并执行相同的操作。

use App\Enums\Applications\GB\Payday\LoanPurpose;

class ApplicationGBPayday extends Model {
  // ...
  public function getAppLoanPurposeAttribute($value) {
    return return LoanPurpose::from($value)->name;
  }
  // ...
}

只需记住,你将失去枚举提供的任何有用性,因为 ApplicationGBPayday::AppLoanPurpose 永远不会返回一个 LoanPurpose 实例。

因此,以下是不可能的

$ApplicationGBPayday::AppLoanPurpose == LoanPurpose::OTHER

正如 @silver 的回答中所述,你还可以使用 CastAttribute 接口,或者完全创建一个 Attribute 实例并返回它,我认为这会更容易。

更新

根据你提供的所有评论,ApplicationGBPayday 模型的属性 AppLoanPurpose 可以如下所示使用,就像你在问题中提到的示例一样。

$model = new ApplicationGBPayday([
  'AppLoanPurpose' => 1 // 或 LoanPurpose::GROCERIES
]);

// 输出:1
$model->AppLoanPurpose->value;
// 输出:GROCERIES
$model->AppLoanPurpose->name;
// 输出:Groceries
$model->AppLoanPurpose->label();

如果你还没有使用它,我强烈建议使用 laravel-web-tinker 中的 tinker 进行测试,它为你提供了很多测试的空间。

英文:

If you really want to use enum, you have to treat enum as a regular object.

enum LoanPurpose: Int {
    case OTHER = 0;
    case GROCERIES = 1;

    public function label() {
        return match($this) {
            self::OTHER =&gt; &#39;Other&#39;,
            self::GROCERIES =&gt; &#39;Groceries&#39;
        };
    }
}

$enum = LoanPurpose::GROCERIES;

The above will create an instance of LoanPurpose containing two attributes, name and value.

= LoanPurpose {#xxxx
    +name: &quot;GROCERIES&quot;,
    +value: 1,
  }

Calling those attributes

// will output: GROCERIES
$enum-&gt;name;
// will output 1
$enum-&gt;value;

If you add a method to LoanPurpose, you still have to call that method in order to do something useful. In your setup, you just have to call the label method which performs a match with whatever you're matching.

// will output: Groceries 
$enum-&gt;label();

If you do not want to call the name or value of the enum instance, you could use an attribute accessor like getAppLoanPurposeAttribute and do just that.

use App\Enums\Applications\GB\Payday\LoanPurpose;

class ApplicationGBPayday extends Model {
// ...
  public function getAppLoanPurposeAttribute($value) {
    return return LoanPurpose::from($value)-&gt;name;
  }
// ...

Just remember that you will have lost any usefulness an enum has to offer because ApplicationGBPayday::AppLoanPurpose will never return a LoanPurpose instance.

So the following would not be possible

$ApplicationGBPayday::AppLoanPurpose == LoanPurpose::OTHER

As stated in @silver's answer, you can also use the CastAttribute interface, or entirely create an instance of Attribute and return that instead, which I think would be much easier.

update

So reading all the comments you made, the attribute AppLoanPurpose for model ApplicationGBPayday can be used as follow as specified in the example given in your question.

$model = new ApplicationGBPayday([
  &#39;AppLoanPurpose&#39; =&gt; 1 // or LoanPurpose::GROCERIES
]);

// will output: 1
$model-&gt;AppLoanPurpose-&gt;value;
// will output: GROCERIES
$model-&gt;AppLoanPurpose-&gt;name;
// will output: Groceries
$model-&gt;AppLoanPurpose-&gt;label();

If you're not already using it, I can really advice using tinker with laravel-web-tinker from spatie. It gives you a lot of space to test around.

答案2

得分: 0

转换是将属性值转换为支持的数据类型,

你不能只是将属性强制转换为自己的数据类型,请查看支持的数据类型这里

在使用之前,你必须创建自己的自定义转换,否则,只需使用访问器和修改器

编辑
在 PHP 8.1 或更高版本中支持枚举转换

英文:

Casting is converting an attribute value into a supported data type,

You can't just cast an attribute to your own data type, check the supported data type here

You have to create your own custom cast before you can use it, otherwise, just use an Accessor and Mutator

EDIT
enum casting is supported in PHP 8.1 or above

huangapple
  • 本文由 发表于 2023年2月8日 21:19:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/75386403.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定