reedware / nova-select-toggle-field
添加一个Laravel Nova选择字段,其值将根据另一个选择字段的内容而变化。
Requires
- php: >=7.1.0
This package is auto-updated.
Last update: 2024-09-07 20:57:58 UTC
README
此包提供了一个Laravel Nova选择字段,其值将根据另一个选择字段的内容而变化。
简介
虽然Laravel Nova提供了选择下拉框,甚至可以在其中搜索,但有时我发现不同的用户体验更适合我的需求。选择框有时可能非常长,如果你不熟悉你正在处理的资源,你可能会不知道从哪里开始搜索。
在过去,我还需要采取一些更高级的方法,这些方法涉及动态生成下拉选项。当列表变长时,计算时间开始影响表单的性能。
这两个问题的解决方案之一就是这个包试图提供的“选择切换”。一个“选择切换”实际上是一个下拉选择框,其选项可以根据另一个下拉选择框的内容而变化。
以下是实际操作效果
安装
使用Composer在Laravel Nova应用程序中安装此包
composer require reedware/nova-select-toggle-field
你需要在任何计划使用该字段的资源中包含以下字段
Reedware\NovaSelectToggleField\SelectToggle
或者,你可以安装我的Field Manager Package,该包旨在帮助减少每个资源文件顶部的大量字段包含。
使用方法
由于选择切换字段依赖于另一个字段,因此你需要定义至少两个字段(一个为“目标”字段,另一个为“切换”字段)。
抽象示例
以下是通用设置
public function fields(Request $request) { return [ Select::make('Target Field', 'target_field') ->options([ /* ... values => labels ... */ ]), SelectToggle::make('Toggle Field', 'toggle_field') ->target('target_field') ->options(function($targetValue) { /** * $targetValue is the in-flight form value from the "target_field" field. * Use this value to return your dynamically generated list. The value * will be the value from the target, not the label within the UI. */ return [ /* ... values => labels ... */ ]; }) ]; }
具体示例
以下是您如何在项目中重新创建介绍示例的方法
public function fields(Request $request) { return [ Select::make('Group', 'group_name') ->help('The group containing the resource.') ->options( collect(Nova::$resources)->mapWithKeys(function($resource) { return [$resource::$group => str_replace('.', ' > ', $resource::$group)]; })->unique()->sort() ), SelectToggle::make('Resource', 'resource_name') ->help('The resource within the group.') ->target('group_name') ->options(function($targetValue) { return collect(Nova::$resources)->filter(function($resource) use ($targetValue) { return $resource::$group == $targetValue; })->mapWithKeys(function($resource) { return [$resource => $resource::label()]; })->sort()->toArray(); }) ]; }
复杂示例
本节包含一个复杂示例,这是我在实际项目中使用的一个示例。这是我的“权限”资源的一部分,用户可以创建新的权限,并将其与策略方法关联起来。目标下拉框包含我应用程序中的资源列表,并且这些资源已被按其资源组分组。切换下拉框包含策略中可许可的方法列表(例如“查看所有”、“创建”等),并且它只显示与“目标”下拉框中指定的资源相关的选项。
在此处,我使用了两个其他包,这些包对于此示例是可选的
- 我的Field Manager Package,它允许我使用
Field::select(...)
而不是Select::make(...)
(这是为了在我的资源中只有一个Field
包含)。 - 我的Value Toggle Field,它允许我根据其他字段的内容仅显示某些字段。我使用此功能在指定目标选项之前隐藏选择切换字段。
以下是代码
/** * Returns the fields displayed by the resource. * * @param \Illuminate\Http\Request $request * * @return array */ public function fields(Request $request) { return [ // "Resource" field Field::select(__('Resource'), 'resource_name') ->help('The resource tied to this permission.') ->required() ->options($this->getPermissionResourceOptions()) ->displayUsingLabels(), // "Ability" (on Create form) Field::selectToggle(__('Ability'), 'ability_name') ->onlyOnForms() ->hideWhenUpdating() ->help('The ability being granted to the resource.') ->target('resource_name') ->options(function($targetValue) { return $this->getPolicyMethodOptions($targetValue); }) ->displayUsing(function($value) { return static::getLabelForAbility($value); }) ->valueToggle(function($toggle) { return $toggle->whereNotNull('resource_name'); }), // "Ability" (on Update form) Field::text(__('Ability'), 'ability_name') ->onlyOnForms() ->hideWhenCreating() ->help('The ability name of this permission.') ->readonly() ->resolveUsing(function($value) { return static::getLabelForAbility($value); }), // "Ability" (on Display & Index) Field::text(__('Ability'), 'ability_name') ->exceptOnForms() ->displayUsing(function($value) { return static::getLabelForAbility($value); }) ]; } /** * Returns the permission resource options. * * @return array */ public function getPermissionResourceOptions() { // Determine all of the resources $resources = collect(Nova::$resources); // Filter to only resources that have policies $resources = $resources->filter(function($resource) { return !is_null(Gate::getPolicyFor($resource::$model)); }); // Convert the resources into selection options $options = $resources->map(function($resource) { return [ 'label' => __($resource::label()), 'value' => $resource, 'group' => str_replace('.', ' > ', $resource::$group) ]; }); // Sort the options $options = $options->sortBy(function($option) { return str_pad($option['group'], 255) . '.' . str_pad($option['label'], 255); }); // Exclude the resources that won't have any selectable abilities $options = $options->filter(function($option) { return !empty($this->getPolicyMethodOptions($option['value'])); }); // Return the options return $options->all(); } /** * Returns the policy method options for the specified resource. * * @param string $resource * * @return array */ public function getPolicyMethodOptions($resource) { // Determine the model from the resource $model = $resource::$model; // Determine the policy for the model $policy = Gate::getPolicyFor($model); // Determine the policy methods $methods = $policy::getPermissableMethods(); // Determine the existing options $existing = static::newModel()->newQuery()->where('resource_name', $resource)->pluck('ability_name')->toArray(); // Filter out the existing options $remaining = array_filter($methods, function($method) use ($existing) { return !in_array($method, $existing); }); // Include the current option if($this->exists && $resource == $this->resource_name) { $options[] = $this->ability_name; } // Determine the method options $options = collect($remaining)->mapWithKeys(function($ability) { return [$ability => static::getLabelForAbility($ability)]; }); // Return the options return $options->all(); } /** * Returns the label for the specified ability. * * @param string $ability * * @return string */ public static function getLabelForAbility($ability) { return Str::title(Str::snake($ability, ' ')); }