跳到主要内容

← 快速开始 · 返回 README · 预加载 →

使用设置

同一个服务同时处理默认值和模型值。入口方法决定要读取或修改的作用域:

入口作用域
(new User)->defaultSettings()所有已保存 User 模型共享的默认值
$user->settings()一个已保存用户的最终设置

共享默认值​

默认值适用于 morph 类相同的所有已保存 Eloquent 模型:

$defaults = (new User)->defaultSettings();

$defaults->set('timezone', 'UTC');
$defaults->set('notifications', ['email' => true]);

通过同一个服务读取或删除默认值:

$timezone = $defaults->get('timezone');
$all = $defaults->all();

$defaults->forget('timezone');

每个模型类的默认值相互独立。

单个模型的覆盖值​

set() 会创建设置或替换其现有值:

$user->settings()->set('timezone', 'Europe/Paris');
$user->settings()->set('timezone', 'America/Toronto');

只有该模型的设置会被修改。其他模型继续使用自己的覆盖值或共享默认值。

get()、has() 和 all() 按相同的优先级解析值:

$timezone = $user->settings()->get('timezone');
$hasTimezone = $user->settings()->has('timezone');
$settings = $user->settings()->all();

all() 返回一个按设置键索引的 Illuminate\Support\Collection。

get() 只接受键。它依次返回模型覆盖值、持久化的类默认值和 null,不接受调用方提供的回退值。has() 可区分键不存在和已存储的 JSON null:

if ($user->settings()->has('timezone')) {
$timezone = $user->settings()->get('timezone');
}

例如,一个覆盖值只替换对应的默认值:

(new User)->defaultSettings()->set('timezone', 'UTC');
(new User)->defaultSettings()->set('locale', 'en');

$user->settings()->set('timezone', 'Europe/Paris');

assert($user->settings()->all()->sortKeys()->all() === [
'locale' => 'en',
'timezone' => 'Europe/Paris',
]);

删除值​

删除模型覆盖值后会再次使用默认值:

(new User)->defaultSettings()->set('timezone', 'UTC');

$user->settings()->set('timezone', 'Europe/Paris');
$user->settings()->forget('timezone');

assert($user->settings()->get('timezone') === 'UTC');

若要删除默认值本身,请通过 defaultSettings() 调用 forget():

(new User)->defaultSettings()->forget('timezone');

对不存在的键调用 forget() 不会产生影响。

批量修改​

需要在一个作用域内修改多个值时,请使用 setMany() 和 forgetMany():

$user->settings()->setMany([
'timezone' => 'Europe/Paris',
'locale' => 'fr',
]);

$user->settings()->forgetMany(['timezone', 'locale']);

这两个方法接受任意 iterable。setMany() 会在写入前规范化每个键。多个输入键规范化为同一个存储键时, 最后一个值生效。每个值都会被存储;只有 forget() 和 forgetMany() 会删除记录。

非空的 setMany() 批次会在一个事务中执行一次数据库原生 upsert。forgetMany() 使用一次删除处理所有 列出的键。查询次数受操作类型限制,而不是键的数量。

使用 purge() 删除整个当前作用域:

$user->settings()->purge();

对于 settings(),purge() 只删除该所有者的覆盖值,并重新显示持久化的默认值。对于 defaultSettings(),它会删除该模型类的默认值,但不会删除模型覆盖值。这三个批量方法均返回 void。

JSON 值​

set() 和 setMany() 会准确存储 JSON 值:

值结果
null保存
'' 或仅包含空白字符的字符串保存
[]保存
0保存
false保存
'0'保存

已存储的 null 视为存在的值。has($key) 返回 true,get($key) 返回 null。值为 null 的模型覆盖 值还会隐藏非空的类默认值,直到通过 forget() 删除该覆盖值。

设置键​

键可以是字符串、整数或实现 UnitEnum 的 PHP 枚举:

enum SettingKey: string
{
case Timezone = 'timezone';
}

$user->settings()->set(SettingKey::Timezone, 'Europe/Paris');

$timezone = $user->settings()->get(SettingKey::Timezone);

Laravel 使用 backed enum 的底层值进行存储,使用 pure unit enum 的 case 名称进行存储。读取、替换或删除 设置时,请使用相同的键或枚举 case。

空键以及仅包含空白字符的键会抛出 DragonCode\LaravelModelSettings\Exceptions\InvalidSettingKey。整数和枚举规范化为存储字符串后再验证。 异常消息绝不会包含被拒绝的键或设置数据。

点号是普通字符。键 mail.from.address 是一个不可拆分的设置键,绝不表示嵌套路径:

$user->settings()->set('mail.from.address', '[email protected]');

$address = $user->settings()->get('mail.from.address');

模型标识符​

支持整数、字符串、UUID 和 ULID 主键。

修改单个模型的设置时,需要所有者已持久化,且主键不为 null。对于未保存的模型,get() 返回 null, has() 返回 false,all() 不查询模型覆盖值并返回空集合。它的 set()、setMany()、forget()、 forgetMany() 和 purge() 会在执行存储查询或读取 iterable 前抛出 InvalidSettingsOwnerException。

标识符为整数 0 或字符串 '0' 的已持久化模型支持与其他已保存所有者相同的读取和修改操作。作用域判别 字段将它们的覆盖值与类默认值分开,即使两条记录都保留 item_id = '0'。其他字符串主键(包括 '00') 仍然有效。

设置按模型当前的 morph 类存储。在设置写入后新增或修改 morph map 别名时,需要更新现有的 item_type 值。

另请参阅​

  • 预加载 — 避免为每个模型执行一次设置查询。
  • 数据转换 — 返回领域对象,而不是解码后的 JSON。
  • API 参考 — 查看方法签名和返回值。