thinkphp仿织梦

99ANYc3cd6
预计阅读时长 41 分钟
位置: 首页 织梦建站 正文

织梦的核心魅力在于其“标签化”的模板调用和“模型/频道管理思想,我们将围绕这两个核心点,来设计如何用 ThinkPHP 实现一个“类织梦”的系统。


核心思想对比

特性 织梦 ThinkPHP 实现方案
核心理念 标签驱动,模板分离 MVC (Model-View-Controller),但可以封装成标签化
模板调用 {dede:arclist} 等专用标签 自定义模板标签引擎,封装 ThinkPHP 的查询构建器
后台逻辑 PHP 文件直接处理请求 控制器 + 路由,逻辑更清晰
扩展性 修改核心文件或使用插件 基于 composer 的依赖和规范的扩展开发

第一步:项目架构与数据库设计

ThinkPHP 项目初始化

使用 Composer 创建一个 ThinkPHP 项目:

composer create-project topthink/think tp_dedemv

进入项目目录 tp_dedemv

数据库设计 (仿织梦的核心)

织梦的数据库设计非常经典,我们参考它来设计。

核心表:

  • dede_arctype (栏目表)

    • id: 主键
    • typename: 栏目名称
    • typedir: 栏目目录 (用于生成URL)
    • reid: 上级栏目ID (用于树形结构)
    • isdefault: 是否是默认栏目 (首页)
    • ishidden: 是否隐藏
    • content: 栏目描述
    • model: 关联的内容模型ID (比如1代表文章,2代表图集)
    • addtime: 创建时间
  • dede_archives (文档主表)

    • id: 主键
    • typeid: 所属栏目ID (关联 arctype)
    • title: 文章标题
    • litpic: 缩略图
    • pubdate: 发布时间
    • click: 点击量
    • iscommend: 是否推荐
    • arcrank: 审核状态 (0为已审核)
    • writer: 作者
    • source: 来源
  • dede_addonarticle (文章附加表)

    • aid: 关联 archives 表的 id
    • body: 文章正文 (使用 TEXT 或 LONGTEXT 类型)
  • dede_arcmulti (多图/软件等附加表示例)

    • aid: 关联 archives 表的 id
    • body: 内容字段 (可以是多图信息、软件信息等)

设计思路:

  • archives 表是所有内容类型的“主表”,存放公共字段。
  • 通过 model 字段属于哪个模型(文章、图集、下载等)。
  • 每个模型对应一个“附加表”,存放该模型特有的字段(如文章的 body)。
  • 这种设计使得系统可以灵活地增加新的内容类型,而无需修改核心表结构。

第二步:后台管理系统实现

录入和管理的地方,遵循标准的 MVC 模式。

模型层

创建对应的模型来操作数据库。

  • app/model/Arctype.php (栏目模型)

    namespace app\model;
    use think\Model;
    class Arctype extends Model
    {
        // 设置数据表名
        protected $name = 'arctype';
        // 设置字段信息
        protected $schema = [
            'id'      => 'int',
            'typename'=> 'string',
            // ... 其他字段
        ];
    }
  • app/model/Archives.php (文档主表模型)

    namespace app\model;
    use think\Model;
    class Archives extends Model
    {
        protected $name = 'archives';
        // 定义关联,一个文档属于一个栏目
        public function arctype()
        {
            return $this->belongsTo(Arctype::class, 'typeid');
        }
    }

控制器层

创建控制器来处理后台的请求逻辑。

  • app/controller/admin/Arctype.php (栏目管理控制器)

    namespace app\controller\admin;
    use app\BaseController;
    use app\model\Arctype;
    use think\facade\View;
    class Arctype extends BaseController
    {
        public function index()
        {
            $list = Arctype::order('id', 'asc')->select();
            View::assign('list', $list);
            return View::fetch();
        }
        public function add()
        {
            if ($this->request->isPost()) {
                $data = $this->request->post();
                if (Arctype::create($data)) {
                    return json(['code' => 1, 'msg' => '添加成功']);
                }
                return json(['code' => 0, 'msg' => '添加失败']);
            }
            // 获取所有栏目作为上级选项
            $types = Arctype::where('reid', 0)->select();
            View::assign('types', $types);
            return View::fetch();
        }
        // ... edit, del 等方法
    }

视图层

