首页 / 开发文档 / 后端开发

后端开发文档

XPCMS 基于 ThinkPHP8 框架,完整后台开发体系指南。涵盖 MVC 架构分层、CurdTrait 快速开发、中间件体系、Service 服务层、新模块开发流程及代码规范。

1. 架构分层

XPCMS 采用 6 层分层架构,请求经过多层处理才能到达业务逻辑,确保安全与可维护性。

请求处理流程

用户请求 → 路由中间件(AuthCheck) → 权限中间件(PermissionCheck)
    → 控制器(Controller) → 验证器(Validate)
    → 逻辑层(Service) → 模型(Model) → 数据库
层级目录位置职责核心类
中间件层app/middleware/请求前置拦截:认证、权限、CORSAuthCheck、PermissionCheck
控制器层app/模块名/controller/接收请求、参数绑定、调用Service、返回响应BaseController
验证器层app/模块名/validate/参数校验、数据过滤、业务规则验证BaseValidate
逻辑层app/模块名/service/业务逻辑封装、数据组装、事务管理各业务Service类
模型层app/模块名/model/数据库访问、关系定义、查询作用域BaseModel
Traitsapp/common/traits/可复用代码块:CurdTrait、ApiResponseCurdTrait

2. CurdTrait 快速开发

CurdTrait 是 XPCMS 最核心的代码复用机制,在控制器中引入即可自动获得完整的增删改查接口能力。位于 app/common/traits/CurdTrait.php

2.1 控制器属性定义(6 个属性)

属性类型必填说明
$modelClassstring✅ 是模型类全限定名,如 app\admin\model\Article::class
$validateClassstring验证器类全限定名,留空则跳过验证
$searchFieldsarray搜索字段列表,支持模糊搜索
$sortFieldstring默认排序字段,默认 id
$sortOrderstring默认排序方向,默认 desc
$withTrashedbool是否包含软删除数据,默认 false

2.2 自动注册接口(6 个接口)

方法请求方式路由后缀功能
index()GET/index分页列表查询,支持搜索与排序
save()POST/save新增记录,自动调用验证器
read()GET/read/:id读取单条记录详情
update()PUT/update/:id更新记录,自动调用验证器
delete()DELETE/delete/:id删除记录(支持软删除)
deleteBatch()DELETE/delete_batch批量删除

2.3 可重写钩子方法(8 个)

钩子方法调用时机用途
beforeIndex($query)列表查询前追加额外查询条件
afterIndex(&$list)列表查询后对查询结果进行二次加工
beforeSave(&$data)新增保存前修改/补充提交数据
afterSave($id)新增保存后新增后的关联操作
beforeUpdate(&$data)更新保存前修改/补充提交数据
afterUpdate($id)更新保存后更新后的关联操作
beforeDelete($id)删除前删除前置检查(如关联检查)
afterDelete($id)删除后删除后的清理操作

2.4 完整示例:ArticleController

<?php
declare(strict_types=1);

namespace app\admin\controller;

use app\admin\model\Article as ArticleModel;
use app\admin\validate\Article as ArticleValidate;
use app\common\traits\CurdTrait;

class Article extends BaseController
{
    use CurdTrait;

    // 绑定模型 - 必填
    protected string $modelClass = ArticleModel::class;
    // 绑定验证器
    protected string $validateClass = ArticleValidate::class;
    // 搜索字段
    protected array $searchFields = ['title', 'author'];
    // 排序
    protected string $sortField = 'create_time';
    protected string $sortOrder = 'desc';

    /**
     * 列表查询前追加条件:仅显示已发布文章
     */
    protected function beforeIndex($query): void
    {
        $query->where('status', 1);
    }

    /**
     * 查询结果后二次加工:追加分类名称
     */
    protected function afterIndex(&$list): void
    {
        foreach ($list as &$item) {
            $item['category_name'] = CategoryModel::getName($item['category_id']);
        }
    }

    /**
     * 删除前检查关联评论
     */
    protected function beforeDelete($id): void
    {
        $commentCount = CommentModel::where('article_id', $id)->count();
        if ($commentCount > 0) {
            throw new \Exception('该文章下有 ' . $commentCount . ' 条评论,请先删除评论');
        }
    }
}

3. BaseController 基类

所有控制器的父类,位于 app\BaseController.php。提供了统一的请求处理、用户信息获取、成功/失败响应等方法。

核心属性

属性类型说明
$requestRequest当前请求对象,通过依赖注入自动绑定
$appApp当前应用实例
$loginUserarray|null当前登录用户信息,中间件注入
$middlewarearray控制器中间件注册数组

