Laravel Nova 首页隐藏编辑删除按钮,详情页保留
2025-03-21 01:26:32
Laravel Nova 首页隐藏编辑和删除按钮,详情页保留
有些时候,咱需要调整 Laravel Nova 资源列表(Index)页面的操作按钮。比如,想隐藏“编辑”和“删除”按钮,但在资源详情(Detail)页面又希望保留它们。直接用 Policy 可能会把所有地方的操作都禁掉,这不行!咱得想个更灵活的招儿。
问题:一刀切的 Policy 不好使
用 Policy 来控制权限,默认情况下,会影响到所有页面(Index、Detail 等)。假如在 Policy 里直接禁止 update
或 delete
,那在详情页也没法编辑或删除了,这不符合需求。
// 比如这样写 Policy,就会把所有地方的更新操作都禁止掉
public function update(User $user, Customer $customer)
{
return false;
}
解决方案:多管齐下
有好几种方法可以解决这个问题,每种方法都有自己的特点。咱一个个来看:
1. 利用请求路由名称 (有点“野”,但不失为一种办法)
直接在 Policy 的 update
和 delete
方法里判断当前的路由名称。如果是 Nova 资源列表页的路由,就返回 false
,禁止操作;否则,允许操作。
// 在你的 Policy 文件里 (比如 CustomerPolicy.php)
public function update(User $user, Customer $customer)
{
if (request()->routeIs('nova.pages.index')) { //或者检查您资源列表页自定义的特定路由。
return false;
}
return true; // 默认允许
}
public function delete(User $user, Customer $customer)
{
if (request()->routeIs('nova.pages.index')) {
return false;
}
return true; // 默认允许.
}
原理: Nova 内部在处理请求时,会根据当前的页面(Index、Detail 等)设置不同的路由名称。通过判断路由名称,就能区分出当前是在哪个页面。
优点: 简单粗暴,代码改动小。
缺点: 依赖于 Nova 内部的路由名称,如果 Nova 将来更新了路由命名方式(虽然可能性不大),这招就可能失效了。
额外说明: 可以通过自定义Nova 资源列表路由,使用 ->withIndexQuery
,在 indexQuery 做条件判断。这种方法其实类似。
2. 自定义 Action 按钮(推荐,更优雅)
Nova 允许咱自定义 Action 按钮。可以创建两个自定义的 Action:一个用于编辑,一个用于删除。然后在 index
查询作用域修改默认行为.
步骤:
-
创建自定义 Action:
php artisan nova:action EditCustomer php artisan nova:action DeleteCustomer
-
修改
EditCustomer
Action (app/Nova/Actions/EditCustomer.php):<?php namespace App\Nova\Actions; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Support\Collection; use Laravel\Nova\Actions\Action; use Laravel\Nova\Fields\ActionFields; use Laravel\Nova\Http\Requests\NovaRequest; // 导入这个 class EditCustomer extends Action { use InteractsWithQueue, Queueable; /** * 在资源列表页隐藏 * @param \Laravel\Nova\Http\Requests\NovaRequest $request * @param \Illuminate\Support\Collection $models * * @return bool */ public function authorizedToRun(NovaRequest $request, $model) //使用参数$model { if( is_null($model) || $request->viaResource() ){ return false; // 首页,批量编辑不可见 } return $request->user()->can('update', $model); } /** * Perform the action on the given models. * * @param \Laravel\Nova\Fields\ActionFields $fields * @param \Illuminate\Support\Collection $models * @return mixed */ public function handle(ActionFields $fields, Collection $models) { $customer = $models->first(); //注意 nova action handle 默认就是 循环处理选择, //如果只是要实现编辑功能,通常选择数量是 1,取 $models 集合中的第一个元素进行后续操作即可。 // 这里可以写编辑逻辑(如果需要的话,比如跳转到自定义的编辑页面) // 直接使用 redirect。 // 通过 nova.path 获取基础配置,resource 资源, $model 模型。 //return Action::message('编辑成功!'); // 如果不需要跳转,可以显示个消息 return Action::redirect(config('nova.path')."/resources/customers/{$customer->id}/edit"); } /** * Get the fields available on the action. * * @param \Laravel\Nova\Http\Requests\NovaRequest $request * @return array */ public function fields(NovaRequest $request) { return []; } }
-
修改
DeleteCustomer
Action (app/Nova/Actions/DeleteCustomer.php):<?php namespace App\Nova\Actions; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Support\Collection; use Laravel\Nova\Actions\Action; use Laravel\Nova\Fields\ActionFields; use Laravel\Nova\Http\Requests\NovaRequest; class DeleteCustomer extends Action { use InteractsWithQueue, Queueable; /** * 只在详情页显示, * index 或 批量删除时不显示 * @param NovaRequest $request * @param $model * @return bool|void */ public function authorizedToRun(NovaRequest $request, $model) { if( is_null($model) || $request->viaResource() ){ //来自 index return false; } return $request->user()->can('delete', $model); } /** * Perform the action on the given models. * * @param \Laravel\Nova\Fields\ActionFields $fields * @param \Illuminate\Support\Collection $models * @return mixed */ public function handle(ActionFields $fields, Collection $models) { foreach ($models as $model) { $model->delete(); //执行真正的删除,或其他符合你需要的删除处理 } return Action::message('删除成功!'); } /** * Get the fields available on the action. * * @param \Laravel\Nova\Http\Requests\NovaRequest $request * @return array */ public function fields(NovaRequest $request) { return []; } }
-
修改 Resource的
actions
方法(例如 app/Nova/Customer.php) :
public function actions(NovaRequest $request)
{
return [
new Actions\EditCustomer,
new Actions\DeleteCustomer,
];
}
原理:
authorizedToRun
方法用于控制 Action 是否显示。利用NovaRequest
对象,可以判断当前请求是否来自资源列表页($request->viaResource()
),以此决定是否隐藏 Action。- 当单个资源模型,传入
authorizedToRun
,第二个参数$model
不为 null. 而首页是多个或者说不确定, 故$model
总是为null。
优点:
- 更优雅、更符合 Nova 的设计思想。
- 不依赖于内部实现细节,更稳定。
进阶用法:
可以增加批量编辑/批量删除 的控制.
例如 $request->viaResourceId
判断是否存在. 决定是显示那个按钮.
3. CSS 障眼法(最简单粗暴,但不推荐)
直接用 CSS 把资源列表页的编辑和删除按钮隐藏掉。
/* 在你的 Nova 自定义 CSS 文件里 (比如 resources/css/nova.css) */
/* 隐藏资源列表页的编辑按钮 */
.index-table [data-testid="edit-button"] {
display: none;
}
/* 隐藏资源列表页的删除按钮 */
.index-table [data-testid="delete-button"] {
display: none;
}
/* 或者用更通用的选择器,如果你知道按钮的类名或属性 */
/*
.index-table .your-edit-button-class {
display: none;
}
*/
原理: 通过 CSS 选择器找到目标元素,然后把它们隐藏。
优点: 简单到爆炸,不需要改任何 PHP 代码。
缺点:
- 这只是视觉上的隐藏,实际上按钮还在那里,只是看不见了。如果用户懂点前端知识,或者通过其他方式(比如 API 请求),还是可以进行编辑和删除操作。
- 不够“优雅”,有点“Hack”的味道。
安全建议: 这种方法只适用于对安全性要求不高的场景 。如果需要严格控制权限,请不要使用这种方法。 还是老老实实用上面说的方法。
4.前端组件控制(vue 层面, 相对更灵活.)
这种就需要修改对应的 vue 文件. 需要一定的 vue 开发能力.
大致思路:
找到 index 相关的组件. 在 mounted 生命周期或 computed 属性进行判断处理。
在对应的 components, 找到 Index.vue (可能会是TableIndex.vue 或 CardIndex.vue ,取决你的UI设计)。
找到按钮位置. 通过控制其 v-if
或 动态 class,进行隐藏。
添加计算属性 shouldShowEditButton
or shouldShowDeleteButton
进行控制。
原理: 通过 nova 提供的组件, 进行 vue 层级的控制.
<!-- 可能是TableIndex.vue,或者 CardIndex.vue , 根据资源显示来定-->
<template>
<div>
<button v-if="shouldShowEditButton" @click="editResource">编辑</button>
</div>
</template>
<script>
export default {
computed:{
shouldShowEditButton(){
// 获取当前的 route.name 或 $route.
// 通过 this.$route 即可. 判断. this.$route.name==='your-detail-route-name' 之类的判断
return this.$route.name === 'your-resource-detail-route-name' ; //自定义的名字.
}
},
methods: {
editResource()
{
// ... 触发路由调整, 跳转.
}
}
};
</script>
优缺点:
- 优点: 前端控制,性能更好.
- 缺点: 需要 vue 开发. 成本相对高点. 修改不方便(至少比配置php要复杂).
建议综合利用 vue + action 双重结合。
小结
根据上面的介绍,自定义 Action (第二种)是我比较推荐的方式,比较优雅,功能可扩展性好,结合 Policy 和 Action 可控程度较高。其他的方案,根据实际情况和个人喜好选择即可. 前端组件的控制可以作为高级进阶内容, 但是整体成本较大.