后台视图使用 ThinkPHP 的模板引擎,可以和 Bootstrap、Layui 等前端框架结合,快速搭建美观的界面。

  • app/view/admin/arctype/index.html
    <table class="table table-striped">
        <thead>
            <tr>
                <th>ID</th>
                <th>栏目名称</th>
                <th>目录</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            {volist name="list" id="vo"}
            <tr>
                <td>{$vo.id}</td>
                <td>{$vo.typename}</td>
                <td>{$vo.typedir}</td>
                <td>
                    <a href="{:url('add', ['reid' => $vo.id])}" class="btn btn-xs btn-primary">添加子栏目</a>
                    <a href="{:url('edit', ['id' => $vo.id])}" class="btn btn-xs btn-info">编辑</a>
                    <a href="{:url('del', ['id' => $vo.id])}" class="btn btn-xs btn-danger" onclick="return confirm('确定删除吗?')">删除</a>
                </td>
            </tr>
            {/volist}
        </tbody>
    </table>

第三步:前台实现与标签化封装 (核心难点)

前台要实现织梦那样的 {dede:xxx} 标签调用,我们需要在 ThinkPHP 中“模拟”这个行为。

创建自定义标签引擎

我们可以创建一个服务类来解析和执行这些“标签”。

  • app/service/TagService.php

    namespace app\service;
    use think\facade\Db;
    use think\facade\View;
    class TagService
    {
        /**
         * 解析并执行标签
         * @param string $tagName 标签名,如 'arclist'
         * @param array $attrs 标签属性,如 ['row' => '10', 'typeid' => '1']
         * @param string $content 标签内容 (用于 {dede:loop} 等)
         * @return string
         */
        public function parse(string $tagName, array $attrs, string $content = ''): string
        {
            switch ($tagName) {
                case 'arclist':
                    return $this->arclist($attrs);
                case 'channel':
                    return $this->channel($attrs);
                case 'field':
                    return $this->field($attrs);
                // ... 更多标签
                default:
                    return ''; // 未知标签返回空
            }
        }
        /**
         * 实现 {dede:arclist} 标签
         */
        protected function arclist(array $attrs): string
        {
            $query = Db::name('archives')
                ->alias('a')
                ->join('arctype t', 'a.typeid = t.id')
                ->where('a.arcrank', 0); // 只调用已审核的文章
            if (isset($attrs['typeid'])) {
                $query->where('a.typeid', $attrs['typeid']);
            }
            if (isset($attrs['row'])) {
                $query->limit($attrs['row']);
            }
            if (isset($attrs['limit'])) {
                $limit = explode(',', $attrs['limit']);
                $query->limit($limit[0], $limit[1] ?? 0);
            }
            // ... 更多属性处理
            $list = $query->order('a.id', 'desc')->select()->toArray();
            // 模拟织梦的字段命名
            foreach ($list as &$item) {
                $item['title'] = $item['title'];
                $item['litpic'] = $item['litpic'];
                $item['description'] = mb_substr(strip_tags($this->getBodyByAid($item['id'])), 0, 100) . '...';
                $item['arcurl'] = $this->getArcUrl($item['id']);
            }
            // 将数据传递给模板
            View::assign('list', $list);
            // 返回渲染后的HTML片段
            return View::fetch('public/arclist_item');
        }
        // ... channel, field 等标签的实现
        /**
         * 根据文档ID获取正文 (从附加表)
         */
        protected function getBodyByAid(int $aid): string
        {
            // 这里简化处理,实际应根据 model 字段查询不同附加表
            return Db::name('addonarticle')->where('aid', $aid)->value('body') ?? '';
        }
        /**
         * 生成文档URL (伪静态)
         */
        protected function getArcUrl(int $aid): string
        {
            // 实际项目中应从缓存或数据库获取栏目信息来构建URL
            // 这里简化为 /view/{id}.html
            return "/view/{$aid}.html";
        }
    }

在模板中使用标签

我们需要一个机制,让模板引擎在编译时能识别 {dede:xxx} 并调用我们的 TagService