核心方法

方法说明
success($msg, $data, $code=200)成功响应,返回标准JSON
error($msg, $code=500, $data=[])失败响应,返回标准JSON
getLoginUserId()获取当前登录用户ID
getLoginUser()获取当前登录用户完整信息
getLoginUserRole()获取当前用户角色标识
isSuperAdmin()判断是否为超级管理员
getPostData($key=null)获取POST提交数据,支持指定字段
param($key, $default=null)获取请求参数(GET/POST通用)

4. BaseModel 模型基类

所有 Model 的父类,位于 app\BaseModel.php。封装了通用的查询作用域、自动时间戳和软删除功能。

核心属性

属性说明
$autoWriteTimestamptrue自动维护 create_time / update_time
$dateFormat'Y-m-d H:i:s'时间格式化格式

查询作用域

Scope 方法用法示例说明
scopeStatus($query, $status)Model::status(1)->select()按状态筛选
scopeKeyword($query, $keyword, $fields)Model::keyword('搜索词', ['title'])->select()多字段模糊搜索
scopeTimeRange($query, $field, $start, $end)Model::timeRange('create_time', '2025-01-01', '2025-12-31')->select()时间范围筛选

完整模型示例:Article

<?php
declare(strict_types=1);

namespace app\admin\model;

use think\model\concern\SoftDelete;

class Article extends \app\BaseModel
{
    // 启用软删除
    use SoftDelete;
    protected string $deleteTime = 'delete_time';

    // 数据表名(不含前缀,自动加 xp_)
    protected string $name = 'article';

    // 自动写入时间戳字段
    protected $autoWriteTimestamp = true;

    /**
     * 关联分类
     */
    public function category()
    {
        return $this->belongsTo(Category::class, 'category_id');
    }

    /**
     * 关联作者
     */
    public function author()
    {
        return $this->belongsTo(User::class, 'author_id');
    }

    /**
     * 获取状态文本
     */
    public function getStatusTextAttr(): string
    {
        $statusMap = [0 => '草稿', 1 => '已发布', 2 => '已下架'];
        return $statusMap[$this->getData('status')] ?? '未知';
    }
}

5. BaseValidate 验证器基类

位于 app\BaseValidate.php,提供统一的验证逻辑封装。支持场景验证和自定义规则。

$defaults 公共规则

所有验证器默认继承以下通用规则:

字段规则
idrequire|integer|>:0
idsrequire|array
pageinteger|>:0
limitinteger|between:1,100
statusin:0,1,2
sort_fieldalphaDash
sort_orderin:asc,desc

完整验证器模板

<?php
declare(strict_types=1);

namespace app\admin\validate;

use app\BaseValidate;

class Article extends BaseValidate
{
    /**
     * 验证规则(业务字段)
     */
    protected $rule = [
        'title'       => 'require|max:200',
        'category_id' => 'require|integer|>:0',
        'content'     => 'require',
        'author'      => 'max:50',
        'sort'        => 'integer|between:0,9999',
    ];

    /**
     * 字段中文名(用于错误提示)
     */
    protected $field = [
        'title'       => '文章标题',
        'category_id' => '所属分类',
        'content'     => '文章内容',
        'author'      => '作者',
        'sort'        => '排序',
    ];

    /**
     * 验证场景定义
     */
    protected $scene = [
        'save'   => ['title', 'category_id', 'content', 'author', 'sort'],
        'update' => ['id', 'title', 'category_id', 'content', 'author', 'sort'],
        'delete' => ['id'],
    ];
}

6. 中间件体系

XPCMS 采用 4 层中间件体系,从应用层到路由层逐级过滤请求。

级别中间件文件位置说明
全局CorsMiddlewareapp/middleware/处理跨域请求,允许前端跨域访问API
应用AuthCheckapp/middleware/验证JWT Token是否有效,注入用户信息
路由PermissionCheckapp/middleware/基于RBAC的角色权限校验
控制器控制器内部注册各Controller控制器特有逻辑拦截(如操作日志)

AuthCheck 认证中间件

拦截所有需要登录的路由,验证请求头 Authorization: Bearer {token} 中的 JWT Token。验证通过后将用户信息注入 $request->loginUser,控制器通过 $this->loginUser 获取。

PermissionCheck 权限中间件

基于 RBAC(基于角色的访问控制) 模型:用户 → 角色 → 权限节点。每个路由定义时指定所需权限节点(如 article/index),中间件校验当前用户角色是否拥有该节点。

