解决 Dcat-Admin 扩展安装问题

起因

因使用到分布表单插件,下载后无法安装,后经过研究解决了该问题,所以有此文章。

Dcat Admin 插件安装底层逻辑

本地安装表单提交

通过 php artisan route:list -vv 找到 admin/dcat-api/form 插件安装路由的控制器文件 vendor/dcat/laravel-admin/src/Http/Controllers/HandleFormController.php

入口方法分析

// 注:来自 Github Copilot Chat
/**
 * 处理表单提交。
 *
 * 这个方法负责处理表单提交过程。它首先从请求中解析出表单,
 * 检查表单是否通过授权,然后验证表单数据。
 * 如果表单数据无效,它将返回带有验证错误的响应。
 * 如果表单数据有效,它将清理输入,然后处理表单提交。
 * 最后,它发送响应。
 *
 * @param  Request  $request  进入的HTTP请求。
 * @return mixed  要发送回客户端的响应。
 */
public function handle(Request $request)
{
    // 从请求中解析出表单。
    $form = $this->resolveForm($request);

    // 检查表单是否通过授权。
    if (! $form->passesAuthorization()) {
        // 如果表单未通过授权,返回表示授权失败的响应。
        return $form->failedAuthorization();
    }

    // 构建表单。
    $form->form();

    // 验证表单数据。
    if ($errors = $form->validate($request)) {
        // 如果表单数据无效,返回带有验证错误的响应。
        return $form->validationErrorsResponse($errors);
    }

    // 清理输入。
    $input = $form->sanitize($request->all());

    // 处理表单提交并发送响应。
    return $this->sendResponse($form->handle($input));
}

通过分析 $form = $this->resolveForm($request);
这段代码,用来检查请求中的 Form::REQUEST_NAME 参数,也就是 _form_,然后通过 laravel app() 函数实例化一个容器类。
请求如图:

解决 Dcat-Admin 扩展安装问题

这里实例化 Dcat\Admin\Http\Forms\InstallFromLocal 类。
通过调用这段代码 $form->form(); 可以看到实际调用的是:

// vendor/dcat/laravel-admin/src/Http/Forms/InstallFromLocal.php
public function form()
{
    $this->file('extension')
        ->required()
        ->disk($this->disk())
        ->accept('zip', 'application/zip')
        ->autoUpload();
}

这是一个文件上传,将文件自动上传到 $this->disk()

protected function disk()
{
    return config('admin.extension.disk') ?: 'local';
}

解决 Dcat-Admin 扩展安装问题

之后调用 $form->handle($input) 开始解压,加载,写入数据库等一堆事情。

点击:更新至版本 XXX 操作 (表单提交)

启用扩展
安装之后,需要在扩展管理页面点击 更新至xxx版本 以及更新 启用 按钮之后方可正常使用

通过 php artisan route:list -vv 找到 admin/dcat-api/action 插件安装路由的控制器文件 vendor/dcat/laravel-admin/src/Http/Controllers/HandleActionController.php

入口方法分析

// 注:来自 Github Copilot Chat
/**
 * 处理操作请求。
  *
 * 该方法负责处理操作请求的过程。它首先从请求中解析出操作实例,设置操作的键,然后检查操作是否通过授权。
  * 如果操作未通过授权,它将返回一个表示授权失败的响应。
  * 如果操作通过了授权,它将处理操作请求。
  * 最后,它检查响应是否是 Response 的实例,如果是,它发送响应,否则直接返回响应。
  *
 * @param Request  $request 进入的HTTP请求。
  * @return mixed 要发送回客户端的响应。
  */
public function handle(Request $request)
{
    // 从请求中解析出操作实例。
  $action = $this->resolveActionInstance($request);

    // 设置操作的键。
  $action->setKey($request->get('_key'));

    // 检查操作是否通过授权。
  if (! $action->passesAuthorization()) {
        // 如果操作未通过授权,返回一个表示授权失败的响应。
  $response = $action->failedAuthorization();
    } else {
        // 如果操作通过了授权,处理操作请求。
  $response = $action->handle($request);
    }

    // 检查响应是否是 Response 的实例,如果是,发送响应,否则直接返回响应。
  return $response instanceof Response ? $response->send() : $response;
}

