DcatAdmin框架小坑

前言

最近在使用 DcatAdmin 开发后台管理系统时,遇到了一个让我困惑很久的问题。明明已经做了 API 分离,为什么还需要在主控制器里手动编写"更新"、"删除"等方法?这不是 DcatAdmin 应该自动处理的吗?

经过深入研究代码和路由配置,我终于搞清楚了这个"坑"的本质。在这里记录下来,希望能帮到遇到类似问题的朋友。

问题描述

我的项目中有两个控制器:

  • `AutoOrderController.php` - 负责页面展示和 CRUD

  • `AutoOrderApiController.php` - 负责业务逻辑 API

看起来已经做了 API 分离,但是在 `AutoOrderController` 中却还需要写这些方法:

复制代码
```php
/**
 * 更新網址
 */
public function updateUrl($id)
{
    return $this->urlForm()->update($id);
}

/**
 * 刪除網址
 */
public function destroyUrl($id)
{
    return $this->urlForm()->destroy($id);
}
```

为什么其他正常使用 DcatAdmin 的项目,都不需要写这些方法,Grid 和 Form 就能自动完成 CRUD 操作?

原因分析

  1. 标准 DcatAdmin CRUD 流程

在典型的 DcatAdmin 项目中,只需要:

复制代码
```php
class OrderController extends AdminController
{
    protected function grid()
    {
        return Grid::make(new Order(), function (Grid $grid) {
            $grid->column('id');
            $grid->column('name');
            // ...
        });
    }

    protected function form()
    {
        return Form::make(new Order(), function (Form $form) {
            $form->text('name');
            // ...
        });
    }
}
```

然后在路由中注册:

复制代码
```php
Admin::resource('orders', OrderController::class);
```

这样 DcatAdmin 会自动生成 RESTful 路由:

  • GET /orders → index

  • POST /orders → store

  • PUT /orders/{id} → update

  • DELETE /orders/{id} → destroy

这种情况下,确实不需要手写 update/destroy 方法。

  1. 我的项目为什么不同?

关键在于:我在一个页面中实现了两个独立的 Grid(表格)

复制代码
```php
public function index(Content $content)
{
    return $content
        ->header('下單工具')
        ->body(function (Row $row) {
            $row->column(12, $this->orderDataGrid());   // 订单资料表格
            $row->column(12, $this->urlManagementGrid()); // 网址管理表格
            $row->column(12, $this->logCard());
        });
}
```

两个 Grid 各自设置了不同的 resource:

复制代码
```php
// 订单表格
$grid->setResource('auto-order/order-tool');

// 网址表格
$grid->setResource('auto-order/urls');
```

这意味着:

  • 订单表格的操作 → 请求 `/admin/auto-order/order-tool/{id}`

  • 网址表格的操作 → 请求 `/admin/auto-order/urls/{id}`

  1. 路由配置证实了这一点

查看 `app/Admin/routes.php`:

复制代码
```php
// 订单资料路由
$router->get('auto-order/order-tool/{id}/edit', 'AutoOrderController@edit');
$router->put('auto-order/order-tool/{id}', 'AutoOrderController@update');
$router->delete('auto-order/order-tool/{id}', 'AutoOrderController@destroy');

// 网址路由
$router->get('auto-order/urls/{id}/edit', 'AutoOrderController@editUrl');
$router->put('auto-order/urls/{id}', 'AutoOrderController@updateUrl');
$router->delete('auto-order/urls/{id}', 'AutoOrderController@destroyUrl');
```

当用户在页面中点击"编辑网址"按钮时:

  1. Dcat 前端发送请求:`PUT /admin/auto-order/urls/{id}`

  2. 路由匹配到:`AutoOrderController@updateUrl`

  3. **如果删除这个方法 → 直接 404 错误!**

核心要点

| 场景 | 是否需要手写 CRUD 方法 |

|------|----------------------|

| 一个页面一个 Grid,使用 `Admin::resource()` | ❌ 不需要 |

| 一个页面多个 Grid,各自独立 resource | ✅ 必须手写 |

| 自定义路由,不使用标准 RESTful 结构 | ✅ 必须手写 |

最佳实践建议

如果你的页面只管理一个模型

推荐使用标准方式,让 DcatAdmin 自动处理:`

复制代码
``php
// 路由
Admin::resource('orders', OrderController::class);

// 控制器
class OrderController extends AdminController
{
    protected function grid() { /* ... */ }
    protected function form() { /* ... */ }
    // 不需要写 update/destroy 等方法
}
```

如果你需要在一个页面管理多个模型

就像我的项目这样,必须:

  1. 为每个 Grid 设置独立的 `resource`

  2. 为每个 resource 手动实现对应的 CRUD 方法

  3. 在路由文件中手动注册这些路由

    php 复制代码
    // Grid 设置
    $grid->setResource('auto-order/urls');
    
    // 控制器方法
    public function editUrl($id, Content $content) { /* ... */ }
    public function updateUrl($id) { /* ... */ }
    public function destroyUrl($id) { /* ... */ }
    
    // 路由
    $router->get('auto-order/urls/{id}/edit', 'AutoOrderController@editUrl');
    $router->put('auto-order/urls/{id}', 'AutoOrderController@updateUrl');
    $router->delete('auto-order/urls/{id}', 'AutoOrderController@destroyUrl');

关于 API 分离

我的项目确实做了 API 分离:

  • `AutoOrderController` - 负责页面渲染和基础 CRUD

  • `AutoOrderApiController` - 负责复杂业务逻辑(处理订单、管理进程、生成报告等)

但 CRUD 操作仍然需要在主控制器中实现,因为:

  • Dcat 的 Grid/Form 组件需要标准的 CRUD 端点

  • 这些端点必须返回 Dcat 期望的响应格式

  • API 控制器主要服务于外部调用或复杂业务流程

总结

DcatAdmin 的自动 CRUD 功能很强大,但前提是你遵循它的标准模式。当你需要在一个页面中管理多个模型,或者需要自定义路由结构时,就必须手动实现相应的方法。

这不是框架的 bug,而是设计上的灵活性。理解了这一点,就不会再对"为什么需要手写这些方法"感到困惑了。

相关推荐
驰骋工作流9 小时前
流程时效考核的表结构:从待办时限到 WF_CH 考核结果驰骋BPM驰骋低代码低代码工作流引擎
android·低代码·rxjava
传奇开心果编程11 小时前
【Flutter踩坑排雷】第7课:巨型 build 函数坑|全部 UI 写在同一个 build,维护难、隐性异常
android·flutter·ios·移动开发·跨平台·dart·声明式ui
方白羽15 小时前
Android 双开环境底层剖析与风控检测落地
android·安全·app
weixin_5633714715 小时前
Android Studio 2024 版 Device Explorer 打开方法
android·ide·android studio
AIsoft_868918 小时前
职场音频文件怎么整理?录音APP有哪些用法
android·音视频
booboosui18 小时前
第一批个人 Agent 用户正在卸载,问题不在模型,在它住在哪儿
android
洞见前行18 小时前
点击图标到内容出现,你的 App 在这几秒里做了什么:Android 冷启动全链路优化实战
android·性能优化·android studio
凯基迪科技18 小时前
芯片UID绑定修改 加密与逆向修改思路分析
android
Liben19 小时前
Weex开发踩坑记录
android·前端·ios
救救孩子把21 小时前
结构化输出为何“格式正确、答案错误”?JSON Schema 的能力边界
android·java·json·agent·agent服务