Backend Controller
Wncms\Http\Controllers\Backend\BackendController 是 后台 CRUD controllers 的基础。它标准化了 model 解析、命名、cache-tag 处理和常见操作(index/create/store/edit/update/destroy/bulk_delete)。为每个后台资源扩展它。
主要职责
- 从 controller 名称解析 model class(
PostController→ model keypost→wncms()->getModelClass('post'))。 - 派生用于视图和标签的资料表 / 单数 / 复数名称。
- 提供 cache tag 辅助方法和
flush()方法。 - 提供具有合理预设值和 AJAX JSON 回应的主观 CRUD 方法。
属性和预设值
| 属性 | 类型 | 来源 | 预设行为 |
|---|---|---|---|
$modelClass | string | getModelClass() | 从不含 Controller 的 controller 基础名称,snake-cased,透过 wncms()->getModelClass(...) 解析。 |
$cacheTags | array | getModelCacheTags() | 预设为 [$this->getModelTable()]。 |
$singular | string | getModelSingular() | str()->singular($this->getModelTable())。 |
$plural | string | getModelPlural() | str()->plural($this->getModelSingular())。 |
在需要时,在子 controller 中将这些作为 protected 属性覆盖。
可覆盖的辅助方法
// 从 controller 名称解析 model class;如需自订映射请覆盖。
public function getModelClass(): string
// 取得底层 Eloquent 资料表名称。
protected function getModelTable()
// 为此资源提供自订 cache tags。
protected function getModelCacheTags(): array
// 自订资源名词。
protected function getModelSingular(): string
protected function getModelPlural(): string
// 为 single/multi 网站模式套用当前网站列表筛选。
protected function applyBackendListWebsiteScope(Builder $q, ?Request $request = null, bool $onlyWhenExplicitFilter = false): void
// 为 create/update 流程解析网站 ID。
protected function resolveBackendMutationWebsiteIds(bool $fallbackToCurrentWhenEmpty = false): array
// 为已创建/更新模型同步网站绑定。
protected function syncBackendMutationWebsites($model, bool $fallbackToCurrentWhenEmpty = false): voidCache 控制
public function flush(string|array|null $tags = null): bool- 透过
wncms()->cache()->tags($tag)->flush()清除已标记的快取。 - 如果
$tags为null,使用$this->cacheTags。
多站点列表筛选辅助方法
applyBackendListWebsiteScope() 用于标准化后台 index 列表筛选(仅针对网站模式为 single 或 multi 的模型)。
- 优先读取请求中的
website_id(兼容旧键website)。 - 当请求未传筛选值时,回退到
wncms()->website()->get()?->id当前网站 ID。 - 仅在模型支持多站点作用域时调用
applyWebsiteScope(...)。 - 对
global模型或无法解析当前网站时不做任何处理。 - 对 index 工具列筛选,建议统一使用
website_id作为请求参数,并兼容读取旧键website。 - 对需要预设显示全部数据的页面(例如 Posts),第三个参数传
true,仅在请求明确传入website_id时才套用网站作用域。 - 共用网站筛选器应仅在
gss('multi_website')启用且模型网站模式为single/multi时显示;global模式应隐藏。
明确筛选模式范例:
$q = $this->modelClass::query();
$this->applyBackendListWebsiteScope($q, $request, true);多站点写入辅助方法
在 create/update 流程,建议使用 syncBackendMutationWebsites($model) 保持 global / single / multi 模式兼容。
- 对
single/multi模型,会从请求键(website_id、website_ids,及旧键)解析网站 ID。 - 对非管理员用户,会自动将请求网站 ID 与当前用户可访问网站做交集过滤。
- 预设不会在网站 ID 为空时强制回退到当前网站。
- 若业务流程需要回退,请改为调用
syncBackendMutationWebsites($model, true)。 - 对
global模型会安全 no-op。
内建 CRUD 操作
所有操作假设标准的 backend Blade 路径:backend.{plural}.*。
index(Request $request)- 在
$modelClass上建立基础查询,按id desc排序,回传backend.{plural}.index。 - 传递
page_title、models。
- 在
create(int|string|null $id = null)- 新实例或载入现有实例用于「复制/编辑为新」模式。
- 回传
backend.{plural}.create与model。
store(Request $request)create($request->all()),然后:透过
syncBackendMutationWebsites($model)自动同步网站绑定。- 如果是 AJAX:JSON
{ status, message, redirect }。 - 否则:重定向到
route('{plural}.edit', ['id' => $model->id])。
- 如果是 AJAX:JSON
edit(int|string $id)- 载入 model,回传
backend.{plural}.edit与model。
- 载入 model,回传
update(Request $request, $id)- 类似
findOrFail的行为(如果缺少则回传讯息),update($request->all())。 - 透过
syncBackendMutationWebsites($model)自动同步网站绑定。 - 如果是 AJAX:JSON
{ status, message, redirect }。 - 否则:重定向回编辑页面。
- 类似
destroy($id)- 删除 model,呼叫
$this->flush(),重定向到 index 并显示成功讯息。
- 删除 model,呼叫
bulk_delete(Request $request)- 接受
model_ids作为 CSV 或阵列,批次删除。 - 如果是 AJAX:包含已删除数量的 JSON;否则
back()并显示讯息。
- 接受
讯息遵循 WNCMS 翻译(例如
__('wncms::word.successfully_updated'))。标题使用__('wncms::word.' . $this->singular)。
最小子类别范例
namespace App\Http\Controllers\Backend;
use Illuminate\Http\Request;
use Wncms\Http\Controllers\Backend\BackendController;
class ProductController extends BackendController
{
// BackendController 会自动解析 'product' → wncms()->getModelClass('product')
// 除非您覆盖 getModelClass() 或设定 protected $modelClass
}覆盖范例
namespace App\Http\Controllers\Backend;
use Illuminate\Http\Request;
use Wncms\Http\Controllers\Backend\BackendController;
class CustomProductController extends BackendController
{
protected function getModelClass(): string
{
return \App\Models\Product::class; // 自订映射
}
protected function getModelCacheTags(): array
{
return ['products', 'catalog']; // 自订 tags
}
public function index(Request $request)
{
// 使用 parent 的逻辑,或完全覆盖
$query = $this->modelClass::query();
$this->applyBackendListWebsiteScope($query);
// 添加自订筛选
if ($category = $request->input('category')) {
$query->where('category', $category);
}
$models = $query->orderByDesc('id')->paginate(20);
return $this->view("backend.{$this->plural}.index", [
'page_title' => __('wncms::word.model_management', ['model_name' => __('wncms::word.' . $this->singular)]),
'models' => $models,
]);
}
}总结
- 为每个后台资源扩展
BackendController。 - 依赖自动 model 解析或根据需要覆盖。
- 使用内建的 CRUD 方法或覆盖以进行自订逻辑。
- 利用 cache flushing 和 WNCMS 辅助方法保持程式码简洁。
手动 sort 栏位排序模式
如果模型有业务排序栏位(例如 sort),建议将其设为 backend index 的预设排序,这样更新顺序后可立即在列表中看到结果。
$sort = in_array($request->sort, $this->modelClass::SORTS) ? $request->sort : 'sort';
$direction = in_array($request->direction, ['asc', 'desc']) ? $request->direction : 'desc';
$q->orderBy($sort, $direction);
// 当主排序值相同,使用 id 维持稳定顺序。
if ($sort !== 'id') {
$q->orderBy('id', 'desc');
}可避免固定追加 orderBy('id', 'desc') 后,让手动排序更新难以在后台列表中验证的问题。
WNCMS 多站点兼容写入模式
当模型支持 WNCMS 多站点方法时,不要在 create()/update() payload 硬写旧版外键栏位(例如 website_id)。建议先用 controller 共享 helper 解析网站 ID,再用 syncModelWebsites(...) 绑定站点关系:
$websiteIds = $this->resolveModelWebsiteIds($this->modelClass);
$model->update([
'name' => $request->name,
'type' => $request->type,
]);
$this->syncModelWebsites($model, $websiteIds);在后台表单页面,建议复用通用网站选择器 partial,而不是重复写网站输入 UI:
@include('wncms::backend.common.website_selector', ['model' => $model, 'websites' => $websites ?? []])