LaravelDocs(中文)

Eloquent:序列化 (Serialization)

Eloquent:序列化

Eloquent:序列化

簡介

使用 Laravel 建構 API 時,你經常需要將 Model 和關聯轉換為陣列或 JSON。Eloquent 包含了便利的方法來進行這些轉換,以及控制哪些屬性會包含在 Model 的序列化表示中。

[!NOTE] 若要獲得更強大的 Eloquent Model 和 Collection JSON 序列化處理方式,請查看 Eloquent API Resources 的文件。

序列化 Model 與 Collection

序列化為陣列

要將 Model 及其已載入的關聯轉換為陣列,你應該使用 toArray 方法。此方法是遞迴的,因此所有屬性和所有關聯(包括關聯的關聯)都會被轉換為陣列:

use App\Models\User;

$user = User::with('roles')->first();

return $user->toArray();

attributesToArray 方法可用於將 Model 的屬性轉換為陣列,但不包含其關聯:

$user = User::first();

return $user->attributesToArray();

你也可以透過在 Collection 實例上呼叫 toArray 方法,將整個 Model Collection 轉換為陣列:

$users = User::all();

return $users->toArray();

序列化為 JSON

要將 Model 轉換為 JSON,你應該使用 toJson 方法。與 toArray 一樣,toJson 方法是遞迴的,因此所有屬性和關聯都會被轉換為 JSON。你也可以指定任何 PHP 支援的 JSON 編碼選項:

use App\Models\User;

$user = User::find(1);

return $user->toJson();

return $user->toJson(JSON_PRETTY_PRINT);

或者,你可以將 Model 或 Collection 轉型為字串,這會自動呼叫 Model 或 Collection 上的 toJson 方法:

return (string) User::find(1);

由於 Model 和 Collection 在轉型為字串時會被轉換為 JSON,你可以直接從應用程式的路由或控制器回傳 Eloquent 物件。Laravel 會在它們從路由或控制器回傳時自動將 Eloquent Model 和 Collection 序列化為 JSON:

Route::get('/users', function () {
    return User::all();
});

關聯

當 Eloquent Model 被轉換為 JSON 時,其已載入的關聯會自動作為屬性包含在 JSON 物件中。另外,雖然 Eloquent 關聯方法是使用「駝峰式大小寫」方法名稱定義的,但關聯的 JSON 屬性會是「蛇底式」。

從 JSON 隱藏屬性

有時你可能希望限制包含在 Model 陣列或 JSON 表示中的屬性,例如密碼。為此,請在 Model 中新增一個 $hidden 屬性。列在 $hidden 屬性陣列中的屬性不會包含在 Model 的序列化表示中:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 應該為序列化隱藏的屬性。
     *
     * @var array<string>
     */
    protected $hidden = ['password'];
}

[!NOTE] 要隱藏關聯,請將關聯的方法名稱加入到 Eloquent Model 的 $hidden 屬性。

或者,你可以使用 visible 屬性來定義一個「允許清單」,指定應該包含在 Model 陣列和 JSON 表示中的屬性。當 Model 被轉換為陣列或 JSON 時,所有不在 $visible 陣列中的屬性都會被隱藏:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 應該在陣列中可見的屬性。
     *
     * @var array
     */
    protected $visible = ['first_name', 'last_name'];
}

臨時修改屬性可見性

如果你想讓某些通常隱藏的屬性在給定的 Model 實例上可見,你可以使用 makeVisiblemergeVisible 方法。makeVisible 方法會回傳 Model 實例:

return $user->makeVisible('attribute')->toArray();

return $user->mergeVisible(['name', 'email'])->toArray();

同樣地,如果你想隱藏某些通常可見的屬性,你可以使用 makeHiddenmergeHidden 方法:

return $user->makeHidden('attribute')->toArray();

return $user->mergeHidden(['name', 'email'])->toArray();

如果你希望臨時覆寫所有可見或隱藏的屬性,你可以分別使用 setVisiblesetHidden 方法:

return $user->setVisible(['id', 'name'])->toArray();

return $user->setHidden(['email', 'password', 'remember_token'])->toArray();

附加值到 JSON

有時,在將 Model 轉換為陣列或 JSON 時,你可能希望新增在資料庫中沒有對應欄位的屬性。為此,首先為該值定義一個存取器

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 判斷使用者是否為管理員。
     */
    protected function isAdmin(): Attribute
    {
        return new Attribute(
            get: fn () => 'yes',
        );
    }
}

如果你希望存取器始終附加到 Model 的陣列和 JSON 表示中,你可以將屬性名稱加入到 Model 的 appends 屬性中。請注意,屬性名稱通常使用其「蛇底式」序列化表示來參照,即使存取器的 PHP 方法是使用「駝峰式」定義的:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * 要附加到 Model 陣列形式的存取器。
     *
     * @var array
     */
    protected $appends = ['is_admin'];
}

一旦屬性被加入到 appends 清單,它就會包含在 Model 的陣列和 JSON 表示中。appends 陣列中的屬性也會遵循在 Model 上設定的 visiblehidden 設定。

在執行時期附加

在執行時期,你可以使用 appendmergeAppends 方法指示 Model 實例附加額外的屬性。或者,你可以使用 setAppends 方法來覆寫給定 Model 實例的整個附加屬性陣列:

return $user->append('is_admin')->toArray();

return $user->mergeAppends(['is_admin', 'status'])->toArray();

return $user->setAppends(['is_admin'])->toArray();

日期序列化

自訂預設日期格式

你可以透過覆寫 serializeDate 方法來自訂預設的序列化格式。此方法不會影響日期在資料庫中儲存的格式:

/**
 * 為陣列 / JSON 序列化準備日期。
 */
protected function serializeDate(DateTimeInterface $date): string
{
    return $date->format('Y-m-d');
}

為每個屬性自訂日期格式

你可以透過在 Model 的轉換宣告中指定日期格式,來自訂個別 Eloquent 日期屬性的序列化格式:

protected function casts(): array
{
    return [
        'birthday' => 'date:Y-m-d',
        'joined_at' => 'datetime:Y-m-d H:00',
    ];
}