第十四天:视图和模板【模型和关联】

第十四天:视图和模板【模型和关联】

模板输出

首先来看如何输出一个数据集,我们修改User控制器的index方法如下:

<?php
namespace app\index\controller;
use app\index\model\User as UserModel;
use think\Controller;
class User extends Controller{    // 获取用户数据列表并输出
    public function index()
    {        
    $list = UserModel::all();        
    $this->assign('list', $list);        
    $this->assign('count', count($list));        
    return $this->fetch();
    }
}

这里的User控制器和之前模型章节有所不同,继承了系统的\think\Controller类,该类对视图类的方法进行了封装,所以可以在无需实例化视图类的情况下,直接调用视图类的相关方法,包括:

方法描述
assign模板变量赋值
fetch渲染模板文件
display渲染内容
engine初始化模板引擎

这里用到的其中两个方法assignfetch,也是最常用的两个方法。

assign方法可以把任何类型的变量赋值给模板,关键在于模板中如何输出,不同的变量类型需要采用不同的标签输出。

前面我们已经学习过,fetch方法默认渲染输出的模板文件应该是当前控制器和操作对应的模板,也就是:

application/index/view/user/index.html

接下来,定义视图文件的内容,采用volist标签输出数据集:

<!doctype html><html lang="en"><head><meta charset="UTF-8"><title>查看用户列表</title><style>body{    color: #333;    font: 16px Verdana, "Helvetica Neue", helvetica, Arial, 'Microsoft YaHei', sans-serif;    margin: 0px;    padding: 20px;
}a{    color: #868686;    cursor: pointer;
}a:hover{
    text-decoration: underline;
}h2{    color: #4288ce;    font-weight: 400;    padding: 6px 0;    margin: 6px 0 0;    font-size: 28px;    border-bottom: 1px solid #eee;
}div{margin:8px;
}.info{    padding: 12px 0;    border-bottom: 1px solid #eee;
}.copyright{    margin-top: 24px;    padding: 12px 0;  border-top: 1px solid #eee;
}</style></head><body><h2>用户列表({$count})</h2> {volist name="list" id="user" }<div class="info">ID:{$user.id}<br/>昵称:{$user.nickname}<br/>邮箱:{$user.email}<br/>生日:{$user.birthday}<br/></div>{/volist}<div class="copyright">
    <a title="官方网站" href="http://www.thinkphp.cn">ThinkPHP</a> 
    <span>V5</span> 
    <span>{ 十年磨一剑-为API开发设计的高性能框架 }</span></div></body></html>

ThinkPHP5.0默认使用的是一个内置的编译型模板引擎,包含了一系列的模板标签,我们会陆续介绍一些常用的标签用法。

index方法给模板赋值了两个变量countlist,分别是标量和二维数组,标量的输出很简单,使用: {$count}便可,一看就明白。

二维数组通常使用volist标签输出。

{volist name="list" id="user"}
ID:{$user.id}<br/>
昵称:{$user.nickname}<br/>
邮箱:{$user.email}<br/>
生日:{$user.birthday}<br/>
------------------------<br/>
{/volist}

volist标签的name属性就是模板变量的名称,id属性则是定义每次循环输出的变量,在volist标签中间使用{$user.id}表示输出当前用户的id属性,以此类推下面的内容则依次输出用户的相关属性。

ID:{$user.id}<br/>
昵称:{$user.nickname}<br/>
邮箱:{$user.email}<br/>
生日:{$user.birthday}<br/>

来看下实际的输出效果,访问URL地址:

http://tp5.com/user/index

就可以看到页面输出结果如图:

当然,实际看到的数据可能有所出入。

分页输出

可以很简单的输出用户的分页数据,控制器index方法修改为:

// 获取用户数据列表
public function index(){    // 分页输出列表 每页显示3条数据
    $list = UserModel::paginate(3);    
    $this->assign('list',$list);    
    return $this->fetch();
}

模板文件修改为:

<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css" />
<h2>用户列表({$list->total()})</h2> 
{volist name="list" id="user"}
ID:{$user.id}<br/>
昵称:{$user.nickname}<br/>
邮箱:{$user.email}<br/>
生日:{$user.birthday}<br/>
------------------------<br/>
{/volist}
{$list->render()}