通过分析 $action = $this->resolveActionInstance($request);
这段代码,用来检查请求中的 _action 参数,然后通过 laravel app() 函数实例化一个容器类。
请求如图:

解决 Dcat-Admin 扩展安装问题

这里实例化 Dcat\Admin\Http\Forms\InstallFromLocal 类。
通过调用这段代码 $response = $action->handle($request); 可以看到实际调用的是:

// vendor/dcat/laravel-admin/src/Http/Actions/Extensions/Update.php
public function handle()
{
    $manager = Admin::extension()
        ->updateManager()
        ->update($this->getKey());

    return $this
        ->response()
        ->success(implode('<br>', $manager->notes))
        ->refresh();
}

Admin::extension() 是一个静态方法调用,它在 Dcat\Admin\Admin 类中定义。
这个方法返回一个 Dcat\Admin\Extend\Manager 实例,该实例用于管理和操作扩展。
Dcat\Admin\Extend\Manager 类提供了一系列方法来处理扩展,例如注册、初始化、启用/禁用扩展,加载扩展,获取扩展的路径,获取扩展对象,判断扩展是否存在,获取所有的扩展,获取所有已启用的扩展,以及解压缩扩展包等。
因此,Admin::extension() 的主要作用是提供一个方便的方式来获取扩展管理器,以便进行扩展的各种操作。

vendor/dcat/laravel-admin/src/AdminServiceProvider.php 文件中可以找到所有容器:

public function registerServices()
{
    $this->app->singleton('admin.app', Application::class);
    $this->app->singleton('admin.asset', Asset::class);
    $this->app->singleton('admin.color', Color::class);
    $this->app->singleton('admin.sections', SectionManager::class);
    $this->app->singleton('admin.extend', Manager::class);
    $this->app->singleton('admin.extend.update', function () {
        return new UpdateManager(app('admin.extend'));
    });
    $this->app->singleton('admin.extend.version', function () {
        return new VersionManager(app('admin.extend'));
    });
    $this->app->singleton('admin.navbar', Navbar::class);
    $this->app->singleton('admin.menu', Menu::class);
    $this->app->singleton('admin.context', Context::class);
    $this->app->singleton('admin.setting', function () {
        return Setting::fromDatabase();
    });
    $this->app->singleton('admin.web-uploader', WebUploader::class);
    $this->app->singleton(ExceptionHandler::class, config('admin.exception_handler') ?: Handler::class);
    $this->app->singleton('admin.translator', Translator::class);
}

最后追踪到 vendor/dcat/laravel-admin/src/Extend/UpdateManager.php 文件的:

public function update($name, ?string $stopOnVersion = null)
{
    $name = $this->manager->getName($name);

    if (! ($extension = $this->manager->get($name))) {
        $this->note('<error>Unable to find:</error> '.$name);

        return;
    }

    $this->note($name);

    $this->versionUpdate($extension, $stopOnVersion);

    $this->publish($name);

    return $this;
}

分析 $this->versionUpdate($extension, $stopOnVersion); 方法得出,该方法主要是处理文件版本和数据库版本问题。通过比对版本,决定是否写入数据库 admin_extensions 表。
最新的 Dcat Admin 代码中对这一块的处理有点问题,导致无法写入版本到数据库,导致安装失败。
可以看一下这个提交:Update VersionManager.php #2108

解决 Dcat-Admin 扩展安装问题

最后,注意扩展名称,从目录开始 dcat-admin-extensions/dcat-admin/form-step 就是 dcat-admin.form-step

终其原因:第一次安装时没有考虑版本为 0 匹配不到的情况,导致插件安装失败。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!