解决 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\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';
}
之后调用 $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\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-extensions/dcat-admin/form-step
就是 dcat-admin.form-step
终其原因:第一次安装时没有考虑版本为 0
匹配不到的情况,导致插件安装失败。
本作品采用《CC 协议》,转载必须注明作者和本文链接