注意,由于演示需要,在模板中引入了bootstrap样式文件。请按照所示位置存放。

多插入一些数据后,访问下面的地址:

http://tp5.com/user/index

后可以看到输出如图所示:

公共模板

加上之前定义的创建用户的模板,现在已经有两个模板文件了,为了避免重复定义模板,可以把模板的公共头部和尾部分离出来,便于维护。

我们把模板文件拆分为三部分:

application/index/view/user/header.html
application/index/view/user/index.html
application/index/view/user/footer.html

header.html内容为:

<!doctype html><html lang="en"><head><meta charset="UTF-8"><title>查看用户列表</title><style>body{    color: #333;    font: 16px Verdana, "Helvetica Neue", helvetica, Arial, 'Microsoft YaHei', sans-serif;    margin: 0px;    padding: 20px;
}a{    color: #868686;    cursor: pointer;
}a:hover{
    text-decoration: underline;
}h2{    color: #4288ce;    font-weight: 400;    padding: 6px 0;    margin: 6px 0 0;    font-size: 28px;    border-bottom: 1px solid #eee;
}div{margin:8px;
}.info{    padding: 12px 0;    border-bottom: 1px solid #eee;
}.copyright{    margin-top: 24px;    padding: 12px 0;  border-top: 1px solid #eee;
}</style></head><body>

footer.html内容为:

<div class="copyright">
    <a title="官方网站" href="http://www.thinkphp.cn">ThinkPHP</a> 
    <span>V5</span> 
    <span>{ 十年磨一剑-为API开发设计的高性能框架 }</span></div></body></html>

index.html内容为:

{include file="user/header" /}
<h2>用户列表({$count})</h2> 
{volist name="list" id="user" }
<div class="info">
ID:{$user.id}<br/>
昵称:{$user.nickname}<br/>
邮箱:{$user.email}<br/>
生日:{$user.birthday}<br/>
</div>
{/volist}
{include file="user/footer" /}

公共头部模板文件中可能存在一些变量,例如这里的页面标题不同的页面会有不同,可以使用

{include file="user/header" title="查看用户列表" /}

然后把头部模板文件中的

<title>查看用户列表</title>

改为:

<title>[title]</title>

再次访问页面可以看到输出结果:

如果需要传递多个变量,则使用多个属性即可,例如:

{include file="user/header" title="查看用户列表" keywords="thinkphp" /}

也可以支持传递动态变量的方式,例如:

{include file="user/header" title="$title" /}

模板定位

fetch方法的第一个参数表示渲染的模板文件或者模板表达式。

通常我们都是使用的模板表达式,而不需要使用完整的文件名。

模板文件名可以随意命名,如果把index.html文件改成:

application/index/view/user/list.html

index操作方法中的fetch方法需要改成:

return $this->fetch('list');

而如果fetch方法改成:

return $this->fetch('index/list');

那么实际渲染的模板文件则是

application/index/view/index/list.html

当然,你可以设置更多的目录级别,例如:

return $this->fetch('one/two/three/list');

那么实际渲染的模板文件则是

application/index/view/one/two/three/list.html

有一些和模板定位相关的设置参数能够帮助你调整模板文件的位置和名称。
通常来说,模板相关的参数可以直接在配置文件中配置template参数,默认的配置如下:

'template'              => [    // 模板引擎类型 支持 php think 支持扩展
    'type'         => 'Think',    // 模板路径
    'view_path'    => '',    // 模板后缀
    'view_suffix'  => '.html',    // 模板文件名分隔符
    'view_depr'    => DS,    // 模板引擎普通标签开始标记
    'tpl_begin'    => '{',    // 模板引擎普通标签结束标记
    'tpl_end'      => '}',    // 标签库标签开始标记
    'taglib_begin' => '{',    // 标签库标签结束标记
    'taglib_end'   => '}',
],

view_path参数决定了你的模板文件的根目录,如果没有设置的话系统会默认使用当前模块的视图目录view

如果希望自定义模板文件的位置、命名和后缀,可以对模板参数稍加修改如下:

'template'              => [    // 模板引擎类型 支持 php think 支持扩展
    'type'         => 'Think',    // 模板路径
    'view_path'    => '../template/index/',    // 模板后缀
    'view_suffix'  => '.tpl',    // 模板文件名分隔符
    'view_depr'    => '_',
],