RBAC 核心数据表

表名说明
xp_admin_user管理员用户表,关联角色
xp_admin_role角色表
xp_admin_permission权限节点表,树形结构
xp_admin_role_permission角色-权限关联中间表

7. Service 服务层

业务逻辑核心层,位于 app/common/service/。控制器通过依赖注入调用 Service,禁止控制器直接写复杂业务逻辑或 SQL。

核心 Service 列表(17 个)

Service文件核心功能
AdminServiceAdminService.php管理员登录、密码修改、个人资料
ArticleServiceArticleService.php文章CRUD、发布、定时发布
CategoryServiceCategoryService.php分类树管理、排序、缓存
ConfigServiceConfigService.php系统配置读取/写入、站点设置
ThemeServiceThemeService.php主题安装、切换、卸载、配置解析
PluginServicePluginService.php插件安装、启用、禁用、卸载
UpdateServiceUpdateService.php系统更新检测、下载、安装
UploadServiceUploadService.php文件上传、图片压缩、存储驱动
MenuServiceMenuService.php后台菜单管理、权限节点同步
LogServiceLogService.php操作日志记录、查询、清理
CacheServiceCacheService.php缓存管理、清理、预热
DatabaseServiceDatabaseService.php数据库备份、恢复、优化
MessageServiceMessageService.php站内消息发送、已读管理
CommentServiceCommentService.php评论管理、审核、敏感词过滤
MemberServiceMemberService.php会员注册、登录、信息管理
WechatServiceWechatService.php微信公众号对接、消息处理
ApiServiceApiService.phpAPI通用工具方法、签名验证

使用示例

<?php
// 控制器中通过 invoke() 调用 Service
use app\common\service\ArticleService;

class Article extends BaseController
{
    /**
     * 发布文章(含复杂业务逻辑)
     */
    public function publish(): \think\Response
    {
        $articleService = $this->app->make(ArticleService::class);
        $data = $this->request->post();
        $result = $articleService->publishArticle($data);
        return $this->success('发布成功', $result);
    }
}

8. 新模块开发 6 步流程

第 1 步:创建数据表

