# 视图模型
[上一页](# "上一页")[下一页](# "下一页")
### 视图定义
视图通常是指数据库的视图,视图是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。但是,视图并不在数据库中以存储的数据值集形式存在。行和列数据来自由定义视图的查询所引用的表,并且在引用视图时动态生成。对其中所引用的基础表来说,视图的作用类似于筛选。定义视图的筛选可以来自当前或其它数据库的一个或多个表,或者其它视图。分布式查询也可用于定义使用多个异类源数据的视图。如果有几台不同的服务器分别存储组织中不同地区的数据,而您需要将这些服务器上相似结构的数据组合起来,这种方式就很有用。视图在有些数据库下面并不被支持,但是ThinkPHP模拟实现了数据库的视图,该功能可以用于多表联合查询。非常适合解决HAS_ONE 和 BELONGS_TO 类型的关联查询。
要定义视图模型,只需要继承Think\Model\ViewModel,然后设置viewFields属性即可。
例如下面的例子,我们定义了一个BlogView模型对象,其中包括了Blog模型的id、name、title和User模型的name,以及Category模型的title字段,我们通过创建BlogView模型来快速读取一个包含了User名称和类别名称的Blog记录(集)。
~~~
namespace Home\Model;
use Think\Model\ViewModel;
class BlogViewModel extends ViewModel {
public $viewFields = array(
'Blog'=>array('id','name','title'),
'Category'=>array('title'=>'category_name', '_on'=>'Blog.category_id=Category.id'),
'User'=>array('name'=>'username', '_on'=>'Blog.user_id=User.id'),
);
}
~~~
我们来解释一下定义的格式代表了什么。
`$viewFields` 属性表示视图模型包含的字段,每个元素定义了某个数据表或者模型的字段。
例如:
~~~
'Blog'=>array('id','name','title');
~~~
表示BlogView视图模型要包含Blog模型中的id、name和title字段属性,这个其实很容易理解,就和数据库的视图要包含某个数据表的字段一样。而Blog相当于是给Blog模型对应的数据表定义了一个别名。
默认情况下会根据定义的名称自动获取表名,如果希望指定数据表,可以使用:
~~~
'_table'=>"test_user"
// 3.2.2版本以上还可以使用简化定义(自动获取表前缀)
'_table'=>"__USER__"
~~~
如果希望给当前数据表定义另外的别名,可以使用
~~~
'_as'=>'myBlog'
~~~
BlogView视图模式除了包含Blog模型之外,还包含了Category和User模型,下面的定义:
~~~
'Category'=>array('title'=>'category_name');
~~~
和上面类似,表示BlogView视图模型还要包含Category模型的title字段,因为视图模型里面已经存在了一个title字段,所以我们通过
~~~
'title'=>'category_name'
~~~
把Category模型的title字段映射为`category_name`字段,如果有多个字段,可以使用同样的方式添加。
可以通过_on来给视图模型定义关联查询条件,例如:
~~~
'_on'=>'Blog.category_id=Category.id'
~~~
理解之后,User模型的定义方式同样也就很容易理解了。
~~~
Blog.categoryId=Category.id AND Blog.userId=User.id
~~~
最后,我们把视图模型的定义翻译成SQL语句就更加容易理解视图模型的原理了。假设我们不带任何其他条件查询全部的字段,那么查询的SQL语句就是
~~~
Select
Blog.id as id,
Blog.name as name,
Blog.title as title,
Category.title as category_name,
User.name as username
from think_blog Blog JOIN think_category Category JOIN think_user User
where Blog.category_id=Category.id AND Blog.user_id=User.id
~~~
视图模型的定义并不需要先单独定义其中的模型类,系统会默认按照系统的规则进行数据表的定位。如果Blog模型并没有定义,那么系统会自动根据当前模型的表前缀和后缀来自动获取对应的数据表。也就是说,如果我们并没有定义Blog模型类,那么上面的定义后,系统在进行视图模型的操作的时候会根据Blog这个名称和当前的表前缀设置(假设为Think_ )获取到对应的数据表可能是think_blog。
ThinkPHP还可以支持视图模型的JOIN类型定义,我们可以把上面的视图定义改成:
~~~
public $viewFields = array(
'Blog'=>array('id','name','title','_type'=>'LEFT'),
'Category'=>array('title'=>'category_name','_on'=>'Category.id=Blog.category_id','_type'=>'RIGHT'),
'User'=>array('name'=>'username','_on'=>'User.id=Blog.user_id'),
);
~~~
需要注意的是,这里的_type定义对下一个表有效,因此要注意视图模型的定义顺序。Blog模型的
~~~
'_type'=>'LEFT'
~~~
针对的是下一个模型Category而言,通过上面的定义,我们在查询的时候最终生成的SQL语句就变成:
~~~
Select
Blog.id as id,
Blog.name as name,
Blog.title as title,
Category.title as category_name,
User.name as username
from think_blog Blog LEFT JOIN think_category Category ON Blog.category_id=Category.id RIGHT JOIN think_user User ON Blog.user_id=User.id
~~~
我们可以在试图模型里面定义特殊的字段,例如下面的例子定义了一个统计字段
~~~
'Category'=>array('title'=>'category_name','COUNT(Blog.id)'=>'count','_on'=>'Category.id=Blog.category_id'),
~~~
### 视图查询
接下来,我们就可以和使用普通模型一样对视图模型进行操作了 。
~~~
$Model = D("BlogView");
$Model->field('id,name,title,category_name,username')->where('id>10')->order('id desc')->select();
~~~
看起来和普通的模型操作并没有什么大的区别,可以和使用普通模型一样进行查询。如果发现查询的结果存在重复数据,还可以使用group方法来处理。
~~~
$Model->field('id,name,title,category_name,username')->order('id desc')->group('id')->select();
~~~
我们可以看到,即使不定义视图模型,其实我们也可以通过方法来操作,但是显然非常繁琐。
~~~
$Model = D("Blog");
$Model->table('think_blog Blog,think_category Category,think_user User')
->field('Blog.id,Blog.name,Blog.title,Category.title as category_name,User.name as username')
->order('Blog.id desc')
->where('Blog.category_id=Category.id AND Blog.user_id=User.id')
->select();
~~~
而定义了视图模型之后,所有的字段会进行自动处理,添加表别名和字段别名,从而简化了原来视图的复杂查询。如果不使用视图模型,也可以用连贯操作的JOIN方法实现相同的功能。
[上一页](# "上一页")[下一页](# "下一页")
- 序言
- 基础
- 获取ThinkPHP
- 环境要求
- 目录结构
- 入口文件
- 自动生成
- 模块
- 控制器
- 开发规范
- 配置
- 配置格式
- 配置加载
- 读取配置
- 动态配置
- 扩展配置
- 批量配置
- 架构
- 模块化设计
- URL模式
- 多层MVC
- CBD模式
- 命名空间
- 自动加载
- 应用模式
- 项目编译
- 系统流程
- 路由
- 路由定义
- 规则路由
- 正则路由
- 静态路由
- 闭包支持
- 实例说明
- 控制器
- 控制器定义
- 前置和后置操作
- Action参数绑定
- 伪静态
- URL大小写
- URL生成
- AJAX返回
- 跳转和重定向
- 输入变量
- 请求类型
- 空操作
- 空控制器
- 操作绑定到类
- 模型
- 模型定义
- 模型实例化
- 字段定义
- 连接数据库
- 切换数据库
- 分布式数据库支持
- 连贯操作
- WHERE
- TABLE
- ALIAS
- DATA
- FIELD
- ORDER
- LIMIT
- PAGE
- GROUP
- HAVING
- JOIN
- UNION
- DISTINCT
- LOCK
- CACHE
- COMMENT
- RELATION
- 命名范围
- CURD操作
- 数据创建
- 数据写入
- 数据读取
- 数据更新
- 数据删除
- ActiveRecord
- 字段映射
- 查询语言
- 查询方式
- 表达式查询
- 快捷查询
- 区间查询
- 组合查询
- 统计查询
- SQL查询
- 动态查询
- 子查询
- 自动验证
- 自动完成
- 参数绑定
- 虚拟模型
- 模型分层
- 视图模型
- 关联模型
- 高级模型
- Mongo模型
- 视图
- 模板定义
- 模板主题
- 模板赋值
- 模板渲染
- 获取模板地址
- 获取内容
- 模板引擎
- 模板
- 变量输出
- 系统变量
- 使用函数
- 默认值输出
- 使用运算符
- 标签库
- 模板继承
- 修改定界符
- 三元运算
- 包含文件
- 内置标签
- Volist标签
- Foreach标签
- For标签
- Switch标签
- 比较标签
- 范围判断标签
- IF标签
- Present标签
- Empty标签
- Defined标签
- Assign标签
- Define标签
- 标签嵌套
- import标签
- 使用PHP代码
- 原样输出
- 模板注释
- 模板布局
- 模板替换
- 调试
- 调试模式
- 异常处理
- 日志记录
- 页面Trace
- Trace方法
- 变量调试
- 性能调试
- 错误调试
- 模型调试
- 缓存
- 数据缓存
- 快速缓存
- 查询缓存
- SQL解析缓存
- 静态缓存
- 安全
- 输入过滤
- 表单合法性检测
- 表单令牌
- 防止SQL注入
- 目录安全文件
- 保护模板文件
- 上传安全
- 防止XSS攻击
- 其他安全建议
- 扩展
- 类库扩展
- 驱动扩展
- 缓存驱动
- 数据库驱动
- 日志驱动
- Session驱动
- 存储驱动
- 模板引擎驱动
- 标签库驱动
- 行为扩展
- 标签扩展
- Widget扩展
- 应用模式
- 部署
- PATH_INFO支持
- URL重写
- 模块部署
- 域名部署
- 入口绑定
- 替换入口
- 专题
- SESSION支持
- Cookie支持
- 多语言支持
- 数据分页
- 文件上传
- 验证码
- 图像处理
- RESTFul
- RPC
- SAE
- IP获取和定位
- 附录
- 常量参考
- 配置参考
- 升级指导
- 更新日志
- 鸣谢
- 关于