Eloquent ORM - Model 资料转换

现在我们可以用各种方法将资料读取出来,不过通常读取後还要将资料做一些转换才适用,举个例子像是 boolean 栏位在 SQL 资料库里存的是数字的 0 跟 1,直接读出来的话也会是 0/1 ,通常我们会想要将这种栏位转换成 false/true 显示。

对於这种情况 Eloquent 就可以在 Model 中指定栏位做资料转换,这样读取出来的资料就会全部自行转换成一样的格式。

反过来也可以定义当写入资料时事先对资料做转换再储存。

Casting

如果是简单的资料格式转换的话,可以在 Model 中用 $casts 定义。

这边用 Todo 简单做示范,用 $casts 定义将 use_id 的值转换为布林值。

@@ -10,10 +10,22 @@ use App\Models\Tag;
 class Todo extends Model
 {
     use HasFactory;
+    
+    protected $casts = [ 
+        'use_id' => 'boolean',
+    ];
 

试着读出来看看

可以看到本来是数字的 user_id 都变成 true 了。

casting 只会影响读取的值,当写入资料时还是依资料表的栏位型别储存。

可以转换的格式

datetime

casting 中比较常用到的是日期的转换,把 UTC 格式转成比较好视读的样子

protected $casts = [ 
    'created_at' => 'datetime:Y-m-d',
];

如果不想把日期栏位一个个加到 casting ,可以定义一个共用的 serializeDate 转换函式

@@ -6,14 +6,30 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 use App\Models\User;
 use App\Models\Tag;
+use DateTimeInterface;
 
 class Todo extends Model
 {
     use HasFactory;
 
     protected $fillable = [
         'name',
     ];
+
+    protected function serializeDate(DateTimeInterface $date)
+    {
+        return $date->format('Y-m-d');
+    }

Accessor

如果要做比较复杂的转换,读取资料可以经由 Accessor 做资料变换,以 Todo 为例,可以看到目前的资料全都是小写。

这时候在 Todo Model 中加上 Accessor getNameAttribute

/app/Models/Todo.php

@@ -14,6 +14,11 @@ class Todo extends Model
     protected $fillable = [
         'name',
     ];
+
+    public function getNameAttribute($value)
+    {
+        return strtoupper($value);
+    }

然後重新整理资料

资料库中的资料完全没变,读出来後变成全大写了。

因为定义了 getNameAttribute ,当经由 Todo Model 读取资料时就会对每个 name 栏位的资料执行转换,像这边是用 strtoupper 将资料转为全大写。

要让 Accessor 对应到栏位是经由函式的命名来设定

get<栏位的驼峰型>Attribute

举例来说

name -> getNameAttribute
first_name -> getFirstNameAttribute

这种参照法还有个有趣的用途,就是创造原本不存在的栏位。

像是在 Model 建立 getUpperNameAttribute 函式

public function getUpperNameAttribute()
{ 
    return strtoupper($this->name);
}

getUpperNameAttribute 会将目前 Model 中的 name 值转为全大写後回传。

这时候用

Todo::first()->upper_name

会发现竟然能读到值,明明 Todo 没有 upper_name 这个栏位。

不过只是这样定义的话,就只有动态读取的时候能够取得 upper_name 的值,并不会常驻在资料结构中,像是用 Todo::get() 的话会发现没有 upper_name 。

appends

如果希望读取出来的每个 Todo 都带有 upper_name 资料的话,要给 Model 追加定义 $appends 变数。

@@ -14,6 +14,13 @@ class Todo extends Model
     protected $fillable = [
         'name',
     ];
+    protected $appends = ['upper_name'];
 
     public function getUpperNameAttribute()
     { 
         return strtoupper($this->name);
     }

Model 会根据 $appends 中的值寻找对应的 Accessor 读取资料後,加入到现有的 Model 回传当中。

注意只要有定义 $appends 就要为每个 append 的值定义 Accessor,不然找不到的话会直接报错。

casts 跟 appends 的顺序

资料读出来会先经过 casts 做转换,才根据 appends 追加栏位,这造成两种状况

  • 在 casts 里定义 append 栏位的资料转换是没意义的,因为当 append 时 cast 已经执行完了
  • append 建立资料会根据 cast 之後的值

像这边先把 name 转换成布林值後 upper_name 也受影响了

Mutator

上面讲的都是读取资料的转换,Model 也可以定义在写入资料时的转换

@@ -6,14 +6,30 @@ class Todo extends Model 
 
     protected $fillable = [
         'name',
     ];
+
+    public function setNameAttribute($value)
+    {
+        $this->attributes['name'] = strtolower($value);
+    }

一样要注意函式的命名

set<栏位的驼峰型>Attribute

另外 Mutator 不像 Accessor 可以直接回传值,必须赋值给 Model 上对应的栏位。


<<:  我目前常用的思考框架

>>:  Day 20: Informix

ProxmoxVE PVE VM 安装 ChromeOS

ProxmoxVE PVE VM 安装 ChromeOS ChromeOS 版本 Download ...

< 关於 React: 开始打地基| function、class function >

09-05-2021 React Component 是基於元件化的思考模式 本章内容 Compon...

D17: 工程师太师了: 第9话

工程师太师了: 第9话 杂记: 最近在泡咖啡 被同事问说: 你一天都喝几杯咖啡阿? 我的回答:我大概...

JavaScript入门 Day11_有关数字的语法3

今天要讲讲数字的其他语法,那今天要讲的是Math.round( ) 那这个是要干嘛的呢,他其实就是四...

Day 4 ( 入门 ) 鱼儿水中游

鱼儿水中游 教学原文参考:鱼儿水中游 这篇文章会介绍,如何在 Scratch 3 里使用角色移动、重...