在 Laravel Nova 中对多个布尔属性进行分组
发布于 作者: Doeke Norg
Laravel Nova 带来了大量令人惊叹的字段。默认情况下,这些字段非常智能,几乎可以适应任何情况。但是,如果您的 UI 符合您的需求,但字段处理数据的方式却有所不同,该怎么办?您是否必须 创建自定义字段?答案可能是否定的。
自定义字段
Laravel Nova 的字段包含许多方法,允许您自定义其行为。如文档中所述,您可以通过附加 fillUsing()
回调来自定义相应模型的 数据填充 方式。您还可以通过附加 resolveUsing()
回调来自定义字段的 解析 方式。因此,让我们使用这些函数来创建自己的“自定义”字段。
BooleanGroup
中
将多个布尔值分组到 假设您有一个 Message
模型,它表示可以在多个范围内显示的消息,例如:website
、app
和 rss
。您希望能够根据范围轻松查询这些消息,因此您添加了 3 个布尔字段:scope_website
、scope_app
和 scope_rss
。
是的,您可以创建类别并创建一个中间表;但是,您只有这些范围,然后您就想起了这是一个示例。.
<?php namespace App\Nova\Resources; use App\Nova\Resource;use Illuminate\Http\Request;use Laravel\Nova\Fields\BooleanGroup; class MessageResource extends Resource{ // ... public function fields(Request $request): array { return [ BooleanGroup::make('Scopes')->options($options = [ 'scope_website' => 'Website', 'scope_app' => 'Application', 'scope_rss' => 'RSS Feed', ]) ]; }}
填充模型属性
我们不希望将 3 个不同的字段添加到 Nova Resource
中,而是希望添加一个包含这些范围的 BooleanGroup
。默认情况下,BooleanGroup
字段会将其选项存储为单个属性上的 JSON 块。为了改变此行为,我们将通过使用此回调函数调用 fillUsing
方法来自定义字段的填充。
use App\Model\Message;use Laravel\Nova\Http\Requests\NovaRequest; // this function goes inside ->fillUsing(...)function (NovaRequest $request, Message $model, string $attribute, string $requestAttribute) { // Make sure the `scopes` value exists on the request. if (!$request->exists($requestAttribute)) { return; } // Decode the values because it is send as a JSON blob. $values = json_decode($request[$requestAttribute], true); // Hydrate the model. foreach ($values as $key => $value) { $model->{$key} = $value; }}
fillUsing()
的回调函数接收 4 个参数
-
NovaReqeust $request
具有POST
值的请求对象 -
Message $model
我们要填充的模型 -
string $attribute
模型上的属性名称(在本示例中我们不使用它) -
string $requestAttribute
$request
对象中包含我们POST
值的属性名称
在确保我们发布了值后,我们可以将所有这些选项映射到其模型属性。实际上,相当于调用类似以下内容:
$model->scope_website = 1; // or 0
解析字段
现在,我们已经将布尔值存储在正确的属性上,现在该看看在 Nova 中如何解析字段了。虽然模型可能设置了范围,但所有复选框都将处于“关闭”状态。这是因为该字段仍然尝试从 $model->scopes
中检索其值;而 $model->scopes
不存在。因此,让我们通过添加 resolveUsing
回调来修复它。
您可能已经注意到,我们在
$options
变量中定义了字段选项。这是故意的,因为我们需要这些选项的键。到目前为止,resolveUsing
回调无法访问该字段本身来检索这些选项。这是我们的解决方法。
// this function goes inside ->resolveUsing(...)function ($value, Message $model, string $attribute) use ($options) { $keys = array_keys($options); $values = array_map(function($value, $key) use ($model) { return $model->{$key}; }, $options, $keys); return array_combine($keys, $values);}
resolveUsing()
的回调函数接收 3 个参数
-
mixed $value
Laravel Nova 尝试检索的值 -
Message $model
提供该值的模型 -
string $attribute
模型上的属性名称(同样,我们不需要它)
我们的回调函数唯一需要做的就是从模型中检索每个布尔值的值,并将这些值作为数组返回。这段代码有点乱。很难看懂它在做什么,而且我们需要在多个函数范围内 use
值。让我们使用简写函数和一些 Laravel collect
魔法来清理代码。
fn($value, Message $model) => collect($options)->map(fn($value, $key) => $model->{$key})
瞧,一个简洁的单行代码,可以从正确的模型属性中解析字段。刷新 Nova 页面后,复选框应该会正确地显示其状态。
required
附加:将此字段设为 为了好玩,假设您需要选择至少一个范围。您可能最先想到的是在字段上设置 ->required()
,但这实际上不起作用,虽然它会在表单上显示一个漂亮的星号 *
。幸运的是,我们也可以通过在字段上调用 rules()
方法来添加自定义验证规则。
$field->rules('required', function (string $attribute, $value, callable $fail) { if (!array_filter(json_decode($value, true) ?? [])) { return $fail(sprintf('The "%s" field must have at least one option selected.', $attribute)); }})
设置 required
规则也会在表单上添加星号 *
。该回调函数会快速检查是否有任何值被返回为 true
。如果没有,我们会调用 $fail
可调用函数并提供验证失败的原因。
就这样;一个自定义字段,无需实际构建自定义字段。