ThinkPHP 的模板引擎支持自定义标签库,我们可以注册一个自定义的标签库。

  • app.php 或应用配置中注册标签库:

    // config/app.php
    'taglib'        => [
        'app\taglib\Dede' => '', // 注册我们的标签库
    ],
  • 创建标签库类:

    • app/taglib/Dede.php

      namespace app\taglib;
      use app\service\TagService;
      use think\template\TagLib;
      class Dede extends TagLib
      {
          // 标签定义
          protected $tags = [
              // 标签名 => 属性列表
              'arclist' => ['attr' => 'typeid,row,limit', 'close' => 0], // close=0 表示是单标签
              'channel' => ['attr' => 'typeid,row', 'close' => 0],
              'field'   => ['attr' => 'name', 'close' => 0],
          ];
          public function tagArclist($tag, $content)
          {
              $attrs = $tag['attr'];
              // 调用服务层
              $service = new TagService();
              return $service->parse('arclist', $attrs);
          }
          public function tagChannel($tag, $content)
          {
              $attrs = $tag['attr'];
              $service = new TagService();
              return $service->parse('channel', $attrs);
          }
          public function tagField($tag, $content)
          {
              // field 标签通常用于循环内部,获取当前循环项的某个字段
              // 实现起来稍微复杂,需要配合循环标签
              // 这里简化处理
              $name = $tag['attr']['name'];
              // 假设我们有一个变量在循环中,如 $vo
              return "<?= isset(\$vo['{$name}']) ? \$vo['{$name}'] : ''; ?>";
          }
      }
  • 前台模板文件 index.html

    <!DOCTYPE html>
    <html>
    <head>
        <title>{dede:field name='typename'/} - {:$site_name}</title>
    </head>
    <body>
        <h1>{dede:field name='typename'/}</h1>
        <h2>最新文章列表</h2>
        {dede:arclist row='5' typeid='1'}
            <li>
                <a href="[field name='arcurl'/]">[field name='title'/]</a>
                <p>[field name='description'/]</p>
            </li>
        {/dede:arclist}
        <h2>栏目列表</h2>
        {dede:channel row='8'}
            <li><a href="[field name='typedir'/]/">[field name='typename'/]</a></li>
        {/dede:channel}
    </body>
    </html>

展示

  • 配置路由 route/app.php

    use think\facade\Route;
    // 首页
    Route::get('/', 'Index/index');
    // 文章详情页,使用路由变量绑定模型
    Route::get('view/:id', 'Index/detail')->pattern(['id' => '\d+']);
  • 创建前台控制器 app/controller/Index.php

    namespace app\controller;
    use app\BaseController;
    use app\model\Archives;
    use app\model\Arctype;
    use think\facade\View;
    class Index extends BaseController
    {
        public function index()
        {
            // 获取当前栏目ID,可以从路由或参数中获取
            $typeid = $this->request->param('typeid', 1);
            // 获取栏目信息
            $type = Arctype::find($typeid);
            View::assign('typename', $type->typename);
            // 模板渲染和标签解析由框架自动完成
            return View::fetch();
        }
        public function detail($id)
        {
            $archives = Archives::with(['arctype'])->find($id);
            if (!$archives || $archives->arcrank != 0) {
                // 文章不存在或未审核
                abort(404, '文章不存在或未通过审核');
            }
            View::assign('archives', $archives);
            return View::fetch('detail');
        }
    }

总结与进阶

通过以上步骤,我们就用 ThinkPHP 搭建了一个具有“织梦风格”的 CMS 框架。

优点:

  1. 逻辑清晰:后台遵循 MVC,代码易于维护和扩展。
  2. 安全稳定:ThinkPHP 提供了安全防护和稳定的底层。
  3. 灵活强大:可以轻松利用 ThinkPHP 的生态(如验证器、中间件、事件等)。
  4. 标签化友好:通过自定义标签库,完美复刻了织梦的模板调用方式,对前端和内容编辑人员友好。

进阶方向:

  1. 模型管理后台:仿照织梦的“模型管理”,做一个可视化的后台,让用户可以动态创建新的内容模型(如“产品”、“招聘”等),并自动生成数据表。
  2. 富文本编辑器集成:集成如 UEditor、TinyMCE 等富文本编辑器,实现图文混排。
  3. 缓存机制:对栏目列表、首页内容等使用缓存,大幅提升访问速度。
  4. 伪静态与路由:完善 URL 生成规则,实现更灵活的伪静态。
  5. 会员系统:开发会员中心、评论、收藏等互动功能。
  6. 插件/钩子系统:开发一个插件机制,方便扩展功能。

这个项目是一个很好的练手和深入理解 ThinkPHP 全家桶的机会,祝你开发顺利!

-- 展开阅读全文 --
头像
dede搜索框模板文件在哪
« 上一篇 02-12
dede文章点击数据存哪表?字段名是click还是count?
下一篇 » 02-12

相关文章

取消
微信二维码
支付宝二维码

目录[+]