CREATE TABLE `xp_news` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `title` varchar(200) NOT NULL DEFAULT '' COMMENT '标题',
  `content` text COMMENT '内容',
  `category_id` int(11) DEFAULT '0' COMMENT '分类ID',
  `cover_image` varchar(500) DEFAULT '' COMMENT '封面图',
  `author` varchar(50) DEFAULT '' COMMENT '作者',
  `status` tinyint(1) DEFAULT '1' COMMENT '状态:0草稿 1发布 2下架',
  `sort` int(11) DEFAULT '100' COMMENT '排序',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `delete_time` datetime DEFAULT NULL COMMENT '删除时间',
  PRIMARY KEY (`id`),
  KEY `idx_category` (`category_id`),
  KEY `idx_status` (`status`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='新闻表';

第 2 步:创建 Model

<?php
declare(strict_types=1);

namespace app\admin\model;

use think\model\concern\SoftDelete;

class News extends \app\BaseModel
{
    use SoftDelete;
    protected string $deleteTime = 'delete_time';
    protected string $name = 'news';

    // 状态获取器
    public function getStatusTextAttr(): string
    {
        $map = [0 => '草稿', 1 => '发布', 2 => '下架'];
        return $map[$this->getData('status')] ?? '未知';
    }

    // 分类关联
    public function category()
    {
        return $this->belongsTo(Category::class, 'category_id');
    }
}

第 3 步:创建 Validate

<?php
declare(strict_types=1);

namespace app\admin\validate;

use app\BaseValidate;

class News extends BaseValidate
{
    protected $rule = [
        'title'       => 'require|max:200',
        'category_id' => 'require|integer|>:0',
        'content'     => 'require',
    ];
    protected $field = [
        'title'       => '新闻标题',
        'category_id' => '所属分类',
        'content'     => '新闻内容',
    ];
    protected $scene = [
        'save'   => ['title', 'category_id', 'content'],
        'update' => ['id', 'title', 'category_id', 'content'],
    ];
}

第 4 步:创建 Service

<?php
declare(strict_types=1);

namespace app\admin\service;

use app\admin\model\News as NewsModel;

class NewsService
{
    /**
     * 发布新闻(含额外业务处理)
     */
    public function publish(array $data): int
    {
        // 业务校验:检查标题是否重复
        $exist = NewsModel::where('title', $data['title'])->find();
        if ($exist) {
            throw new \Exception('新闻标题已存在');
        }
        $data['status'] = 1;
        $data['create_time'] = date('Y-m-d H:i:s');
        $news = NewsModel::create($data);
        // 发布后清除分类缓存
        cache('category_tree', null);
        return $news->id;
    }
}

第 5 步:创建 Controller

<?php
declare(strict_types=1);

namespace app\admin\controller;

use app\admin\model\News as NewsModel;
use app\admin\validate\News as NewsValidate;
use app\common\traits\CurdTrait;

class News extends BaseController
{
    use CurdTrait;

    protected string $modelClass = NewsModel::class;
    protected string $validateClass = NewsValidate::class;
    protected array $searchFields = ['title', 'author'];
    protected string $sortField = 'sort';
    protected string $sortOrder = 'asc';
}

第 6 步:注册路由与菜单

① 在 route/admin.php 中注册 RESTful 资源路由:

Route::group('news', function () {
    Route::get('/', 'News/index');          // 列表
    Route::post('/', 'News/save');         // 新增
    Route::get('/:id', 'News/read');       // 详情
    Route::put('/:id', 'News/update');     // 更新
    Route::delete('/:id', 'News/delete');  // 删除
})->middleware([AuthCheck::class, PermissionCheck::class]);

② 在后台菜单管理中新增菜单,关联权限节点为 news/index

9. 插件开发指南

XPCMS 插件系统基于 PluginService(位于 app/common/service/PluginService.php),支持插件的安装、启用、禁用、卸载完整生命周期管理。插件存放于项目根目录 plugins/ 下。

9.1 插件目录结构

一个完整的插件包含以下文件:

plugins/
  └── my_plugin/              ← 插件名(小写蛇形命名)
      ├── plugin.json         ← 插件元数据(必填)
      ├── Install.php         ← 安装脚本(必填,命名空间 plugins\my_plugin\Install)
      ├── Uninstall.php       ← 卸载脚本(可选,命名空间 plugins\my_plugin\Uninstall)
      ├── Bootstrap.php       ← 启动引导(可选,启用时自动加载)
      ├── config.php          ← 配置项(可选,返回数组)
      ├── controller/         ← 控制器目录
      ├── view/               ← 视图模板
      ├── public/             ← 静态资源(将被软链接到 public/static/plugins/my_plugin/)
      └── assets/             ← 资源文件(图片/CSS/JS)

9.2 plugin.json 定义

插件元数据文件,JSON 格式:

{ "title": "示例插件", "version": "1.0.0", "description": "这是一个示例插件,演示插件开发流程", "author": "彭浩", "icon": "fas fa-puzzle-piece" }
字段类型必填说明
titlestring插件显示名称
versionstring版本号,格式 x.y.z
descriptionstring插件功能描述
authorstring作者署名
iconstringFont Awesome 图标 class

9.3 生命周期钩子:Install.php

安装插件时自动执行。命名空间必须为 plugins\{插件名},类名必须为 Install,实现 run() 方法:

<?php declare(strict_types=1); namespace plugins\my_plugin; use think\facade\Db; class Install { /** * 安装插件时执行 * 用于创建数据表、插入默认数据、注册菜单和权限节点 * @return void * @throws \Exception 安装失败时抛出异常 */ public function run(): void { // 1. 创建插件专用数据表 Db::execute('CREATE TABLE IF NOT EXISTS `xp_my_plugin_log` ( `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `content` text COMMENT '日志内容', `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'); // 2. 插入默认配置 Db::name('config')->insert([ 'name' => 'my_plugin_enable', 'title' => '启用插件功能', 'value' => '1', 'type' => 'switch' ]); // 3. 注册后台菜单(可选) // MenuService 会自动处理权限节点同步 } }

9.4 Uninstall.php(卸载脚本)

卸载插件时可选执行,用于清理数据表、删除配置等:

<?php declare(strict_types=1); namespace plugins\my_plugin; use think\facade\Db; class Uninstall { /** * 卸载插件时执行 * 用于删除数据表、清理配置等清理操作 * @return void */ public function run(): void { // 删除插件专用数据表 Db::execute('DROP TABLE IF EXISTS `xp_my_plugin_log`'); // 删除插件配置项 Db::name('config')->where('name', 'like', 'my_plugin_%')->delete(); } }

9.5 Bootstrap.php(启动引导)

插件启用时自动加载,可用于注册路由、事件监听、中间件等初始化操作:

<?php declare(strict_types=1); namespace plugins\my_plugin; use think\facade\Event; /** * 插件启动引导 * 在插件启用时被 PluginService::enablePlugin() 自动加载 */ // 注册事件监听 Event::listen('article_published', function ($article) { // 当文章发布时,记录日志 \think\facade\Db::name('my_plugin_log')->insert([ 'content' => '文章《' . $article['title'] . '》已发布', 'create_time' => date('Y-m-d H:i:s') ]); });

9.6 config.php(插件配置)

返回 PHP 数组,通过 PluginService::getPluginConfig($name) 读取:

<?php return [ 'enable' => true, 'api_key' => '', 'max_items' => 10, 'cache_time' => 3600, 'allowed_roles' => ['super_admin', 'editor'], ];

9.7 插件生命周期完整流程

阶段触发的操作触发的钩子
📦 安装解压 ZIP → 写入 xp_plugin 表(status=0)Install.php::run() + event: plugin_install
✅ 启用更新 status=1加载 Bootstrap.php + event: plugin_enable
⏸️ 禁用更新 status=0event: plugin_disable
🗑️ 卸载先禁用 → 删数据库记录(保留物理文件)Uninstall.php::run() + event: plugin_uninstall
💣 删除卸载 + 递归删除物理目录同卸载

9.8 插件开发最佳实践

  • 插件名使用 小写蛇形命名,如 seo_optimizer
  • Install.php 创建数据库表时使用 CREATE TABLE IF NOT EXISTS 避免重复安装报错
  • Uninstall.php 做好清理工作,不留下冗余数据
  • 静态资源放在 assets/ 下,通过 /static/plugins/{插件名}/ 路径访问
  • 配置文件 key 统一加插件名前缀,避免与系统配置冲突
  • 通过 Event 事件系统与核心系统解耦,监听需要的钩子即可

10. 主题开发指南

XPCMS 主题系统基于 ThemeService(位于 app/common/service/ThemeService.php),支持前台主题、后台主题和双端合并主题三种类型。主题存放于项目根目录 themes/ 下。

10.1 主题目录结构

themes/
  └── frontend/                    ← 前台主题(scope: frontend)
  │   └── default/                 ← 默认主题(不可物理删除)
  │   └── my_theme/                ← 自定义前台主题
  │       ├── theme.json           ← 主题元数据(必填)
  │       ├── config.json          ← 主题配置项(可选)
  │       ├── screenshot.png       ← 主题截图(可选)
  │       ├── view/                ← 视图模板目录
  │       │   ├── common/          ← 公共模板(_header/_footer)
  │       │   ├── index/           ← 首页模块模板
  │       │   ├── article/         ← 文章模块模板
  │       │   └── ...
  │       └── assets/              ← 静态资源(CSS/JS/图片)
  │           ├── css/
  │           ├── js/
  │           └── images/
  └── backend/                     ← 后台主题(scope: backend)
      └── xpui-portal/             ← XPUI 后台主题
          ├── theme.json
          └── view/
              └── admin/           ← 后台模板
                  ├── index/
                  └── ...

10.2 theme.json 主题定义

字段类型必填说明
titlestring主题显示名称
versionstring版本号,格式 x.y.z
descriptionstring主题简介
authorstring作者署名
typestring前端:front / 后端:admin / 双端:both
screenshotstring截图文件名,如 screenshot.png

10.3 type 字段详解

type 值安装目标使用场景
frontthemes/frontend/纯前台主题(企业官网/博客/商城首页)
adminthemes/backend/纯后台主题(后台UI换肤)
both同时安装到前后台目录前后台统一风格的企业级主题

10.4 前台主题开发

⚠ 前台主题的核心是 view/ 目录下的模板文件,必须提供对应模块的模板才能正常渲染。

theme.json 示例

{ "title": "企业门户主题", "version": "1.0.0", "description": "适用于企业官网的前台主题", "author": "彭浩", "type": "front", "screenshot": "screenshot.png" }

模板文件映射

URL路径对应控制器方法模板路径(相对 view/)
/IndexController::index()index/index.html
/article/1ArticleController::detail()article/detail.html
/articleArticleController::index()article/index.html
/productProductController::index()product/index.html
/page/aboutPageController::detail()page/detail.html
/searchSearchController::index()search/index.html

模板文件最小示例(index/index.html)

{include file="common/_header" /} {$xp_current_module = 'index'} <div class="xp-container"> <h1>欢迎访问 {$site_config.site_name|default='我的网站'}</h1> <!-- 文章列表 --> {if !empty($article_list)} <div class="xp-article-list"> {foreach $article_list as $item} <div class="xp-article-item"> <h2><a href="{:url('article/detail', ['id' => $item.id])}">{$item.title}</a></h2> <p>{$item.description|default=''}</p> <span>{$item.create_time}</span> </div> {/foreach} </div> {/if} </div> {include file="common/_footer" /}

公共模板(common/_header.html)关键要素

  • 引入 xp-all.cssxp-all.js
  • 引入主题自定义 CSS(放在 assets/css/ 下)
  • 根据 $xp_current_module 变量自动高亮当前导航项
  • 输出 $site_config 中的 SEO 信息(title/keywords/description)

10.5 后台主题开发

后台主题主要用于定制后台 UI 风格,模板位于 view/admin/ 目录下,对应后台管理界面各个功能模块的视图。

{ "title": "深色后台主题", "version": "1.0.0", "description": "暗色系后台管理界面主题", "author": "彭浩", "type": "admin", "screenshot": "screenshot.png" }

10.6 config.json(主题配置)

主题配置通过 ThemeService::getThemeConfig($name) 读取:

{ "primary_color": "#16a34a", "layout": "wide", "show_banner": true, "home_sections": ["banner", "about", "news", "products"], "footer_text": "Copyright © 2026 小彭CMS", "social_links": { "wechat": "", "weibo": "", "github": "" } }

在模板中使用:{$theme_config.primary_color}(由控制器通过 ThemeService 读取后 assign 到模板变量 $theme_config)。

10.7 主题安装与分发(ZIP 包规范)

步骤操作说明
1. 打包将主题目录打包为 ZIPZIP 根目录直接是主题文件(不要外层文件夹),如 my_theme.zip 内含 theme.json + view/ + assets/
2. 上传后台 → 主题管理 → 安装主题调用 ThemeService::installTheme($zipPath)
3. 解析系统自动读取 theme.json根据 type 字段决定安装到 frontend 或/和 backend 目录
4. 入库写入 xp_theme 表记录名称、版本、作者等信息
5. 启用设为当前主题调用 ThemeService::setCurrentTheme($name),更新 is_current/is_admin 字段

10.8 主题切换机制

XPCMS 通过以下方式动态决定使用哪个主题的模板:

  1. 控制器注入 FrontendConfig Trait,自动调用 ThemeService::getCurrentViewPath() 设置视图路径
  2. getCurrentViewPath() 查询 xp_theme 表中 is_current=1 的记录
  3. 若查询到已启用的非默认主题,则使用该主题的 view/ 路径渲染模板
  4. 若未找到或主题目录不存在,自动回退到 themes/frontend/default/view/

10.9 主题开发最佳实践

  • 始终保留 default 主题作为回退模板,新主题只需覆盖需要的模块
  • 模板中使用 !empty($variable) 检查变量存在性,避免未定义数组键报错
  • 静态资源放在 assets/ 目录下,通过绝对路径引用
  • theme.jsontype 字段务必正确:前台写 front,后台写 admin,双端写 both
  • 所有 class/id 命名必须使用 xp- 前缀 + 短横线命名
  • 前台主题必须提供 view/common/_header.htmlview/common/_footer.html
  • 不要修改 default 主题的原始文件,该主题作为系统回退保障

11. 代码规范 15 项清单

所有 XPCMS 后端代码提交前必须通过以下检查:

序号检查项标准
1文件首行声明declare(strict_types=1);
2文件头部注释模块描述、作者彭浩、开发日期、版权声明
3命名空间符合 PSR-4 规范,与目录路径完全一致
4类名/方法名大驼峰类名、小驼峰方法名,禁止拼音和下划线
5数据库操作仅使用 think\facade\Db,禁止 db() 助手函数
6参数接收Request 依赖注入,禁止 $_GET / $_POST / $_REQUEST
7方法注释完整 PHPDoc:功能、@param、@return、@throws
8业务注释复杂逻辑/算法/模糊变量必须有简体中文注释
9分层调用Controller → Service → Model,禁止跨层调用
10数据表规范xp_ 前缀、小写蛇形命名、id 主键、create_time/update_time
11安全禁止 eval/exec,所有前端输入必须过滤验证
12异常处理使用 try-catch 包裹可能出错的操作,记录日志
13返回值API 统一返回 {code, msg, data} JSON 格式
14软删除敏感数据使用 SoftDelete Trait,不允许物理删除
15依赖注入优先使用容器依赖注入,避免 new 实例化