通过配置我们把当前渲染的模板文件移动到了

ROOT_PATH/template/index/user_index.tpl

模板布局

现在使用模板布局来进一步简化模板定义。

首先需要定义一个布局模板文件,放到 application/index/view/layout.html内容如下:

{include file="user/header" /}
 {__CONTENT__}
{include file="user/footer" /}

application/index/view/user/index.html改成:

{layout name="layout" /}
<h2>用户列表({$count})</h2> 
{volist name="list" id="user" }
<div class="info">
ID:{$user.id}<br/>
昵称:{$user.nickname}<br/>
邮箱:{$user.email}<br/>
生日:{$user.birthday}<br/>
</div>
{/volist}

在index模板文件中开头定义layout标签 ,表示当前模板使用了布局,布局模板文件为layout.html,布局模板中的 {__CONTENT__}会自动替换为解析后的index.html内容。

如果你的布局模板中不是使用{__CONTENT__}的话,可以改成:

{layout name="layout/newlayout" replace="[__REPLACE__]" /}

那么回自动读取模板文件
application/index/view/layout/newlayout.html,内容如下:

{include file="user/header" /}
[__REPLACE__]
{include file="user/footer" /}

如果你所有的模板文件都统一使用布局,并且都是有同一个布局模板,那么可以统一配置而不需要在模板文件中使用layout标签定义。

在应用配置或者模块配置中添加如下设置参数:

'template'  =>  [    'layout_on'     =>  true,    'layout_name'   =>  'layout',    'layout_item'   =>  '[__REPLACE__]']

模板文件中只需要定义如下:

<h2>用户列表({$count})</h2> 
{volist name="list" id="user" mod="2" }
<div class="info">
ID:{$user.id}<br/>
昵称:{$user.nickname}<br/>
邮箱:{$user.email}<br/>
生日:{$user.birthday}<br/>
</div>
{/volist}

访问

http://tp5.com/user/index

可以看到页面输出结果和之前一样:

如果想动态控制模板文件使用布局,则可以在控制器中使用:

// 获取用户数据列表
public function index(){    
$list = UserModel::all();    
$this->assign('list', $list);    
$this->assign('count', count($list));    // 动态使用布局
    $this->view->engine->layout('layout','[__REPLACE__]');    
    return $this->fetch();
}

注意:这里调用的是this->view->engine对象的layout方法,并不是所有的模板引擎都支持布局功能,如果你使用的是其它的模板引擎,可能不提供layout方法。

如果使用配置方式开启了布局模板,也可以使用该方法临时关闭布局,例如:

// 获取用户数据列表
public function index(){    
$list = UserModel::all();    
$this->assign('list', $list);    
$this->assign('count', count($list));    // 临时关闭布局
    $this->view->engine->layout(false);    
    return $this->fetch();
}

或者直接在模板文件的开头加上{__NOLAYOUT__}标签:

{__NOLAYOUT__}
<h2>用户列表({$count})</h2> 
{volist name="list" id="user" mod="2" }
<div class="info">
ID:{$user.id}<br/>
昵称:{$user.nickname}<br/>
邮箱:{$user.email}<br/>
生日:{$user.birthday}<br/>
</div>
{/volist}

标签定制

可以设置模板标签的定界符:

'template'              => [    // 模板引擎类型 支持 php think 支持扩展
    'type'         => 'Think',    // 模板路径
    'view_path'    => '../template/index/',    // 模板后缀
    'view_suffix'  => '.tpl',    // 模板文件名分隔符
    'view_depr'    => '_',    // 模板引擎普通标签开始标记
    'tpl_begin'    => '{',    // 模板引擎普通标签结束标记
    'tpl_end'      => '}',    // 标签库标签开始标记
    'taglib_begin' => '<',    // 标签库标签结束标记
    'taglib_end'   => '>',
],

并且修改index.html模板中的标签修改如下:

<h2>用户列表({$count})</h2> <div class="info"><volist name="list" id="user" >ID:{$user.id}<br/>昵称:{$user.nickname}<br/>邮箱:{$user.email}<br/>生日:{$user.birthday}<br/></volist></div>

输出替换

