# 视图模型
[上一页](# "上一页")[下一页](# "下一页")
### 视图定义
视图通常是指数据库的视图,视图是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。但是,视图并不在数据库中以存储的数据值集形式存在。行和列数据来自由定义视图的查询所引用的表,并且在引用视图时动态生成。对其中所引用的基础表来说,视图的作用类似于筛选。定义视图的筛选可以来自当前或其它数据库的一个或多个表,或者其它视图。分布式查询也可用于定义使用多个异类源数据的视图。如果有几台不同的服务器分别存储组织中不同地区的数据,而您需要将这些服务器上相似结构的数据组合起来,这种方式就很有用。
视图在有些数据库下面并不被支持,但是ThinkPHP模拟实现了数据库的视图,该功能可以用于多表联合查询。非常适合解决HAS_ONE 和 BELONGS_TO 类型的关联查询。
要定义视图模型,只需要继承ViewModel,然后设置viewFields属性即可。例如下面的例子,我们定义了一个BlogView模型对象,其中包括了Blog模型的id、name、title和User模型的name,以及Category模型的title字段,我们通过创建BlogView模型来快速读取一个包含了User名称和类别名称的Blog记录(集)。 `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_db.test_table"`如果希望给当前数据表定义另外的别名,可以使用`'_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方法实现相同的功能。
[上一页](# "上一页")[下一页](# "下一页")
- 序言
- 1. 入门
- 1.1 简介
- 1.2 基础概念
- 1.3 获取ThinkPHP
- 1.4 环境要求
- 1.5 许可协议
- 1.6 目录结构
- 1.7 命名规范
- 1.8 MVC分层
- 1.9 CBD架构
- 1.10 特性概述
- 1.11 系统流程
- 1.12 开发流程
- 2. 入口
- 2.1 入口文件
- 2.2 项目目录
- 2.3 部署目录
- 2.4 项目编译
- 2.5 调试模式
- 3. 配置
- 3.1 配置格式
- 3.2 惯例配置
- 3.3 项目配置
- 3.4 调试配置
- 3.5 分组配置
- 3.6 读取配置
- 3.7 动态配置
- 3.8 扩展配置
- 4. 函数和类库
- 4.1 函数库
- 4.2 类库
- 5. 控制器
- 5.1 URL模式
- 5.2 模块和操作
- 5.3 定义控制器
- 5.4 空操作
- 5.5 空模块
- 5.6 模块分组
- 5.7 URL伪静态
- 5.8 URL路由
- 5.9 URL重写
- 5.10 URL生成
- 5.11 URL大小写
- 5.12 前置和后置操作
- 5.13 跨模块调用
- 5.14 页面跳转
- 5.15 重定向
- 5.16 获取系统变量
- 5.17 判断请求类型
- 5.18 获取URL参数
- 5.19 AJAX返回
- 5.20 Action参数绑定
- 5.21 多层控制器支持
- 6. 模型
- 6.1 模型定义
- 6.2 模型实例化
- 6.3 字段定义
- 6.4 数据主键
- 6.5 属性访问
- 6.6 跨库操作
- 6.7 连接数据库
- 6.8 切换数据库
- 6.9 分布式数据库
- 6.10 创建数据
- 6.11 字段映射
- 6.12 连贯操作
- 6.13 CURD操作
- 6.14 ActiveRecord
- 6.15 自动验证
- 6.16 命名范围
- 6.17 自动完成
- 6.18 查询语言
- 6.19 查询锁定
- 6.20 字段排除
- 6.21 事务支持
- 6.22 高级模型
- 6.23 视图模型
- 6.24 关联模型
- 6.25 Mongo模型
- 6.26 动态模型
- 6.27 虚拟模型
- 6.28 多层模型支持
- 7. 视图
- 7.1 模板定义
- 7.2 模板赋值
- 7.3 模板输出
- 7.4 模板替换
- 7.5 获取内容
- 7.6 模板引擎
- 7.7 布局模板
- 8. 模板引擎
- 8.1 变量输出
- 8.2 系统变量
- 8.3 使用函数
- 8.4 默认值输出
- 8.5 使用运算符
- 8.6 内置标签
- 8.7 包含文件
- 8.8 导入文件
- 8.9 Volist标签
- 8.10 Foreach标签
- 8.11 For标签
- 8.12 Switch标签
- 8.13 比较标签
- 8.14 三元运算
- 8.15 范围判断标签
- 8.16 Present标签
- 8.17 Empty标签
- 8.18 Defined标签
- 8.19 Define标签
- 8.20 Assign标签
- 8.21 IF标签
- 8.22 标签嵌套
- 8.23 使用PHP代码
- 8.24 模板布局
- 8.25 模板继承
- 8.26 原样输出
- 8.27 模板注释
- 8.28 引入标签库
- 8.29 修改定界符
- 8.30 避免JS混淆
- 9. 日志
- 9.1 日志级别
- 9.2 记录方式
- 9.3 手动记录
- 10. 错误
- 10.1 异常处理
- 10.2 异常模板
- 10.3 异常显示
- 11. 调试
- 11.1 运行状态
- 11.2 页面Trace
- 11.3 调试方法
- 12. 缓存
- 12.1 缓存方式
- 12.2 动态缓存
- 12.3 缓存队列
- 12.4 快捷缓存
- 12.5 快速缓存
- 12.6 查询缓存
- 12.7 SQL解析缓存
- 12.8 静态缓存
- 13. 扩展
- 13.1 行为扩展
- 13.2 类库扩展
- 13.3 控制器扩展
- 13.4 模型扩展
- 13.5 驱动扩展
- 13.6 Widget扩展
- 13.7 模式扩展
- 13.8 引擎扩展
- 14. 安全
- 14.1 表单令牌
- 14.2 字段类型验证
- 14.3 防止SQL注入
- 14.4 输入过滤
- 14.5 上传安全
- 14.6 防止XSS攻击
- 14.7 其他安全建议
- 14.8 目录安全文件
- 14.9 保护模板文件
- 15. 性能
- 15.1 关闭调试模式
- 15.2 开启缓存
- 15.3 合并字段缓存
- 15.4 优化SQL
- 15.5 替换入口
- 15.6 前端优化
- 16. 部署
- 16.1 PATH_INFO支持
- 16.2 隐藏index.php
- 16.3 二级域名部署
- 16.4 定制错误页面
- 16.5 设置时区
- 17. SAE支持
- 17.1 SAE介绍
- 17.2 获取SAE
- 17.3 SAE开发
- 18. REST支持
- 18.1 REST介绍
- 18.2 REST模式
- 18.3 REST配置
- 18.4 REST路由
- 18.5 REST方法
- 19. 杂项
- 19.1 Session支持
- 19.2 Cookie支持
- 19.3 日期和时间
- 19.4 WML开发
- 19.5 多语言
- 19.6 数据分页
- 19.7 文件上传
- 19.8 验证码
- 19.9 图片添加水印
- 19.10 IP获取和定位
- 20. 附录
- 20.1 常量参考
- 20.2 配置参考
- 20.3 关于升级
- 20.4 大事记
- 鸣谢
- 关于