在 ThinkPHP 中,默认情况下,一个模块(Module)的控制器(Controller)只能渲染其自身模块下的 view 目录中的模板文件。Index 模块的控制器会去 view/index/ 目录下找模板。

thinkphp 跨模块模板
(图片来源网络,侵删)

但在实际开发中,我们经常需要在一个模块中调用另一个模块的模板,

  • 在后台管理模块中,调用前台用户模块的某个列表页模板。
  • 创建一个公共的布局模板,供所有模块共享。

ThinkPHP 提供了非常灵活和强大的模板调用机制,主要有以下几种方法,我会从最推荐到最灵活的顺序进行介绍。


使用 extend 标签 (推荐,用于布局和继承)

这是最常用、最符合 ThinkPHP 设计思想的方法,主要用于实现模板的布局和继承,它可以在当前模板中引入并继承另一个模板的结构。

适用场景

thinkphp 跨模块模板
(图片来源网络,侵删)
  • 创建一个全局的 layout.html 作为基础布局。
  • 让各个模块的页面继承这个基础布局,只需填充内容块。

操作步骤

  1. 创建公共布局模板 假设我们创建一个公共布局模板,放在 view/public/layout.html

    <!-- view/public/layout.html -->
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>{block name="title"}默认标题{/block}</title>
        <link rel="stylesheet" href="/static/css/common.css">
    </head>
    <body>
        <header>网站公共头部</header>
        <main>
            {block name="content"}这里是默认内容{/block}
        </main>
        <footer>网站公共底部</footer>
    </body>
    </html>
  2. 在子模块模板中继承布局 我们希望在 index 模块的 index 控制器所对应的模板 view/index/index.html 中使用这个公共布局。

    <!-- view/index/index.html -->
    {extend name="public/layout" /}
    {block name="title"}首页 - 我的应用{/block}
    {block name="content"}
    <h1>欢迎来到首页!</h1>
    <p>这是首页的内容。</p>
    {/block}

说明

thinkphp 跨模块模板
(图片来源网络,侵删)
  • {extend name="public/layout" /}: 这行代码告诉当前模板要继承 public/layout.html
  • ThinkPHP 会自动在 view 目录下查找 public/layout.html
  • {block}...{/block}: 这些标签会覆盖掉父模板(layout.html)中同名的 {block}

跨模块使用: 如果想继承 admin 模块下的布局模板 view/admin/layout.html,只需修改路径即可:

{extend name="admin/layout" /}

ThinkPHP 会自动在 view/admin/ 目录下查找。


使用 include 标签 (简单,用于引入片段)

include 标签用于在当前位置直接引入并解析另一个模板文件的内容,类似于 PHP 的 include,它不涉及继承,只是简单地把一个模板文件“粘贴”过来。

适用场景

  • 引入一个通用的头部、底部、侧边栏等。
  • 引入一个在其他模块中定义的某个小部件模板。

操作步骤

假设 admin 模块需要一个侧边栏,而这个侧边栏的模板定义在 index 模块里。

  1. 创建侧边栏模板 view/index/sidebar.html:

    <!-- view/index/sidebar.html -->
    <div class="sidebar">
        <h3>用户菜单</h3>
        <ul>
            <li><a href="#">个人中心</a></li>
            <li><a href="#">我的订单</a></li>
            <li><a href="#">账户设置</a></li>
        </ul>
    </div>
  2. 在目标模板中引入admin 模块的某个页面 view/admin/dashboard.html 中引入它:

    <!-- view/admin/dashboard.html -->
    <div class="dashboard">
        <div class="main-content">
            <h1>管理面板</h1>
            <p>欢迎管理员!</p>
        </div>
        <div class="aside">
            {include file="index/sidebar" /}
        </div>
    </div>

说明

  • {include file="index/sidebar" /}: 这行代码会引入 view/index/sidebar.html 文件。
  • 路径是相对于当前模块的 view 目录的,如果你想引入 public 模块的模板,可以写成 {include file="public/header" /}

使用 template 属性 (最灵活,完全自定义路径)

如果你不想依赖 ThinkPHP 的默认 view 目录结构,或者需要动态地指定一个完全不同路径的模板,可以在控制器中使用 template 属性来指定模板文件的完整路径。

适用场景

  • 模板文件存放在项目根目录的 templates 文件夹下。
  • 根据用户权限或某些条件,动态加载不同模块、不同位置的模板。
  • 第三方库或插件自带了模板文件,需要直接调用。

操作步骤

假设我们有一个公共模板文件,放在 app/templates/common/about_us.html

  1. 在控制器中指定模板路径

    <?php
    namespace app\index\controller;
    use think\Controller;
    class Index extends Controller
    {
        public function about()
        {
            // 使用 template 属性指定模板的完整路径
            // 注意:这里的路径是相对于应用(app)根目录的
            $this->template = '../templates/common/about_us';
            // 也可以使用助手函数,效果一样
            // return view('../templates/common/about_us');
            // 你仍然可以传递数据给模板
            $this->assign('site_name', '我的ThinkPHP应用');
            return $this->fetch();
        }
    }

说明

  • $this->template = '../templates/common/about_us';:这里指定的路径是相对于当前模块的视图目录(view)的。Index 模块的 view 目录是 app/view/index/, 会回到 app/ 目录,然后找到 templates/common/about_us.html
  • 推荐使用助手函数 view():在现代 ThinkPHP 版本中,直接在控制器方法中返回 view() 是更简洁的方式。
    public function about()
    {
        return view('../templates/common/about_us', ['site_name' => '我的ThinkPHP应用']);
    }
  • 绝对路径:如果你觉得相对路径容易混淆,可以使用绝对路径(从 public 目录开始)。
    // 从 public 目录开始
    return view('../../templates/common/about_us');

    或者使用 APP_PATH 常量来构建绝对路径:

    $templatePath = APP_PATH . 'templates/common/about_us';
    return view($templatePath);

总结与对比

方法 标签/函数 适用场景 优点 缺点
继承布局 {extend name="..." /} 创建公共布局、页面框架 结构清晰,代码复用率高,符合MVC思想 不适合引入独立的、不依赖结构的模板片段
引入片段 {include file="..." /} 引入头部、底部、小部件等 简单直接,灵活 每次引入都是完整解析,不适合大段重复代码
自定义路径 $this->template / view() 路径不固定、动态加载、第三方模板 最灵活,可以指定任何位置的模板 破坏了模块化的视图组织,路径管理稍显复杂

最佳实践建议

  1. 优先使用 {extend}:对于网站的公共布局(如 header, footer, main 结构),使用 {extend}{block} 是最优雅、最强大的方式。
  2. 次选 {include}:对于一些可复用的、不依赖父模板结构的独立模板片段(如一个商品卡片、一个评论框),使用 {include}
  3. 慎用自定义路径:仅在特殊需求(如模板文件在非标准目录、动态加载)下使用 $this->templateview() 的完整路径功能,因为它会让视图的查找变得不那么直观。

希望这个详细的解释能帮助你完全掌握 ThinkPHP 的跨模块模板调用!