为了更加清晰,需要把资源文件独立出来,并在模板文件中引入,例如增加public/static/common.css文件:

body{    color: #333;    font: 16px Verdana, "Helvetica Neue", helvetica, Arial, 'Microsoft YaHei', sans-serif;    margin: 0px;    padding: 20px;
}a{    color: #868686;    cursor: pointer;
}a:hover{
    text-decoration: underline;
}h2{    color: #4288ce;    font-weight: 400;    padding: 6px 0;    margin: 6px 0 0;    font-size: 28px;    border-bottom: 1px solid #eee;
}div{margin:8px;
}.info{    padding: 12px 0;    border-bottom: 1px solid #eee;
}.copyright{    margin-top: 24px;    padding: 12px 0;  border-top: 1px solid #eee;
}

我们在header.html文件中引入资源文件

<html><head><meta charset="UTF-8"><title>[title]</title><link charset="utf-8" rel="stylesheet" href="/static/common.css"></head><body>

但这样有一个问题,如果部署的目录变化的话,资源文件的路径就会跟着变化,这里我们采用输出替换功能,使得资源文件的引入动态化
可以在输出之前对解析后的内容进行替换,使用:

// 读取用户数据
public function read($id=''){    
$user = UserModel::get($id);    
$this->assign('user',$user);    
$this->view->replace([        
'__PUBLIC__'    =>  '/static',
    ]);    
    return $this->fetch();
}

模板文件改为:

<html><head><meta charset="UTF-8"><title>[title]</title><link charset="utf-8" rel="stylesheet" href="__PUBLIC__/common.css"></head><body>

最终输出的时候,会自动进行__PUBLIC__替换。

渲染内容

有时候,并不需要模板文件,而是直接渲染内容或者读取数据库中存储的内容,控制器方法修改如下:

<?php
namespace app\index\controller;
use app\index\model\User as UserModel;
use think\Controller;
class User extends Controller{    // 获取用户数据列表并输出
    public function index()
    {        
    $list = UserModel::all();        
    $this->assign('list', $list);        
    $this->assign('count', count($list));        // 关闭布局 
        $this->view->engine->layout(false);        
        $content = <<<EOT
<style>            
body{
    color: #333;
    font: 16px Verdana, "Helvetica Neue", helvetica, Arial, 'Microsoft YaHei', sans-serif;
    margin: 0px;
    padding: 20px;
}

a{
    color: #868686;
    cursor: pointer;
}
a:hover{
    text-decoration: underline;
}
h2{
    color: #4288ce;
    font-weight: 400;
    padding: 6px 0;
    margin: 6px 0 0;
    font-size: 28px;
    border-bottom: 1px solid #eee;
}
div{
margin:8px;
}
.info{
    padding: 12px 0;
    border-bottom: 1px solid #eee;
}

.copyright{
    margin-top: 24px;
    padding: 12px 0;
  border-top: 1px solid #eee;
}
</style>
<h2>用户列表({\$count})</h2>
<div>
{volist name="list" id="user"  }
ID:{\$user.id}<br/>
昵称:{\$user.nickname}<br/>
邮箱:{\$user.email}<br/>
生日:{\$user.birthday}<br/>
------------------------<br/>
{/volist}
</div>
<div class="copyright">
    <a title="官方网站" href="http://www.thinkphp.cn">ThinkPHP</a> 
    <span>V5</span> 
    <span>{ 十年磨一剑-为API开发设计的高性能框架 }</span>
</div>

EOT;
        return $this->display($content);
    }
}

display方法用于渲染内容而不是模板文件输出,和直接使用echo输出的区别是display方法输出的内容支持模板标签的解析。

助手函数

可以使用系统提供的助手函数view简化模板渲染输出(注意不适用于内容渲染输出):

前面的模板渲染代码可以改为:

<?php
namespace app\index\controller;
use app\index\model\User as UserModel;
class User{    // 读取用户数据
    public function read($id='')
    {        
    $user = UserModel::get($id);        
    return view('', ['user' => $user], ['__PUBLIC__'    => '/static']);
    }
}

使用view助手函数,不需要继承think\Controller类。该方法的第一个参数就是渲染的模板表达式。



回复列表



回复操作

正在加载验证码......

请先拖动验证码到相应位置