基础:
1. 基础概念
LAMP
LAMP是基于Linux,Apache,MySQL和PHP的开放资源网络开发平台。这个术语来自欧洲,在那里这些程序常用来作为一种标准开发环境。名字来源于每个程序的第一个字母。每个程序在所有权里都符合开放源代码标准:Linux是开放系统;Apache是最通用的网络服务器;MySQL是带有基于网络管理附加工具的关系数据库;PHP是流行的对象脚本语言,它包含了多数其它语言的优秀特征来使得它的网络开发更加有效。开发者在Windows操作系统下使用这些Linux环境里的工具称为使用WAMP。虽然这些开放源代码程序本身并不是专门设计成同另外几个程序一起工作的,但由于它们都是影响较大的开源软件,拥有很多共同特点,这就导致了这些组件经常在一起使用。在过去的几年里,这些组件的兼容性不断完善,在一起的应用情形变得更加普遍。并且它们为了改善不同组件之间的协作,已经创建了某些扩展功能。目前,几乎在所有的Linux发布版中都默认包含了这些产品。Linux操作系统、Apache服务器、MySQL数据库和Perl、PHP或者 Python语言,这些产品共同组成了一个强大的Web应用程序平台。
随着开源潮流的蓬勃发展,开放源代码的LAMP已经与J2EE和.Net商业软件形成三足鼎立之势,并且该软件开发的项目在软件方面的投资成本较低,因此受到整个IT界的关注。从网站的流量上来说,70%以上的访问流量是LAMP来提供的,LAMP是最强大的网站解决方案.
OOP
面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构。OOP 的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成。OOP 达到了软件工程的三个主要目标:重用性、灵活性和扩展性。为了实现整体运算,每个对象都能够接收信息、处理数据和向其它对象发送信息。OOP 主要有以下的概念和组件:组件 - 数据和功能一起在运行着的计算机程序中形成的单元,组件在 OOP 计算机程序中是模块和结构化的基础。
抽象性 - 程序有能力忽略正在处理中信息的某些方面,即对信息主要方面关注的能力。
封装 - 也叫做信息封装:确保组件不会以不可预期的方式改变其它组件的内部状态;只有在那些提供了内部状态改变方法的组件中,才可以访问其内部状态。每类组件都提供了一个与其它组件联系的接口,并规定了其它组件进行调用的方法。
多态性 - 组件的引用和类集会涉及到其它许多不同类型的组件,而且引用组件所产生的结果得依据实际调用的类型。
继承性 - 允许在现存的组件基础上创建子类组件,这统一并增强了多态性和封装性。典型地来说就是用类来对组件进行分组,而且还可以定义新类为现存的类的扩展,这样就可以将类组织成树形或网状结构,这体现了动作的通用性。
由于抽象性、封装性、重用性以及便于使用等方面的原因,以组件为基础的编程在脚本语言中已经变得特别流行。
MVC
MVC是一个设计模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型(M)、视图(V)、控制器(C),它们各自处理自己的任务。视图 :视图是用户看到并与之交互的界面。对老式的Web应用程序来说,视图就是由HTML元素组成的界面,在新式的Web应用程序中,HTML依旧在视图中扮演着重要的角色,但一些新的技术已层出不穷,它们包括Adobe Flash和象XHTML,XML/XSL,WML等一些标识语言和Web services。如何处理应用程序的界面变得越来越有挑战性。MVC一个大的好处是它能为你的应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,不管这些数据是联机存储的还是一个雇员列表,作为视图来讲,它只是作为一种输出数据并允许用户操纵的方式。
模型 :模型表示企业数据和业务规则。在MVC的三个部件中,模型拥有最多的处理任务。例如它可能用象EJBs和ColdFusion Components这样的构件对象来处理数据库。被模型返回的数据是中立的,就是说模型与数据格式无关,这样一个模型能为多个视图提供数据。由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。
控制器 :控制器接受用户的输入并调用模型和视图去完成用户的需求。所以当单击Web页面中的超链接和发送HTML表单时,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后确定用哪个视图来显示模型处理返回的数据。
现在我们总结MVC的处理过程,首先控制器接收用户的请求,并决定应该调用哪个模型来进行处理,然后模型用业务逻辑来处理用户的请求并返回数据,最后控制器用相应的视图格式化模型返回的数据,并通过表示层呈现给用户。
ORM
对象-关系映射(Object/Relation Mapping,简称ORM),是随着面向对象的软件开发方法发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。
AOP
AOP(Aspect-Oriented Programming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”
CURD
CURD是一个数据库技术中的缩写词,一般的项目开发的各种参数的基本功能都是CURD。它代表创建(Create)、更新(Update)、读取(Read)和删除(Delete)操作。CURD 定义了用于处理数据的基本原子操作。之所以将CURD 提升到一个技术难题的高度是因为完成一个涉及在多个数据库系统中进行CURD操作的汇总相关的活动,其性能可能会随数据关系的变化而有非常大的差异。CURD在具体的应用中并非一定使用create、update 、read和delete字样的方法,但是他们完成的功能是一致的。例如,ThinkPHP就是使用add、save、select和delete方法表示模型的CURD操作。
ActiveRecord
Active Record(中文名:活动记录)是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。Active Record 和 Row Gateway (行记录入口)十分相似,但前者是领域模型,后者是一种数据源模式。关系型数据库往往通过外键来表述实体关系,Active Record 在数据源层面上也将这种关系映射为对象的关联和聚集。 Active Record 适合非常简单的领域需求,尤其在领域模型和数据库模型十分相似的情况下。如果遇到更加复杂的领域模型结构(例如用到继承、策略的领域模型),往往需要使用分离数据源的领域模型,结合 Data Mapper (数据映射器)使用。Active Record 驱动框架一般兼有 ORM 框架的功能,但 Active Record 不是简单的 ORM,正如和 Row Gateway 的区别。由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。
单一入口
单一入口通常是指一个项目或者应用具有一个统一(但并不一定是唯一)的入口文件,也就是说项目的所有功能操作都是通过这个入口文件进行的,并且往往入口文件是第一步被执行的。单一入口的好处是项目整体比较规范,因为同一个入口,往往其不同操作之间具有相同的规则。另外一个方面就是单一入口带来的好处是控制较为灵活,因为拦截方便了,类似如一些权限控制、用户登录方面的判断和操作可以统一处理了。
或者有些人会担心所有网站都通过一个入口文件进行访问,是否会造成太大的压力,其实这是杞人忧天的想法。
2. 目录结构
目录/文件 | 说明 |
---|---|
ThinkPHP.php | 框架入口文件 |
Common | 框架公共文件目录 |
Conf | 框架配置文件目录 |
Lang | 框架系统语言目录 |
Lib | 系统核心基类库目录 |
Tpl | 系统模板目录 |
Extend | 框架扩展目录(关于扩展目录的详细信息请参考后面的扩展章节) |
3. 命名规范
- 类文件都是以.class.php为后缀(这里是指的ThinkPHP内部使用的类库文件,不代表外部加载的类库文件),使用驼峰法命名,并且首字母大写,例如DbMysql.class.php;
- 确保文件的命名和调用大小写一致,是由于在类Unix系统上面,对大小写是敏感的(而ThinkPHP在调试模式下面,即使在Windows平台也会严格检查大小写);
- 类名和文件名一致(包括上面说的大小写一致),例如 UserAction类的文件命名是UserAction.class.php, InfoModel类的文件名是InfoModel.class.php, 并且不同的类库的类命名有一定的规范;
- 函数、配置文件等其他类库文件之外的一般是以.php为后缀(第三方引入的不做要求);
- 函数的命名使用小写字母和下划线的方式,例如 get_client_ip;
- 方法的命名使用驼峰法,并且首字母小写或者使用下划线“_”,例如 getUserName,_parseType,通常下划线开头的方法属于私有方法;
- 属性的命名使用驼峰法,并且首字母小写或者使用下划线“_”,例如 tableName、_instance,通常下划线开头的属性属于私有属性;
- 以双下划线“__”打头的函数或方法作为魔法方法,例如 __call 和 __autoload;
- 常量以大写字母和下划线命名,例如 HAS_ONE和 MANY_TO_MANY;
- 配置参数以大写字母和下划线命名,例如HTML_CACHE_ON;
- 语言变量以大写字母和下划线命名,例如MY_LANG,以下划线打头的语言变量通常用于系统语言变量,例如 _CLASS_NOT_EXIST_;
- 对变量的命名没有强制的规范,可以根据团队规范来进行;
- ThinkPHP的模板文件默认是以.html 为后缀(可以通过配置修改);
- 数据表和字段采用小写加下划线方式命名,并注意字段名不要以下划线开头,例如 think_user 表和 user_name字段,类似 _username 这样的数据表字段可能会被过滤。
4. CBD架构
5. 开发流程
使用ThinkPHP创建应用的一般开发流程是:- 系统设计、创建数据库和数据表;(可选)
- 项目命名并创建项目入口文件,开启调试模式;
- 完成项目配置;
- 创建项目函数库;(可选)
- 开发项目需要的扩展(模式、驱动、标签库等);(可选)
- 创建控制器类;
- 创建模型类;(可选)
- 创建模板文件;
- 运行和调试、分析日志;
- 开发和设置缓存功能;(可选)
- 添加路由支持;(可选)
- 安全检查;(可选 )
- 部署到生产环境。
6. 入口文件
ThinkPHP采用单一入口模式进行项目部署和访问,无论完成什么功能,一个项目都有一个统一(但不一定是唯一)的入口。应该说,所有项目都是从入口文件开始的,并且所有的项目的入口文件是类似的,入口文件中主要包括:- 定义框架路径、项目路径和项目名称(可选)
- 定义调试模式和运行模式的相关常量(可选)
- 载入框架入口文件(必须)
7. 项目目录
目录 | 说明 |
---|---|
Common | 项目公共文件目录,一般放置项目的公共函数 |
Conf | 项目配置目录,项目所有的配置文件都放在这里 |
Lang | 项目语言包目录(可选 如果不需要多语言支持 可删除) |
Lib | 项目类库目录,通常包括Action和Model子目录 |
Tpl | 项目模板目录,支持模板主题 |
Runtime | 项目运行时目录,包括Cache(模板缓存)、Temp(数据缓存)、Data(数据目录)和Logs(日志文件)子目录,如果存在分组的话,则首先是分组目录。 |
- //定义项目名称
- define('APP_NAME', 'App');
- //定义项目路径
- define('APP_PATH', './App/');
- //加载框架入文件
- require './App/ThinkPHP/ThinkPHP.php';
APP_PATH 是指项目路径(必须以“/”结束),项目路径是指项目的Common、Lib目录所在的位置,而不是项目入口文件所在的位置。
注意:在类Unix或者Linux环境下面Runtime目录需要可写权限。
8. 部署目录
目录/文件 | 说明 |
---|---|
ThinkPHP | 系统目录(下面的目录结构同上面的系统目录) |
Public | 网站公共资源目录(存放网站的Css、Js和图片等资源) |
Uploads | 网站上传目录(用户上传的统一目录) |
Home | 项目目录(下面的目录结构同上面的应用目录) |
Admin | 后台管理项目目录 |
…… 更多的项目目录 | |
index.php | 项目Home的入口文件 |
admin.php | 项目Admin的入口文件 |
…… 更多的项目入口文件 |
事实上,系统目录和项目目录可以放到非WEB访问目录下面,网站目录下面只需要放置Public公共目录和入口文件,从而提高网站的安全性。
如果希望自己设置目录,可以在入口文件里面更改RUNTIME_PATH常量进行更改,例如:
- define('RUNTIME_PATH','./App/temp/');
除了自定义编译缓存目录之外,还支持自定义编译缓存文件名,例如:
- define('RUNTIME_FILE','./App/temp/runtime_cache.php');
- //项目配置文件
- return array(
- 'DEFAULT_MODULE' => 'Index', //默认模块
- 'URL_MODEL' => '2', //URL模式
- 'SESSION_AUTO_START' => true, //是否开启session
- //更多配置参数
- //...
- );
- //项目配置文件
- return array(
- 'DEFAULT_MODULE' => 'Index', //默认模块
- 'URL_MODEL' => '2', //URL模式
- 'SESSION_AUTO_START' => true, //是否开启session
- 'USER_CONFIG' => array(
- 'USER_AUTH' => true,
- 'USER_TYPE' => 2,
- ),
- //更多配置参数
- //...
- );
9. 惯例配置和项目配置,调试配置
惯例重于配置是系统遵循的一个重要思想,系统内置有一个惯例配置文件(位于系统目录下面的Conf\convention.php),按照大多数的使用对常用参数进行了默认配置。在项目配置文件里面除了添加内置的参数配置外,还可以额外添加项目需要的配置参数。
- 'APP_STATUS' => 'debug', //应用调试模式状态
如果想在调试模式下面增加应用状态,例如测试状态,则可以在项目配置文件中改变设置如下:
- 'APP_STATUS' => 'test', //应用调试模式状态
注意:一旦关闭调试模式,项目的调试配置文件即刻失效。
10. 分组配置和读取配置,动态配置
如果启用了模块分组,则可以在对每个分组单独定义配置文件,分组配置文件位于:项目配置目录/分组名称/config.php
可以通过如下配置启用分组:
- 'APP_GROUP_LIST' => 'Home,Admin', //项目分组设定
- 'DEFAULT_GROUP' => 'Home', //默认分组
Conf/Home/config.php
Conf/Admin/config.php
每个分组的配置文件仅在当前分组有效,分组配置的定义格式和项目配置是一样的。
注意:分组名称区分大小写,必须和定义的分组名一致。
- C('参数名称')//获取已经设置的参数值
- C('USER_CONFIG.USER_TYPE')//获取用户配置中的用户类型设置
设置新的值:
- C('参数名称','新的参数值');
- C('DATA_CACHE_TIME','60');
获取已经设置的参数值:
- C('USER_CONFIG.USER_TYPE');
- C('USER_CONFIG.USER_TYPE','1');
- C($array,'name');
获取缓存的设置列表数据 可以用
- C('','name'); //或者C(null,'name');
11. 扩展配置
项目配置文件在部署模式的时候会纳入编译缓存,也就是说编译后再修改项目配置文件就不会立刻生效,需要删除编译缓存后才能生效。扩展配置文件则不受此限制影响,即使在部署模式下面,修改配置后可以实时生效,并且配置格式和项目配置一样。设置扩展配置的方式如下(多个文件用逗号分隔):
- 'LOAD_EXT_CONFIG' => 'user,db', // 加载扩展配置文件
- 'LOAD_EXT_CONFIG' => array(
- 'USER' => 'user', //用户配置
- 'DB' => 'db', //数据库配置
- ), //加载扩展配置文件
- C('USER.USER_AUTH_ID');
文件名 | 说明 |
---|---|
config.php | 项目配置文件 |
tags.php | 项目行为配置文件 |
alias.php | 项目别名定义文件 |
debug.php | 项目调试模式配置文件(以及项目设置的APP_STATUS对应的配置文件) |
core.php | 项目追加的核心编译列表文件(不会覆盖核心编译列表) |
12. 函数库
系统函数库
库系统函数库位于系统的Common目录下面,有三个文件:common.php是全局必须加载的基础函数库,在任何时候都可以直接调用;
functions.php是框架标准模式的公共函数库,其他模式可以替换加载自己的公共函数库或者对公共函数库中的函数进行重新定义;
runtime.php是框架运行时文件,仅在调试模式或者编译过程才会被加载,因此其中的方法在项目中不能直接调用;
项目函数库
库项目函数库通常位于项目的Common目录下面,文件名为common.php,该文件会在执行过程中自动加载,并且合并到项目编译统一缓存,如果使用了分组部署方式,并且该目录下存在"分组名称/function.php"文件,也会根据当前分组执行时对应进行自动加载,因此项目函数库的所有函数也都可以无需手动载入而直接使用。如果项目配置中使用了动态函数加载配置的话,项目Common目录下面可能会存在更多的函数文件,动态加载的函数文件不会纳入编译缓存。
在特殊的情况下,模式可以改变自动加载的项目函数库的位置或者名称。
扩展函数库
库我们可以在项目公共目录下面定义扩展函数库,方便需要的时候加载和调用。扩展函数库的函数定义规范和项目函数库一致,只是函数库文件名可以随意命名,一般来说,扩展函数库并不会自动加载,除非你设置了动态载入。函数加载
系统函数库和项目函数库中的函数无需加载就可以直接调用,对于项目的扩展函数库,可以采用下面两种方式调用:动态载入
我们可以在项目配置文件中定义LOAD_EXT_FILE参数,例如:
- "LOAD_EXT_FILE"=>"user,db"
手动载入
如果你的函数只是个别模块偶尔使用,则不需要采用自动加载方式,可以在需要调用的时候采用load方法手动载入,方式如下:
- load("@.user")
13. 类库
ThinkPHP的类库包括基类库和应用类库,系统的类库命名规则如下:类库 | 规则 | 示例 |
---|---|---|
控制器类 | 模块名+Action | 例如 UserAction、InfoAction |
模型类 | 模型名+Model | 例如 UserModel、InfoModel |
行为类 | 行为名+Behavior | 例如CheckRouteBehavior |
Widget类 | Widget名+Widget | 例如BlogInfoWidget |
驱动类 | 引擎名+驱动名 | 例如DbMysql表示mysql数据库驱动、CacheFile表示文件缓存驱动 |
基类库
基类库是指符合ThinkPHP类库规范的系统类库,包括ThinkPHP的核心基类库和扩展基类库。核心基类库目录位于系统的Lib目录,核心基类库也就是Think类库,扩展基类库位于Extend/Library目录,可以扩展ORG 、Com扩展类库。核心基类库的作用是完成框架的通用性开发而必须的基础类和内置支持类等,包含有:目录 | 调用路径 | 说明 |
---|---|---|
Lib/Core | Think.Core | 核心类库包 |
Lib/Behavior | Think.Behavior | 内置行为类库包 |
Lib/Driver | Think.Driver | 内置驱动类库包 |
Lib/Template | Think.Template | 内置模板引擎类库包 |
类名 | 说明 |
---|---|
Action | 系统基础控制器类 |
App | 系统应用类 |
Behavior | 系统行为基础类 |
Cache | 系统缓存类 |
Db | 系统抽象数据库类 |
Dispatcher | URL调度类 |
Log | 系统日志类 |
Model | 系统基础模型类 |
Think | 系统入口和静态类 |
ThinkException | 系统基础异常类 |
View | 视图类 |
Widget | 系统Widget基础类 |
应用类库
应用类库是指项目中自己定义或者使用的类库,这些类库也是遵循ThinkPHP的命名规范。应用类库目录位于项目目录下面的Lib目录。应用类库的范围很广,包括Action类库、Model类库或者其他的工具类库,通常包括:目录 | 调用路径 | 说明 |
---|---|---|
Lib/Action | @.Action或自动加载 | 控制器类库包 |
Lib/Model | @.Model或自动加载 | 模型类库包 |
Lib/Behavior | 用B方法调用或自动加载 | 应用行为类库包 |
Lib/Widget | 用W方法在模板中调用 | 应用Widget类库包 |
类库导入
一、Import显式导入
ThinkPHP模拟了Java的类库导入机制,统一采用import方法进行类文件的加载。import方法是ThinkPHP内建的类库导入方法,提供了方便和灵活的文件导入机制,完全可以替代PHP的require和include方法。例如:- import("Think.Util.Session");
- import("App.Model.UserModel");
- import("Think.Util.Session");
- import("ORG.Util.Page");
- import("MyApp.Action.UserAction");
- import("MyApp.Model.InfoModel");
- import("@.Action.UserAction");
- import("@.Model.InfoModel");
二,别名导入
除了命名空间的导入方式外,import方法还可以支持别名导入,要使用别名导入,首先要定义别名,我们可以在项目配置目录下面增加alias.php 用以定义项目中需要用到的类库别名,例如:- return array(
- 'rbac' =>LIB_PATH.'Common/Rbac.class.php',
- 'page' =>LIB_PATH.'Common/Page.class.php',
- );
- import("rbac");
- import("page");
导入第三方类库
第三方类库统一放置在系统扩展目录下的Vendor 目录,并且使用vendor 方法导入,其参数和 import 方法是 一致的,只是默认的值有针对变化。 例如,我们把 Zend 的 Filter\Dir.php 放到 Vendor 目录下面,这个时候 Dir 文件的路径就是- Vendor('Zend.Filter.Dir');
Vendor方法也可以支持和import方法一样的基础路径和文件名后缀参数,例如:
- Vendor('Zend.Filter.Dir',dirname(__FILE__),'.class.php');
自动加载
在大多数情况下,我们无需手动导入类库,而是通过配置采用自动加载机制即可,自动加载机制是真正的按需加载,可以很大程度的提高性能。自动加载有三种情况,按照加载优先级从高到低分别是:别名自动加载、系统规则自动加载和自定义路径自动加载。一、别名自动加载
在前面我们提到了别名的定义方式,并且采用了import方法进行别名导入,其实所有定义别名的类库都无需再手动加载,系统会按需自动加载。二、 系统规则自动加载
果你没有定义别名的话,系统会首先按照内置的规则来判断加载,系统规则仅针对行为类、模型类和控制器类,搜索规则如下:类名 | 规则 | 说明 |
---|---|---|
行为类 | 规则1 | 搜索系统类库目录下面的Behavior目录 |
规则2 | 搜索系统扩展目录下面的Behavior目录 | |
规则3 | 搜索应用类库目录下面的Behavior目录 | |
规则4 | 如果启用了模式扩展,则搜索模式扩展目录下面的Behavior目录 | |
模型类 | 规则1 | 如果启用分组,则搜索应用类库目录的Model/当前分组 目录 |
规则2 | 搜索应用类库下面的Model目录 | |
规则3 | 搜索系统扩展目录下面的Model目录 | |
控制器类 | 规则1 | 如果启用分组,则搜索应用类库目录的Action/当前分组 目录 |
规则2 | 搜索项目类库目录下面的Action目录 | |
规则3 | 搜索系统扩展目录下面的Action目录 |
三、 自定义路径自动加载
当你的类库比较集中在某个目录下面,而且不想定义太多的别名导入的话,可以使用自定义路径自动加载方式,这种方式需要在项目配置文件中添加自动加载的搜索路径,例如:- 'APP_AUTOLOAD_PATH' =>'@.Common,@.Tool',
注意:自动搜索路径定义只能采用命名空间方式,也就是说这种方式只能自动加载项目类库目录和基类库目录下面的类库文件。
控制器:
1. URL模式
采用传统的URL参数模式
- http://serverName/appName/?m=module&a=action&id=1
默认情况使用PATHINFO模式,ThinkPHP内置强大的PATHINFO支持,提供灵活和友好URL支持。PATHINFO模式自动识别模块和操作,例如
- http://serverName/appName/module/action/id/1/或者
- http://serverName/appName/module,action,id,1/
该URL模式和PATHINFO模式功能一样,除了可以不需要在URL里面写入口文件,和可以定义.htaccess 文件外。在开启了Apache的URL_REWRITE模块后,就可以启用REWRITE模式了,具体参考下面的URL重写部分。
兼容模式是普通模式和PATHINFO模式的结合,并且可以让应用在需要的时候直接切换到PATHINFO模式而不需要更改模板和程序,还可以和URL_WRITE模式整合。兼容模式URL可以支持任何的运行环境。
- http://serverName/appName/?s=/module/action/id/1/
- http://serverName/appName/?s=module~action~id~1
2. 模块和操作
http://域名/项目名/分组名/模块名/操作名/其他参数Dispatcher会根据URL地址来获取当前需要执行的项目、分组(如果有定义的话)模块、操作以及其他参数,在某些情况下,项目名可能不会出现在URL地址中(通常情况下入口文件则代表了某个项目,而且入口文件可以被隐藏)。
每一个模块就是一个控制器类,通常位于项目的Lib\Action目录下面。
例如,如果设置:
- 'ACTION_SUFFIX'=>'Act'
3. 定义控制器和空操作,空模块
一个应用如果不需要和数据库交互的时候可以不需要定义模型类,但是必须定义Action控制器,一般位于项目的Lib/Action目录下面。Action控制器的定义非常简单,只要继承Action基础类就可以了,例如:
- Class UserAction extends Action{}
4. 模块分组
配置参数 | 说明 |
---|---|
APP_GROUP_LIST | 项目分组列表(配置即表示开启分组) |
DEFAULT_GROUP | 默认分组(默认值为Home) |
TMPL_FILE_DEPR | 分组模板下面模块和操作的分隔符,默认值为“/” |
VAR_GROUP | 分组的URL参数名,默认为g(普通模式URL才需要) |
- 'APP_GROUP_LIST' => 'Home,Admin', //项目分组设定
- 'DEFAULT_GROUP' => 'Home', //默认分组
5. URL伪静态
ThinkPHP支持伪静态URL设置,可以通过设置URL_HTML_SUFFIX参数随意在URL的最后增加你想要的静态后缀,而不会影响当前操作的正常执行。例如,我们设置- 'URL_HTML_SUFFIX'=>'shtml'
- http://serverName/Blog/read/id/1
- http://serverName/Blog/read/id/1.shtml
注意:伪静态后缀设置时可以不包含后缀中的“.”。所以,下面的配置其实是等效的:
- 'URL_HTML_SUFFIX'=>'.shtml'
- 'URL_HTML_SUFFIX'=>'html|shmtl|xml' // 多个用 | 分割
6. URL路由
ThinkPHP支持URL路由功能,要启用路由功能,需要设置URL_ROUTER_ON 参数为true。开启路由功能后,并且配置URL_ROUTE_RULES参数后,系统会自动进行路由检测,如果在路由定义里面找到和当前URL匹配的路由名称,就会进行路由解析和重定向。7. URL重写
详情见:http://doc.thinkphp.cn/manual/url_rewrite.html
8. URL生成
为了配合所使用的URL模式,我们需要能够动态的根据当前的URL设置生成对应的URL地址,为此,ThinkPHP内置提供了U方法,用于URL的动态生成,可以确保项目在移植过程中不受环境的影响。
U方法的定义规则如下(方括号内参数根据实际应用决定):
- U('[分组/模块/操作]?参数' [,'参数','伪静态后缀','是否跳转','显示域名'])
如果不定义项目和模块的话 就表示当前项目和模块名称,下面是一些简单的例子:
- U('User/add') // 生成User模块的add操作的URL地址
- U('Blog/read?id=1') // 生成Blog模块的read操作 并且id为1的URL地址
- U('Admin/User/select') // 生成Admin分组的User模块的select操作的URL地址
U方法的第二个参数支持数组和字符串两种定义方式,如果只是字符串方式的参数可以在第一个参数中定义,例如:
- U('Blog/cate',array('cate_id'=>1,'status'=>1))
- U('Blog/cate','cate_id=1&status=1')
- U('Blog/cate?cate_id=1&status=1')
三种方式是等效的,都是 生成Blog模块的cate操作 并且cate_id为1 status为1的URL地址
但是不允许使用下面的定义方式来传参数
- U('Blog/cate/cate_id/1/status/1')
- U('Blog/read?id=1')这个定义为例。
http://serverName/index.php?m=Blog&a=read&id=1
如果当前URL设置为PATHINFO模式的话,同样的方法最后生成的URL地址是:
http://serverName/index.php/Blog/read/id/1
如果当前URL设置为REWRITE模式的话,同样的方法最后生成的URL地址是:
http://serverName/Blog/read/id/1
如果当前URL设置为REWRITE模式,并且设置了伪静态后缀为.html的话,同样的方法最后生成的URL地址是:
http://serverName/Blog/read/id/1.html
U方法还可以支持路由,如果我们定义了一个路由规则为:
- 'news/:id\d'=>'News/read'
- U('/news/1')
- http://serverName/index.php/news/1
注意:如果你是在模板文件中直接使用U方法的话,需要采用 {:U('参数1', '参数2'…)} 的方式,具体参考模板引擎章节的8.3 使用函数内容。
如果你的应用涉及到多个子域名的操作地址,那么也可以在U方法里面指定需要生成地址的域名,例如:
- U('Blog/[email protected]','id=1');
@后面传入需要指定的域名即可。
此外,U方法的第5个参数如果设置为true,表示自动识别当前的域名,并且会自动根据子域名部署设置APP_SUB_DOMAIN_DEPLOY和APP_SUB_DOMAIN_RULES自动匹配生成当前地址的子域名。
如果开启了URL_CASE_INSENSITIVE,则会统一生成小写的URL地址。
9. URL大小写
只要在项目配置中,增加:
- 'URL_CASE_INSENSITIVE' =>true
就可以实现URL访问不再区分大小写了。
这里需要注意一个地方,如果我们定义了一个UserTypeAction的模块类,那么URL的访问应该是:
- http://serverName/index.php/user_type/list
- //而不是
- http://serverName/index.php/usertype/list
利用系统提供的U方法可以为你自动生成相关的URL地址。
如果设置
- 'URL_CASE_INSENSITIVE' =>false
的话,URL就又变成:
- http://serverName/index.php/UserType/list
注意:URL不区分大小写并不会改变系统的命名规范,并且只有按照系统的命名规范后才能正确的实现URL不区分大小写。
10. 前置和后置操作
系统会检测当前操作是否具有前置和后置操作,如果存在就会按照顺序执行,前置和后置操作的方法名是在要执行的方法前面加 _before_和_after_,例如:
- class CityAction extends Action{
- //前置操作方法
- public function _before_index(){
- echo 'before
'; - }
- public function index(){
- echo 'index
'; - }
- //后置操作方法
- public function _after_index(){
- echo 'after
'; - }
- }
需要注意的是,在有些方法里面使用了exit或者错误输出之类的话 有可能不会再执行后置方法了。
例如,如果在当前操作里面调用了系统Action的error方法,那么将不会再执行后置操作,但是不影响success方法的后置方法执行。
11. 跨模块调用
例如,我们在Index模块调用User模块的操作方法
- class IndexAction extends Action{
- public function index(){
- //实例化UserAction
- $User = new UserAction();
- //其他用户操作
- //...
- $this->display(); //输出页面模板
- }
- }
并且为了方便跨模块调用,系统内置了A方法和R方法。 $User = A('User');
事实上,A方法还支持跨分组或者跨项目调用,默认情况下是调用当前项目下面的模块。
跨项目调用的格式是:
A('[项目名://][分组名/]模块名')
例如:
- A('User') //表示调用当前项目的User模块
- A('Admin://User') //表示调用Admin项目的User模块
- A('Admin/User') //表示调用Admin分组的User模块
- A('Admin://Tool/User') //表示调用Admin项目Tool分组的User模块
R('[项目名://][分组名/]模块名/操作名',array('参数1','参数2'…))
例如:
- R('User/info') //表示调用当前项目的User模块的info操作方法
- R('Admin/User/info') //表示调用Admin分组的User模块的info操作方法
- R('Admin://Tool/User/info') //表示调用Admin项目Tool分组的User模块的info操作方法
- class UserAction extends Action{
- protected function info($id){
- $User = M('User');
- $User->find($id);
- //...
- }
- }
- R('User/info',array(15))
12. 页面跳转
系统的Action类内置了两个跳转方法success和error,用于页面跳转提示,而且可以支持ajax提交。使用方法很简单,举例如下:
- $User = M('User'); //实例化User对象
- $result = $User->add($data);
- if($result){
- //设置成功后跳转页面的地址,默认的返回页面是$_SERVER['HTTP_REFERER']
- $this->success('新增成功', 'User/list');
- } else {
- //错误页面的默认跳转页面是返回前一页,通常不需要设置
- $this->error('新增失败');
- }
- //默认错误跳转对应的模板文件
- 'TMPL_ACTION_ERROR' => THINK_PATH . 'Tpl/dispatch_jump.tpl';
- //默认成功跳转对应的模板文件
- 'TMPL_ACTION_SUCCESS' => THINK_PATH . 'Tpl/dispatch_jump.tpl';
- //默认错误跳转对应的模板文件
- 'TMPL_ACTION_ERROR' => 'Public:error';
- //默认成功跳转对应的模板文件
- 'TMPL_ACTION_SUCCESS' => 'Public:success';
$msgTitle | 操作标题 |
$message | 页面提示信息 |
$status | 操作状态 1表示成功 0 表示失败 具体还可以由项目本身定义规则 |
$waitSecond | 跳转等待时间 单位为秒 |
$jumpUrl | 跳转页面地址 |
- $this->assign('var1','value1');
- $this->assign('var2','value2');
- $this->error('错误的参数','要跳转的URL地址');
当采用AJAX方式提交的时候,会自动调用ajaxReturn方法传值过去(包括跳转的URL地址url和状态值status)
13. 重定向
Action类的redirect方法可以实现页面的重定向功能。redirect方法的参数用法和U函数的用法一致(参考上面的URL生成部分),例如:
- //重定向到New模块的Category操作
- $this->redirect('New/category', array('cate_id' => 2), 5, '页面跳转中...');
如果你仅仅是想重定向要一个指定的URL地址,而不是到某个模块的操作方法,可以直接使用redirect方法重定向,例如:
- //重定向到指定的URL地址
- redirect('/New/category/cate_id/2', 5, '页面跳转中...')
14. 获取系统变量
- $this->方法名("变量名",["过滤方法"],["默认值"])
方法名可以支持:
方法名 | 含义 |
---|---|
_get | 获取GET参数 |
_post | 获取POST参数 |
_param | 自动判断请求类型获取GET、POST或者PUT参数(3.1新增) |
_request | 获取REQUEST 参数 |
_put | 获取PUT 参数 |
_session | 获取 $_SESSION 参数 |
_cookie | 获取 $_COOKIE 参数 |
_server | 获取 $_SERVER 参数 |
_globals | 获取 $GLOBALS参数 |
变量名:(必须)是要获取的系统变量的名称
过滤方法:(可选)可以用任何的内置函数或者自定义函数名,如果没有指定的话,采用默认的htmlspecialchars函数进行安全过滤(由DEFAULT_FILTER 参数配置),参数就是前面方法名获取到的值,也就是说如果调用:
- $this->_get("name");
最终调用的结果就是 htmlspecialchars($_GET["name"]),如果要改变过滤方法,可以使用:
- $this->_get("name","strip_tags");
默认值:(可选)是要获取的参数变量不存在的情况下设置的默认值,例如:
- $this->_get("id","strip_tags",0);
如果$_GET["id"] 不存在的话,会返回0。
如果没有设置任何默认值的话,系统默认返回NULL。
也可以支持多函数过滤。
例如,可以设置:
- 'DEFAULT_FILTER'=>'htmlspecialchars,strip_tags'
那么在控制器类如果调用
- $this->_get('id');
的话,会依次对$_GET['id'] 变量进行htmlspecialchars和strip_tags方法过滤后返回结果。
下面调用方式也同样支持:
- $this->_get('id','htmlspecialchars,strip_tags',0);
其他变量获取方法用法相同。
支持获取全部变量,例如:
- $this->_get();
表示获取$_GET变量值。
支持不过滤处理
如果不希望过滤某个参数,可以使用
- $this->_get('id',false);
- $this->_post('id',false);
- //或者
- $this->_get('id','');
- $this->_post('id','');
第二个参数使用false或者空字符串则表示不作任何过滤处理,即使我们有配置默认的过滤方法。
如果我们忽略第二个参数调用的话
- $this->_get('id');
- $this->_post('id');
则表示调用默认的过滤方法(由DEFAULT_FILTER参数进行配置)。
3.1版本开始,Action类增加_param方法,可以自动根据当前请求类型(例如GET POST)获取参数。
例如:
- $this->_param('id');
当前为get方式提交的时候,就是获取$_GET['id'](进行默认过滤后)的值
当前为post方式提交的时候,就是获取$_POST['id'](进行默认过滤后)的值
还可以用_param方法获取URL中的参数
- $this->_param(0); // 获取PATHINFO地址中的第一个参数
- $this->_param(2); // 获取PATHINFO地址中的第3个参数
15. 判断请求类型
系统Action类内置了一些判断方法用于判断请求类型,包括:
方法 | 说明 |
---|---|
isGet | 判断是否是GET方式提交 |
isPost | 判断是否是POST方式提交 |
isPut | 判断是否是PUT方式提交 |
isDelete | 判断是否是DELETE方式提交 |
isHead | 判断是否是HEAD提交 |
使用举例如下:
- class UserAction extends Action{
- public function update(){
- if ($this->isPost()){
- $User = M('User');
- $User->create();
- $User->save();
- $this->success('保存完成');
- }else{
- $this->error('非法请求');
- }
- }
- }
isAjax 是否属于AJAX提交
需要注意的是,如果使用的是ThinkAjax或者自己写的Ajax类库的话,需要在表单里面添加一个隐藏域,告诉后台属于ajax方式提交,默认的隐藏域名称是ajax(可以通过VAR_AJAX_SUBMIT配置),如果是JQUERY类库的话,则无需添加任何隐藏域即可自动判断。
16. 获取URL参数
我们可以把URL地址 News/archive/2012/01/15 按照“/”分成多个参数,$_GET["_URL_"][0] 获取的就是News,$_GET["_URL_"][1]获取的就是archive,依次类推,可以通过数字索引获取所有的URL参数。
VAR_URL_PARAMS参数,默认值是:
- 'VAR_URL_PARAMS' => '_URL_', // PATHINFO URL参数变量
我们访问
- http://serverName.com/index.php/Blog/read/2012/03
$GET['_URL_'][2] 获取参数,表示获取PATH_INFO的URL参数
Blog/read/2012/03中的第3个参数(数组索引从0开始)
- $year = $GET['_URL_'][2]; // 2012
- $month = $GET['_URL_'][3]; // 03
_param方法的用法同_get和_post等方法,区别在于,_param方法能够自动根据当前请求类型自动获取相应的参数,例如:
如果当前是get请求方式,
- $this->_param('id');
当采用POST请求方式的时候,同样的代码将会返回$_POST['id']的处理数据
如果采用的是PUT请求,那么会自动返回PUT的处理数据,而无需开发人员进行判断。
并且需要注意的是,无论是什么方式的请求,系统都可以支持URL参数的获取,如果C('VAR_URL_PARAMS')设置不为空的话,就可以使用:
- $this->_param(1);
- $this->_param(2);
- $year = $this->_param(2);
- $month = $this->_param(3);
这样的好处是可以不需要使用路由功能就可以获取某个不规则的URL地址中的参数。
17. AJAX返回
系统支持任何的AJAX类库,Action类提供了ajaxReturn方法用于AJAX调用后返回数据给客户端。并且支持JSON、XML和EVAL三种方式给客户端接受数据,通过配置DEFAULT_AJAX_RETURN进行设置,默认配置采用JSON格式返回数据,在选择不同的AJAX类库的时候可以使用不同的方式返回数据。要使用ThinkPHP的ajaxReturn方法返回数据的话,需要遵守一定的返回数据的格式规范。
status | 操作状态 |
info | 提示信息 |
data | 返回数据 |
- $this->ajaxReturn(返回数据,提示信息,操作状态);
- $User = M("User"); // 实例化User对象
- $result = $User->add($data);
- if ($result){
- // 成功后返回客户端新增的用户ID,并返回提示信息和操作状态
- $this->ajaxReturn($result,"新增成功!",1);
- }else{
- // 错误后返回错误的操作状态和提示信息
- $this->ajaxReturn(0,"新增错误!",0);
- }
在客户端接受数据的时候,根据使用的编码格式进行解析即可。
如果需要改变Ajax返回的数据格式,可以在控制器Action中增加ajaxAssign方法定义,定义格式如下:
- public function ajaxAssign(&$result) {
- // 返回数据中增加url属性
- $result['url'] = $this->url;
- }
3.1版本以后,ajaxReturn方法可以更加灵活的进行ajax传值,并且废弃了ajaxAssign方法扩展。能够完全定义传值的数组和类型,例如:
- $data['status'] = 1;
- $data['info'] = 'info';
- $data['size'] = 9;
- $data['url'] = $url;
- $this->ajaxReturn($data,'JSON');
data传值数组可以随意定义。
改进后的ajaxReturn方法也兼容之前的写法:
- $this->ajaxReturn($data,'info',1);
系统会自动把info和1两个参数并入$data数组中,等同于赋值
- $data['info'] = 'info';
- $data['status'] = 1;
18. Action参数绑定
http://doc.thinkphp.cn/manual/action_param_bind.html
19. 多层控制器支持
3.1版本开始,控制器支持自定义分层。同时A方法增加第二个参数layer,用于设置控制器分层。例如
- A('User','Event');
UserEvent如果继承Action类的话,可以使用Action类所有的功能。
- A('User','Api');
分层控制器仅用于内部调用,URL访问的控制器还是Action层,但是可以配置
DEFAULT_C_LAYER修改默认控制器层名称(该参数默认值为Action)。
模型:
模型名(类名) | 约定对应数据表(假设数据库的前缀定义是 think_) |
---|---|
UserModel | think_user |
UserTypeModel | think_user_type |
属性 | 说明 |
---|---|
tableName | 不包含表前缀的数据表名称,一般情况下默认和模型名称相同,只有当你的表名和当前的模型类的名称不同的时候才需要定义。 |
trueTableName | 包含前缀的数据表名称,也就是数据库中的实际表名,该名称无需设置,只有当上面的规则都不适用的情况或者特殊情况下才需要设置。 |
dbName | 定义模型当前对应的数据库名称,只有当你当前的模型类对应的数据库名称和配置文件不同的时候才需要定义。 |
1. 模型实例化
在没有定义任何模型的时候,我们可以使用下面的方法实例化一个模型类来进行操作:
- //实例化User模型
- $User = new Model('User');
- //或者使用M()快捷方法实例化,和上面的方法是等效的
- $User = M('User');
- //执行其他的数据操作
- $User->select();
- $User = new CommonModel('User');
模型类的实例化方法有三个参数,第一个参数是模型名称,第二个参数用于设置数据表的前缀(留空则取当前项目配置的表前缀),第三个参数用于设置当前使用的数据库连接信息(留空则取当前项目配置的数据库连接信息),例如:
- $User = new CommonModel('User','think_','db_config');
- $User = M('CommonModel:User','think_','db_config');
这种情况是使用的最多的,一个项目不可避免的需要定义自身的业务逻辑实现,就需要针对每个数据表定义一个模型类,例如UserModel 、InfoModel等等。
定义的模型类通常都是放到项目的Lib\Model目录下面。例如,
- class UserModel extends Model{
- public function getTopUser(){
- //添加自己的业务逻辑
- // ...
- }
- }
- //实例化自定义模型
- $User = new UserModel();
- //或者使用D快捷方法
- $User = D('User');
- //执行具体的数据操作
- $User->select();
D方法还可以支持跨项目和分组调用,需要使用:
- //实例化Admin项目的User模型
- D('Admin://User')
- //实例化Admin分组的User模型
- D('Admin/User')
2. 字段定义
字段缓存保存在Runtime/Data/_fields/ 目录下面,缓存机制是每个模型对应一个字段缓存文件(而并非每个数据表对应一个字段缓存文件),命名格式是:数据库名.模型名.php
- 'DB_FIELDS_CACHE'=>false
如果需要显式获取当前数据表的字段信息,可以使用模型类的getDbFields方法来获取当前数据对象的全部字段信息,例如:
- $fields = $User->getDbFields();
如果不希望依赖字段缓存或者想提高性能,也可以在模型类里面手动定义数据表字段的名称,可以避免IO加载的效率开销,在模型类里面添加fields属性即可,定义格式如下:
- class UserModel extends Model{
- protected $fields = array(
- 'id', 'username', 'email', 'age', '_pk' => 'id', '_autoinc' => true
- );
- }
3. 数据主键
ThinkPHP的默认约定每个数据表的主键名采用统一的id作为标识,并且是自动增长类型的。系统会自动识别当前操作的数据表的字段信息和主键名称,所以即使你的主键不是id,也无需进行额外的设置,系统会自动识别。要在外部获取当前数据对象的主键名称,请使用下面的方法:- $pk = $Model->getPk();
4. 属性访问
ThinkPHP的模型对象实例本身也是一个数据对象,可以支持对象和数组两种方式来访问数据属性,例如下面的方式采用数据对象的方式来访问User模型的属性:
- //实例化User模型
- $User = D('User');
- //查询用户数据
- $User->find(1);
- //获取name属性的值
- echo $User->name;
- //设置name属性的值
- $User->name = 'ThinkPHP';
除了find方法会产生数据对象属性外,data方法和create方法也会产生数据对象,例如:
- $User = D('User');
- $User->create();
- echo $User->name;
- //实例化User模型
- $User = D('User');
- //查询用户数据
- $data = $User->find(1);
- //获取name属性的值
- echo $data['name'];
- //设置name属性的值
- $data['name'] = 'ThinkPHP';
5. 跨库操作
ThinkPHP可以支持模型的同一数据库服务器的跨库操作,跨库操作只需要简单配置一个模型所在的数据库名称即可,例如,假设UserModel对应的数据表在数据库user下面,而InfoModel对应的数据表在数据库info下面,那么我们只需要进行下面的设置即可。- class UserModel extends Model {
- protected $dbName = 'user';
- }
- class InfoModel extends Model {
- protected $dbName = 'info';
- }
- $User = D('User');
- $User->select();
- echo $User->getLastSql();
- // 输出的SQL语句为 select * from user.think_user
- protected $tablePrefix = 'other_';
- $User = M('user.User','other_');
6. 连接数据库
ThinkPHP内置了抽象数据库访问层,把不同的数据库操作封装起来,我们只需要使用公共的Db类进行操作,而无需针对不同的数据库写不同的代码和底层实现,Db类会自动调用相应的数据库驱动来处理。如果应用需要使用数据库,必须配置数据库连接信息,数据库的配置文件有多种定义方式。
常用的配置方式是在项目配置文件中添加下面的参数:
- //项目配置文件
- return array(
- //数据库配置信息
- 'DB_TYPE' => 'mysql', // 数据库类型
- 'DB_HOST' => 'localhost', // 服务器地址
- 'DB_NAME' => 'thinkphp', // 数据库名
- 'DB_USER' => 'root', // 用户名
- 'DB_PWD' => '', // 密码
- 'DB_PORT' => 3306, // 端口
- 'DB_PREFIX' => 'think_', // 数据库表前缀
- //其他项目配置参数
- // ...
- );
或者采用如下配置
- 'DB_DSN' => 'mysql://username:password@localhost:3306/DbName'
注意:如果要设置分布式数据库,暂时不支持DB_DSN方式配置。
- //PDO连接方式
- 'DB_TYPE' => 'pdo', // 数据库类型
- 'DB_USER' => 'root', // 用户名
- 'DB_PWD' => '', // 密码
- 'DB_PREFIX' => 'think_', // 数据库表前缀
- 'DB_DSN' => 'mysql:host=localhost;dbname=thinkphp;charset=UTF-8'
配置文件定义的数据库连接信息一般是系统默认采用的,因为一般一个项目的数据库访问配置是相同的。该方法系统在连接数据库的时候会自动获取,无需手动连接。可以对每个项目和不同的分组定义不同的数据库连接信息,如果开启了调试模式的话,还可以在不同的应用状态的配置文件里面定义独立的数据库配置信息。
第二种 在模型类里面定义connection属性
如果在某个模型类里面定义了connection属性的话,则实例化该自定义模型的时候会采用定义的数据库连接信息,而不是配置文件中设置的默认连接信息,通常用于某些数据表位于当前数据库连接之外的其它数据库,例如:
- //在模型里单独设置数据库连接信息
- protected $connection = array(
- 'db_type' => 'mysql',
- 'db_user' => 'root',
- 'db_pwd' => '1234',
- 'db_host' => 'localhost',
- 'db_port' => '3306',
- 'db_name' => 'thinkphp'
- );
也可以采用DSN方式定义,例如:
- //或者使用DSN定义
- protected $connection = 'mysql://root:1234@localhost:3306/thinkphp';
如果我们已经在配置文件中配置了额外的数据库连接信息,例如:
- //数据库配置1
- 'DB_CONFIG1' => array(
- 'db_type' => 'mysql',
- 'db_user' => 'root',
- 'db_pwd' => '1234',
- 'db_host' => 'localhost',
- 'db_port' => '3306',
- 'db_name' => 'thinkphp'
- ),
- //数据库配置2
- 'DB_CONFIG2' => 'mysql://root:1234@localhost:3306/thinkphp';
那么,我们可以把模型类的属性定义改为:
- //调用配置文件中的数据库配置1
- protected $connection = 'DB_CONFIG1';
- //调用配置文件中的数据库配置2
- protected $connection = 'DB_CONFIG2';
- $User = M('User','other_','mysql://root:1234@localhost/demo');
- $User = M('User','other_','DB_CONFIG2');
- $User = M('User',Null,'DB_CONFIG2');
7. 切换数据库
只需要调用Model类的db方法,用法:
- Model->db("数据库编号","数据库配置");
数据库编号用数字格式,对于已经调用过的数据库连接,是不需要再传入数据库连接信息的,系统会自动记录。对于默认的数据库连接,内部的数据库编号是0,因此为了避免冲突,请不要再次定义数据库编号为0的数据库配置。
数据库配置的定义方式和模型定义connection属性一样,支持数组、字符串以及调用配置参数三种格式。
Db方法调用后返回当前的模型实例,直接可以继续进行模型的其他操作,所以该方法可以在查询的过程中动态切换,例如:
- $this->db(1,"mysql://root:123456@localhost:3306/test")->query("查询SQL");
该方法添加了一个编号为1的数据库连接,并自动切换到当前的数据库连接。
当第二次切换到相同的数据库的时候,就不需要传入数据库连接信息了,可以直接使用:
- $this->db(1)->query("查询SQL");
如果需要切换到默认的数据库连接,只需要调用:
- $this->db(0);
如果我们已经在项目配置中定义了其他的数据库连接信息,例如:
- //数据库配置1
- 'DB_CONFIG1' = array(
- 'db_type' => 'mysql',
- 'db_user' => 'root',
- 'db_pwd' => '1234',
- 'db_host' => 'localhost',
- 'db_port' => '3306',
- 'db_name' => 'thinkphp'
- ),
- //数据库配置2
- 'DB_CONFIG2' => 'mysql://root:1234@localhost:3306/thinkphp';
我们就可以直接在db方法中调用配置进行连接了:
- $this->db(1,"DB_CONFIG1")->query("查询SQL");
- $this->db(2,"DB_CONFIG2")->query("查询SQL");
如果切换数据库之后,数据表和当前不一致的话,可以使用table方法指定要操作的数据表:
- $this->db(1)->table("top_user")->find();
我们也可以直接用M方法切换数据库,例如:
- M("User","think_","mysql://root:123456@localhost:3306/test")->query("查询SQL");
或者
- M("User","think_","DB_CONFIG1")->query("查询SQL");
8. 分布式数据库
ThinkPHP内置了分布式数据库的支持,包括主从式数据库的读写分离,但是分布式数据库必须是相同的数据库类型。配置DB_DEPLOY_TYPE 为1 可以采用分布式数据库支持。如果采用分布式数据库,定义数据库配置信息的方式如下:
- //在项目配置文件里面定义
- return array(
- //分布式数据库配置定义
- 'DB_TYPE' => 'mysql', //分布式数据库类型必须相同
- 'DB_HOST' => '192.168.0.1,192.168.0.2',
- 'DB_NAME' => 'thinkphp', //如果相同可以不用定义多个
- 'DB_USER' => 'user1,user2',
- 'DB_PWD' => 'pwd1,pwd2',
- 'DB_PORT' => '3306',
- 'DB_PREFIX' => 'think_',
- //其他配置参数
- // ...
- );
- 'DB_RW_SEPARATE'=>true,
CURD操作系统会自动判断当前执行的方法的读操作还是写操作,如果你用的是原生SQL,那么需要注意系统的默认规则:
写操作必须用模型的execute方法,读操作必须用模型的query方法,否则会发生主从读写错乱的情况。
注意:主从数据库的数据同步工作不在框架实现,需要数据库考虑自身的同步或者复制机制。
9. 创建数据
在进行数据操作之前,我们往往需要手动创建需要的数据,例如对于提交的表单数据:
- // 获取表单的POST数据
- $data['name'] = $_POST['name'];
- $data['email'] = $_POST['email'];
- // 更多的表单数据值获取
- //……
很简单的例子:
- // 实例化User模型
- $User = M('User');
- // 根据表单提交的POST数据创建数据对象
- $User->create();
- // 把创建的数据对象写入数据库
- $User->add();
Create方法支持从其它方式创建数据对象,例如,从其它的数据对象,或者数组等
- $data['name'] = 'ThinkPHP';
- $data['email'] = '[email protected]';
- $User->create($data);
甚至还可以支持从对象创建新的数据对象
- // 从User数据对象创建新的Member数据对象
- $User = M("User");
- $User->find(1);
- $Member = M("Member");
- $Member->create($User);
- $User = M('User');
- $User->create(); //创建User数据对象
- $User->status = 1; // 设置默认的用户状态
- $User->create_time = time(); // 设置用户的创建时间
- $User->add(); // 把用户对象写入数据库
使用如下:
- // 实例化User模型
- $User = M('User');
- // 创建数据后写入到数据库
- $data['name'] = 'ThinkPHP';
- $data['email'] = '[email protected]';
- $User->data($data)->add();
安全提示:
create方法如果没有传值,默认取$_POST数据,如果用户提交的变量内容,含有可执行的html代码,请进行手工过滤。
- $_POST['title'] = "";
非法html代码可以使用htmlspecialchars进行编码,以防止用户提交的html代码在展示时被执行,以下是两种安全处理方法。
- $_POST['title'] = htmlspecialchars($_POST['title']);
- M('User')->create();
- $data['title'] = $this->_post('title', 'htmlspecialchars');
- M('User')->create($data);
- Class UserModel extends Model{
- protected $_map = array(
- 'name' =>'username', // 把表单中name映射到数据表的username字段
- 'mail' =>'email', // 把表单中的mail映射到数据表的email字段
- );
- }
- // 实例化User模型
- $User = M('User');
- $data = $User->find(3);
- $data = $User->parseFieldsMap($data);
11. 连贯操作
- $User->where('status=1')->order('create_time')->limit(10)->select();
- $User->order('create_time')->limit(10)->where('status=1')->select();
- $User->select(array('order'=>'create_time','where'=>'status=1','limit'=>'10'));
- $User->where('id=1')->field('id,name,email')->find();
- $User->where('status=1 and id=1')->delete();
系统支持的连贯操作方法有:
连贯操作 | 作用 | 支持的参数类型 |
---|---|---|
where | 用于查询或者更新条件的定义 | 字符串、数组和对象 |
table | 用于定义要操作的数据表名称 | 字符串和数组 |
alias | 用于给当前数据表定义别名 | 字符串 |
data | 用于新增或者更新数据之前的数据对象赋值 | 数组和对象 |
field | 用于定义要查询的字段(支持字段排除) | 字符串和数组 |
order | 用于对结果排序 | 字符串和数组 |
limit | 用于限制查询结果数量 | 字符串和数字 |
page | 用于查询分页(内部会转换成limit) | 字符串和数字 |
group | 用于对查询的group支持 | 字符串 |
having | 用于对查询的having支持 | 字符串 |
join* | 用于对查询的join支持 | 字符串和数组 |
union* | 用于对查询的union支持 | 字符串、数组和对象 |
distinct | 用于查询的distinct支持 | 布尔值 |
lock | 用于数据库的锁机制 | 布尔值 |
cache | 用于查询缓存 | 支持多个参数 |
relation | 用于关联查询(需要关联模型支持) | 字符串 |
12. CURD操作
创建(Create)
在ThinkPHP中使用add方法新增数据到数据库(而并不是create方法)。
- $User = M("User"); // 实例化User对象
- $data['name'] = 'ThinkPHP';
- $data['email'] = '[email protected]';
- $User->add($data);
- $User->data($data)->add();
- $User = M("User"); // 实例化User对象
- // 根据表单提交的POST数据创建数据对象
- $User->create();
- $User->add(); // 根据条件保存修改的数据
读取(Read)
在ThinkPHP中读取数据的方式很多,通常分为读取数据和读取数据集。
- $User = M("User"); // 实例化User对象
- // 查找status值为1的用户数据 以创建时间排序 返回10条数据
- $list = $User->where('status=1')->order('create_time')->limit(10)->select();
- $User = M("User"); // 实例化User对象
- // 查找status值为1name值为think的用户数据
- $User->where('status=1 AND name="think"')->find();
- $User = M("User"); // 实例化User对象
- // 获取ID为3的用户的昵称
- $nickname = $User->where('id=3')->getField('nickname');
如果需要返回数组,可以用:
- $this->getField('id',true); // 获取id数组
- $User = M("User"); // 实例化User对象
- // 获取所有用户的ID和昵称列表
- $list = $User->getField('id,nickname');
如果传入多个字段的名称,例如:
- $list = $User->getField('id,nickname,email');
如果我们传入一个字符串分隔符:
- $list = $User->getField('id,nickname,email',':');
getField方法的sepa参数还可以支持限制数量,例如:
- $this->getField('id,name',5); // 限制返回5条记录
- $this->getField('id',3); // 获取id数组 限制3条记录
更新(Update)
在ThinkPHP中使用save方法更新数据库,并且也支持连贯操作的使用。
- $User = M("User"); // 实例化User对象
- // 要修改的数据对象属性赋值
- $data['name'] = 'ThinkPHP';
- $data['email'] = '[email protected]';
- $User->where('id=5')->save($data); // 根据条件保存修改的数据
为了保证数据库的安全,避免出错更新整个数据表,如果没有任何更新条件,数据对象本身也不包含主键字段的话,save方法不会更新任何数据库的记录。
因此下面的代码不会更改数据库的任何记录
- $User->save($data);
除非使用下面的方式:
- $User = M("User"); // 实例化User对象
- // 要修改的数据对象属性赋值
- $data['id'] = 5;
- $data['name'] = 'ThinkPHP';
- $data['email'] = '[email protected]';
- $User->save($data); // 根据条件保存修改的数据
如果id是数据表的主键的话,系统自动会把主键的值作为更新条件来更新其他字段的值。
还有一种方法是通过create或者data方法创建要更新的数据对象,然后进行保存操作,这样save方法的参数可以不需要传入。
- $User = M("User"); // 实例化User对象
- // 要修改的数据对象属性赋值
- $data['name'] = 'ThinkPHP';
- $data['email'] = '[email protected]';
- $User->where('id=5')->data($data)->save(); // 根据条件保存修改的数据
使用create方法的例子:
- $User = M("User"); // 实例化User对象
- // 根据表单提交的POST数据创建数据对象
- $User->create();
- $User->save(); // 根据条件保存修改的数据
使用示例:
- $User = M("User"); // 实例化User对象
- // 更改用户的name值
- $User-> where('id=5')->setField('name','ThinkPHP');
setField方法支持同时更新多个字段,只需要传入数组即可,例如:
- $User = M("User"); // 实例化User对象
- // 更改用户的name和email的值
- $data = array('name'=>'ThinkPHP','email'=>'[email protected]');
- $User-> where('id=5')->setField($data);
- $User = M("User"); // 实例化User对象
- $User->where('id=5')->setInc('score',3); // 用户的积分加3
- $User->where('id=5')->setInc('score'); // 用户的积分加1
- $User->where('id=5')->setDec('score',5); // 用户的积分减5
- $User->where('id=5')->setDec('score'); // 用户的积分减1
删除(Delete)
在ThinkPHP中使用delete方法删除数据库中的记录。
示例如下:
- $User = M("User"); // 实例化User对象
- $User->where('id=5')->delete(); // 删除id为5的用户数据
- $User->where('status=0')->delete(); // 删除所有状态为0的用户数据
delete方法可以用于删除单个或者多个数据,主要取决于删除条件,也就是where方法的参数,也可以用order和limit方法来限制要删除的个数,例如:
- // 删除所有状态为0的5 个用户数据 按照创建时间排序
- $User->where('status=0')->order('create_time')->limit('5')->delete();
13. ActiveRecord
ThinkPHP实现了ActiveRecords模式的ORM模型,采用了非标准的ORM模型:表映射到类,记录映射到对象。
一、创建数据
- $User = M("User"); // 实例化User对象
- // 然后直接给数据对象赋值
- $User->name = 'ThinkPHP';
- $User->email = '[email protected]';
- // 把数据对象添加到数据库
- $User->add();
如果使用了create方法创建数据对象的话,仍然可以在创建完成后进行赋值
- $User = D("User");
- $User->create(); // 创建User数据对象,默认通过表单提交的数据进行创建
- // 增加或者更改其中的属性
- $User->status = 1;
- $User->create_time = time();
- // 把数据对象添加到数据库
- $User->add();
- $User = M("User"); // 实例化User对象
- // 查找id为8的用户数据
- $User->where('id=8')->find();
用AR模式的话可以直接写成:
- $User->find(8);
如果要根据某个字段查询,例如查询姓名为ThinkPHP的可以用:
- $User = M("User"); // 实例化User对象
- $User->getByName("ThinkPHP");
如果要查询数据集,可以直接使用:
- // 查找主键为1、3、8的多个数据
- $userList = $User->select('1,3,8');
三、更新记录
在完成查询后,可以直接修改数据对象然后保存到数据库。
- $User->find(1); // 查找主键为1的数据
- $User->name = 'TOPThink'; // 修改数据对象
- $User->save(); // 保存当前数据对象
上面这种方式仅仅是示例,不代表保存操作之前一定要先查询。因为下面的方式其实是等效的:
- $User->id = 1;
- $User->name = 'TOPThink'; // 修改数据对象
- $User->save(); // 保存当前数据对象
四、删除记录
可以删除当前查询的数据对象
- $User->find(2);
- $User->delete(); // 删除当前的数据对象
或者直接根据主键进行删除
- $User->delete(8); // 删除主键为8的数据
- $User->delete('5,6'); // 删除主键为5、6的多个数据
14. 自动验证
大多数情况下面,数据对象是由表单提交的$_POST数据创建。需要使用系统的自动验证功能,只需要在Model类里面定义$_validate属性,是由多个验证因子组成的二维数组。
验证因子格式:
- array(验证字段,验证规则,错误提示,[验证条件,附加规则,验证时间])
示例:
- protected $_validate = array(
- array('verify','require','验证码必须!'), //默认情况下用正则进行验证
- array('name','','帐号名称已经存在!',0,'unique',1), // 在新增的时候验证name字段是否唯一
- array('value',array(1,2,3),'值的范围不正确!',2,'in'), // 当值不为空的时候判断是否在一个范围内
- array('repassword','password','确认密码不正确',0,'confirm'), // 验证确认密码是否和密码一致
- array('password','checkPwd','密码格式不正确',0,'function'), // 自定义函数验证密码格式
- );
当使用系统的create方法创建数据对象的时候会自动进行数据验证操作,代码示例:
- $User = D("User"); // 实例化User对象
- if (!$User->create()){
- // 如果创建失败 表示验证没有通过 输出错误提示信息
- exit($User->getError());
- }else{
- // 验证通过 可以进行其他数据操作
- }
通常来说,每个数据表对应的验证规则是相对固定的,但是有些特殊的情况下面可能会改变验证规则,我们可以动态的改变验证规则来满足不同条件下面的验证:
- $User = D("User"); // 实例化User对象
- $validate = array(
- array('verify','require','验证码必须!'), // 仅仅需要进行验证码的验证
- );
- $User-> setProperty("_validate",$validate);
- $result = $User->create();
- if (!$result){
- // 如果创建失败 表示验证没有通过 输出错误提示信息
- exit($User->getError());
- }else{
- // 验证通过 可以进行其他数据操作
- }
自动验证功能中的function和callback规则可以支持多字段。
例子:
- protected $_validate = array(
- array('user_id,good_id', 'checkIfOrderToday', '今天已经购买过,请明天再来', 1,'callback', 1),
- );
- protected function checkIfOrderToday($data){
- $map = $data;
- $map['ctime'] = array(array('gt',[开始时间]), array('lt', [结束时间]));
- if($this->where($map)->find())
- return false;
- else
- return true;
- }
新版支持数据的批量验证功能,只需要在模型类里面设置patchValidate属性为true( 默认为false),设置批处理验证后,getError() 方法返回的错误信息是一个数组,返回格式是:
- array("字段名1"=>"错误提示1","字段名2"=>"错误提示2"... )
手动验证
3.1版本开始,可以使用validate方法实现动态和批量手动验证,例如:
- $this->validate($validate)->create();
其中$validate变量的规范和_validate属性的定义规则一致,而且还可以支持函数调用(由于PHP本身的限制,在类的属性定义中不能调用函数)。
通过这一改进,以前需要支持数据自动验证,必须定义模型类的情况已经不再出现,你完全可以通过M方法实例化模型类后使用动态设置完成自动验证操作。
另外还有一个check方法,用于对单个数据的手动验证,支持部分自动验证的规则,用法如下:
- check('验证数据','验证规则','验证类型')
验证类型支持 in between equal length regex expire ip_allow ip_deny,默认为regex
结果返回布尔值
- $model->check($value,'email');
- $model->check($value,'1,2,3','in');
15. 命名范围
首先定义_scope属性:- class NewsModel extends Model {
- protected $_scope = array(
- // 命名范围normal
- 'normal'=>array(
- 'where'=>array('status'=>1),
- ),
- // 命名范围latest
- 'latest'=>array(
- 'order'=>'create_time DESC',
- 'limit'=>10,
- ),
- );
- }
调用某个命名范围
最简单的调用方式就直接调用某个命名范围,例如:
- $Model->scope('normal')->select();
- $Model->scope('latest')->select();
生成的SQL语句分别是:
- SELECT * FROM think_news WHERE status=1
- SELECT * FROM think_news ORDER BY create_time DESC LIMIT 10
调用多个命名范围
也可以支持同时调用多个命名范围定义,例如:- $Model->scope('normal')->scope('latest')->select();
- $Model->scope('normal,latest')->select();
- SELECT * FROM think_news WHERE status=1 ORDER BY create_time DESC LIMIT 10
默认命名范围
系统支持默认命名范围功能,如果你定义了一个default命名范围,例如:
- protected $_scope = array(
- // 默认的命名范围
- 'default'=>array(
- 'where'=>array('status'=>1),
- 'limit'=>10,
- ),
- );
那么调用default命名范围可以直接使用:
- $Model->scope()->select();
命名范围调整
如果你需要在normal命名范围的基础上增加额外的调整,可以使用:
- $Model->scope('normal',array('limit'=>5))->select();
生成的SQL语句是:
- SELECT * FROM think_news WHERE status=1 LIMIT 5
当然,也可以在两个命名范围的基础上进行调整,例如:
- $Model->scope('normal,latest',array('limit'=>5))->select();
生成的SQL是:
- SELECT * FROM think_news WHERE status=1 ORDER BY create_time DESC LIMIT 5
自定义命名范围
又或者,干脆不用任何现有的命名范围,我直接传入一个命名范围:- $Model->scope(array('field'=>'id,title','limit'=>5,'where'=>'status=1','order'=>'create_time DESC'))->select();
- SELECT id,title FROM think_news WHERE status=1 ORDER BY create_time DESC LIMIT 5
与连贯操作混合使用
命名范围一样可以和之前的连贯操作混合使用,例如定义了命名范围_scope属性:- protected $_scope = array(
- 'normal'=>array(
- 'where'=>array('status'=>1),
- 'field'=>'id,title',
- 'limit'=>10,
- ),
- );
- $Model->scope('normal')->limit(8)->order('id desc')->select();
- SELECT id,title FROM think_news WHERE status=1 ORDER BY id desc LIMIT 8
如果是这样调用:
- $Model->limit(8)->scope('normal')->order('id desc')->select();
- SELECT id,title FROM think_news WHERE status=1 ORDER BY id desc LIMIT 10
16. 自动完成
在Model类定义 $_auto 属性,可以完成数据自动处理功能,用来处理默认值、数据过滤以及其他系统写入字段。$_auto属性是由多个填充因子组成的数组。
填充因子格式:
- array(填充字段,填充内容,[填充条件,附加规则])
- protected $_auto = array (
- array('status','1'), // 新增的时候把status字段设置为1
- array('password','md5',1,'function') , // 对password字段在新增的时候使md5函数处理
- array('name','getName',1,'callback'), // 对name字段在新增的时候回调getName方法
- array('create_time','time',2,'function'), // 对create_time字段在更新的时候写入当前时间戳
- );
和自动验证一样,自动完成机制需要使用create方法才能生效。并且,也可以在操作方法中动态的更改自动完成的规则。
- $auto = array (
- array('password','md5',1,'function') // 对password字段在新增的时候使md5函数处理
- );
- $User-> setProperty("_auto",$auto);
- $User->create();
还可以使用auto方法动态设置自动完成规则,例如:
- $this->auto($auto)->create();
通过这一改进,以前需要支持数据自动完成,必须定义模型类的情况已经不再出现,你完全可以通过M方法实例化模型类后使用动态设置完成自动完成操作。
17. 查询语言
查询方式
ThinkPHP可以支持直接使用字符串作为查询条件,但是大多数情况推荐使用索引数组或者对象来作为查询条件,因为会更加安全。
一、使用字符串作为查询条件
这是最传统的方式,但是安全性不高,例如:
- $User = M("User"); // 实例化User对象
- $User->where('type=1 AND status=1')->select();
最后生成的SQL语句是
SELECT * FROM think_user WHERE type=1 AND status=1
- $User = M("User"); // 实例化User对象
- $condition['name'] = 'thinkphp';
- $condition['status'] = 1;
- // 把查询条件传入查询方法
- $User->where($condition)->select();
SELECT * FROM think_user WHERE `name`='thinkphp' AND status=1
如果进行多字段查询,那么字段之间的默认逻辑关系是 逻辑与 AND,但是用下面的规则可以更改默认的逻辑判断,通过使用 _logic 定义查询逻辑:
- $User = M("User"); // 实例化User对象
- $condition['name'] = 'thinkphp';
- $condition['account'] = 'thinkphp';
- $condition['_logic'] = 'OR';
- // 把查询条件传入查询方法
- $User->where($condition)->select();
SELECT * FROM think_user WHERE `name`='thinkphp' OR `account`='thinkphp'
- $User = M("User"); // 实例化User对象
- // 定义查询条件
- $condition = new stdClass();
- $condition->name = 'thinkphp';
- $condition->status= 1;
- $User->where($condition)->select();
SELECT * FROM think_user WHERE `name`='thinkphp' AND status=1
使用对象方式查询和使用数组查询的效果是相同的,并且是可以互换的,大多数情况下,我们建议采用数组方式更加高效,后面我们会以数组方式为例来讲解具体的查询语言用法。
表达式查询
上面的查询条件仅仅是一个简单的相等判断,可以使用查询表达式支持更多的SQL查询语法,并且可以用于数组或者对象方式的查询(下面仅以数组方式为例说明),查询表达式的使用格式:$map['字段名'] = array('表达式','查询条件');
表达式不分大小写,支持的查询表达式有下面几种,分别表示的含义是:
表达式 | 含义 |
---|---|
EQ | 等于(=) |
NEQ | 不等于(<>) |
GT | 大于(>) |
EGT | 大于等于(>=) |
LT | 小于(<) |
ELT | 小于等于(<=) |
LIKE | 模糊查询 |
[NOT] BETWEEN | (不在)区间查询 |
[NOT] IN | (不在)IN 查询 |
EXP | 表达式查询,支持SQL语法 |
快捷查询
新版增加了快捷查询方式,可以进一步简化查询条件的写法,例如:一、实现不同字段相同的查询条件
- $User = M("User"); // 实例化User对象
- $map['name|title'] = 'thinkphp';
- // 把查询条件传入查询方法
- $User->where($map)->select();
二、实现不同字段不同的查询条件
- $User = M("User"); // 实例化User对象
- $map['status&title'] =array('1','thinkphp','_multi'=>true);
- // 把查询条件传入查询方法
- $User->where($map)->select();
$map['status&score&title'] =array('1',array('gt','0'),'thinkphp','_multi'=>true);
查询条件就变成 status= 1 AND score >0 AND title = 'thinkphp'
注意:快捷查询方式中“|”和“&”不能同时使用。
区间查询
ThinkPHP支持对某个字段的区间查询,例如:- $map['id'] = array(array('gt',1),array('lt',10)) ;
- $map['id'] = array(array('gt',3),array('lt',10), 'or') ;
- $map['id'] = array(array('neq',6),array('gt',3),'and');
最后一个可以是AND、 OR或者 XOR运算符,如果不写,默认是AND运算。
组合查询
组合查询的主体还是采用数组方式查询,只是加入了一些特殊的查询支持,包括字符串模式查询(_string)、复合查询(_complex)、请求字符串查询(_query),混合查询中的特殊查询每次查询只能定义一个,由于采用数组的索引方式,索引相同的特殊查询会被覆盖。
数组条件还可以和字符串条件混合使用,例如:
- $User = M("User"); // 实例化User对象
- $map['id'] = array('neq',1);
- $map['name'] = 'ok';
- $map['_string'] = 'status=1 AND score>10';
- $User->where($map)->select();
( `id` != 1 ) AND ( `name` = 'ok' ) AND ( status=1 AND score>10 )
请求字符串查询是一种类似于URL传参的方式,可以支持简单的条件相等判断。
- $map['id'] = array('gt','100');
- $map['_query'] = 'status=1&score=100&_logic=or';
- $where['name'] = array('like', '%thinkphp%');
- $where['title'] = array('like','%thinkphp%');
- $where['_logic'] = 'or';
- $map['_complex'] = $where;
- $map['id'] = array('gt',1);
( id > 1) AND ( ( name like '%thinkphp%') OR ( title like '%thinkphp%') )
统计查询
方法 | 说明 |
---|---|
Count | 统计数量,参数是要统计的字段名(可选) |
Max | 获取最大值,参数是要统计的字段名(必须) |
Min | 获取最小值,参数是要统计的字段名(必须) |
Avg | 获取平均值,参数是要统计的字段名(必须) |
Sum | 获取总分,参数是要统计的字段名(必须) |
- $User = M("User"); // 实例化User对象
- $userCount = $User->count();
- $userCount = $User->count("id");
- $maxScore = $User->max('score');
- $minScore = $User->where('score>0')->min('score');
- $avgScore = $User->avg('score');
- $sumScore = $User->sum('score');
定位查询
ThinkPHP支持定位查询,但是要求当前模型必须继承高级模型类才能使用,可以使用getN方法直接返回查询结果中的某个位置的记录。例如:
获取符合条件的第3条记录:
- $User->where('score>0')->order('score desc')->getN(2);
获取符合条件的最后第二条记录:
- $User-> where('score>80')->order('score desc')->getN(-2);
获取第一条记录:
- $User->where('score>80')->order('score desc')->first();
获取最后一条记录:
- $User->where('score>80')->order('score desc')->last();
SQL查询
query 执行SQL查询操作 | |
---|---|
用法 | query($sql,$parse=false) |
参数 | query(必须):要查询的SQL语句 parse(可选):是否需要解析SQL |
返回值 | 如果数据非法或者查询错误则返回false 否则返回查询结果数据集(同select方法) |
- $Model = new Model() // 实例化一个model对象 没有对应任何数据表
- $Model->query("select * from think_user where status=1");
execute用于更新和写入数据的sql操作 | |
---|---|
用法 | execute($sql,$parse=false) |
参数 | query(必须):要执行的SQL语句 parse(可选):是否需要解析SQL |
返回值 | 如果数据非法或者查询错误则返回false 否则返回影响的记录数 |
- $Model = new Model() // 实例化一个model对象 没有对应任何数据表
- $Model->execute("update think_user set name='thinkPHP' where status=1");
自动获取当前表名
通常使用原生SQL需要手动加上当前要查询的表名,如果你的表名以后会变化的话,那么就需要修改每个原生SQL查询的sql语句了,针对这个情况,系统还提供了一个小的技巧来帮助解决这个问题。
例如:
- $model = M("User");
- $model->query('select * from __TABLE__ where status>1');
支持连贯操作和SQL解析
新版对query和execute两个原生SQL操作方法增加第二个参数支持, 表示是否需要解析SQL (默认为false 表示直接执行sql ),如果设为true 则会解析SQL中的特殊字符串 (需要配合连贯操作)。
例如,支持 如下写法:
- $model->table("think_user")
- ->where(array("name"=>"thinkphp"))
- ->field("id,name,email")
- ->query('select %FIELD% from %TABLE% %WHERE%',true);
其中query方法中的%FIELD%、%TABLE%和%WHERE%字符串会自动替换为同名的连贯操作方法的解析结果SQL,支持的替换字符串包括:
替换字符串 | 对应连贯操作方法 |
---|---|
%FIELD% | field |
%TABLE% | table |
%DISTINCT% | distinct |
%WHERE% | where |
%JOIN% | join |
%GROUP% | group |
%HAVING% | having |
%ORDER% | order |
%LIMIT% | limit |
%UNION% | union |
动态查询
借助PHP5语言的特性,ThinkPHP实现了动态查询,包括下面几种:
方法名 | 说明 | 举例 |
---|---|---|
getBy | 根据某个字段的值查询数据 | 例如,getByName,getByEmail |
getFieldBy | 根据某个字段查询并返回某个字段的值 | 例如,getFieldByName |
top | 获取前多少条记录(需要高级模型支持) | 例如,top8,top12 |
该查询方式针对数据表的字段进行查询。例如,User对象拥有id,name,email,address 等属性,那么我们就可以使用下面的查询方法来直接根据某个属性来查询符合条件的记录。
- $user = $User->getByName('liu21st');
- $user = $User->getByEmail('[email protected]');
- $user = $User->getByAddress('中国深圳');
针对某个字段查询并返回某个字段的值,例如
- $user = $User->getFieldByName('liu21st','id');
三、top动态查询
ThinkPHP还提供了另外一种动态查询方式,就是获取符合条件的前N条记录(和定位查询一样,也要求当前模型类必须继承高级模型类后才能使用)。例如,我们需要获取当前用户中积分大于0,积分最高的前5位用户 :
- $User-> where('score>80')->order('score desc')->top5();
要获取积分的前8位可以改成:
- $User-> where('score>80')->order('score desc')->top8();
子查询
新版新增了子查询支持,有两种使用方式:1、使用select方法
当select方法的参数为false的时候,表示不进行查询只是返回构建SQL,例如:
- // 首先构造子查询SQL
- $subQuery = $model->field('id,name')->table('tablename')->group('field')->where($where)->order('status')->select(false);
- $subQuery = $model->field('id,name')->table('tablename')->group('field')->where($where)->order('status')->buildSql();
- // 利用子查询进行查询
- $model->table($subQuery.' a')->where()->order()->select()
18. 查询锁定
ThinkPHP支持查询或者更新的锁定,只需要在查询或者更新之前使用lock方法即可。
查询锁定使用:
- $list = $User->lock(true)->where('status=1')->order('create_time')->limit(10)->select();
更新锁定使用:
- $list = $User->lock(true)->where('status=1')->data($data)->save();
19. 字段排除
当使用下面的字段排除方式查询的时候- $Model->field('create_time,read_count,comment_count',true);
生成的SQL语句就变成了SELECT id,name,title,status FROM article
20. 事务支持
ThinkPHP提供了单数据库的事务支持,如果要在应用逻辑中使用事务。事务是针对数据库本身的,所以可以跨模型操作的 。
- // 在User模型中启动事务
- $User->startTrans();
- // 进行相关的业务逻辑操作
- $Info = M("Info"); // 实例化Info对象
- $Info->save($User); // 保存用户信息
- if (操作成功){
- // 提交事务
- $User->commit();
- }else{
- // 事务回滚
- $User->rollback();
- }
21. 高级模型
http://doc.thinkphp.cn/manual/adv_model.html
22. 视图模型
http://doc.thinkphp.cn/manual/view_model.html
23. 关联模型
http://doc.thinkphp.cn/manual/relation_model.html
24. Mongo模型
http://doc.thinkphp.cn/manual/mongo_model.html
25. 动态模型
你可以从基本模型切换到高级模型或者视图模型,而当前的数据不会丢失,并可以控制要传递的参数和动态赋值。
要切换模型,可以使用:
- $User = M("User"); // 实例化User对象 是基础模型类的实例
- // 动态切换到高级模型类 执行top10查询操作
- $User->switchModel("Adv")->top10();
上面的写法也可以改成
- $User = M("AdvModel:User"); // 实例化User对象 是基础模型类的实例
- $User->top10();
如果要传递参数,可以使用:
- $User = D("User"); // 实例化User对象 是基础模型类的实例
- // 动态切换到视图模型类 并传入viewFields属性
- $UserView = $User->switchModel("View",array("viewFields"));
如果要动态赋值,可以使用:
- $User = M("User"); // 实例化User对象 是基础模型类的实例
- // 动态切换到关联模型类 并传入data属性
- $advUser = $User->switchModel("Relation");
- // 或者在切换模型后再动态赋值给新的模型
- $advUser->setProperty("_link",$link);
- // 查找关联数据
- $user = $advUser->relation(true)->find(1);
26. 虚拟模型
第一种:继承Model类
- Class UserModel extends Model {
- Protected $autoCheckFields = false;
- }
- Class UserModel { }
实例化UserModel类(默认的情况)
文件位于项目的Lib/Model/UserModel.class.php
- D('User');
文件位于项目的Lib/Logic/UserLogic.class.php
- D('User','Logic');
文件位于项目的Lib/Service/UserService.class.php
- D('User','Service');
视图:
ThinkPHP的视图有两个部分组成:View类和模板文件。Action控制器直接和View视图类打交道,把要输出的数据通过模板变量赋值的方式传递到视图类,而具体的输出工作则交由View视图类来进行,同时视图类还和模板引擎进行接口,包括完成布局渲染、输出替换、页面Trace等功能。
1. 模板定义
为了对模板文件更加有效的管理,ThinkPHP对模板文件进行目录划分,默认的模板文件定义规则是:模板目录/[分组名/][模板主题/]模块名/操作名+模板后缀
模板目录默认是项目下面的Tpl, 当定义分组的情况下,会按照分组名分开子目录,新版模板主题默认是空(表示不启用模板主题功能),模板主题功能是为了多模板切换而设计的,如果有多个模板主题的话,可以用 DEFAULT_THEME参数设置默认的模板主题名。
在每个模板主题下面,是以项目的模块名为目录,然后是每个模块的具体操作模板文件,例如:
User模块的add操作 对应的模板文件就应该是:
- Tpl/User/add.html
如果项目启用了模块分组功能(假设User模块属于Home分组),那么默认对应的模板文件可能变成 :
- Tpl/Home/User/add.html
例如 TMPL_FILE_DEPR如果配置成“_”的话,默认的模板文件就变成了:
- Tpl/Home/User_add.html
2. 模板赋值
要在模板中输出变量,必须在在Action类中把变量传递给模板,视图类提供了assign方法对模板变量赋值,无论何种变量类型都统一使用assign赋值。- $this->assign('name',$value);
- // 下面的写法是等效的
- $this->name = $value;
如果要同时输出多个模板变量,可以使用下面的方式:
- $array['name'] = 'thinkphp';
- $array['email'] = '[email protected]';
- $array['phone'] = '12335678';
- $this->assign($array);
模板变量赋值后,怎么在模板文件中输出,需要根据选择的模板引擎来用不同的方法,如果使用的是内置的模板引擎,请参考后面的模板指南部分。如果你使用的是PHP本身作为模板引擎的话 ,就可以直接在模板文件里面输出了,如下:
- echo $name.'['.$email.''.$phone.']';
如果要获得全部的模板变量,可以调用View类的get方法支持获取全部模板变量的值,例如:
- $this->get('name'); // 获取name模板变量的值
- $this->get(); // 获取所有模板赋值变量的值
3. 模板输出
模板变量赋值后就需要调用模板文件来输出相关的变量,模板调用通过display方法来实现。我们在操作方法的最后使用:- $this->display();
一、调用当前模块的其他操作模板
格式:display('操作名')例如,假设当前操作是User模块下面的read操作,我们需要调用User模块的edit操作模版,使用:
- $this->display('edit');
二、调用其他模块的操作模板
格式:display('模块名:操作名')
例如,当前是User模块,我们需要调用Member模块的read操作模版 ,使用:
- $this->display('Member:read');
三、调用其他主题的操作模板
格式:display('主题名:模块名:操作名')例如我们需要 调用Xp主题的User模块的edit操作模版,使用:
- $this->display('Xp:User:edit');
四、直接全路径输出模板
格式:display('模板文件名')例如,我们直接输出当前的Public目录下面的menu.html模板文件,使用:
- $this->display('./Public/menu.html');
- $this->display('./Public/menu.tpl');
五、直接解析内容
Action类的display方法如果传入第四个参数,表示不读取模板文件而是直接解析内容。例如:
- $this->assign('foo','ThinkPHP');
- $this->show('Hello, {$foo}!');
会在页面输出: Hello,ThinkPHP!
直接输出的内容仍然支持模板布局功能。
show方法也可以支持指定编码和输出格式,例如:
- $this->show($content, 'utf-8', 'text/xml');
事实上,display方法还有其他的参数和用法。
有时候某个模板页面我们需要输出指定的编码,而不是默认的编码,可以使用:
- $this->display('Member:read', 'gbk');
或者输出的模板文件不是text/html格式的,而是XML格式的,可以用:
- $this->display('Member:read', 'utf-8', 'text/xml');
如果你的网站输出编码不是默认的编码,可以使用:
- 'DEFAULT_CHARSET'=> 'gbk'
如果要输出XML格式的,可以用:
- 'TMPL_CONTENT_TYPE'=> 'text/xml'
4. 模板替换
在进行模板输出之前,系统还会对渲染的模板结果进行一些模板的特殊字符串替换操作,也就是实现了模板输出的替换和过滤。模板替换适用于所有的模板引擎,包括原生的PHP模板。这个机制可以使得模板文件的定义更加方便,默认的替换规则有:../Public: 会被替换成当前项目的公共模板目录 通常是 /项目目录/Tpl/当前主题/Public/
__TMPL__: 会替换成项目的模板目录 通常是 /项目目录/Tpl/当前主题/
(注:为了部署安全考虑,../Public和__TMPL__不再建议使用)
__PUBLIC__:会被替换成当前网站的公共目录 通常是 /Public/
__ROOT__: 会替换成当前网站的地址(不含域名)
__APP__: 会替换成当前项目的URL地址 (不含域名)
__GROUP__:会替换成当前分组的URL地址 (不含域名)
__URL__: 会替换成当前模块的URL地址(不含域名)
__ACTION__:会替换成当前操作的URL地址 (不含域名)
__SELF__: 会替换成当前的页面URL
注意这些特殊的字符串是严格区别大小写的,并且这些特殊字符串的替换规则是可以更改或者增加的,我们只需要在项目配置文件中配置TMPL_PARSE_STRING就可以完成。如果有相同的数组索引,就会更改系统的默认规则。例如:
- 'TMPL_PARSE_STRING' =>array(
- '__PUBLIC__' => '/Common', // 更改默认的/Public 替换规则
- '__JS__' => '/Public/JS/', // 增加新的JS类库路径替换规则
- '__UPLOAD__' => '/Uploads', // 增加新的上传路径替换规则
- )
- 'TMPL_PARSE_STRING' =>array(
- '--PUBLIC--' => '__PUBLIC__', // 采用新规则输出/Public字符串
- )
5. 获取内容
有些时候我们不想直接输出模板内容,而是希望对内容再进行一些处理后输出,就可以使用fetch方法来获取解析后的模板内容,在Action类里面使用:- $content = $this->fetch();
- $content = $this->fetch('Member:read');
注意,fetch方法仍然会执行上面的模板替换操作。
6. 模板引擎
系统支持原生的PHP模板,而且本身内置了一个基于XML的高效的编译型模板引擎,系统默认使用的模板引擎是内置模板引擎,关于这个模板引擎的标签详细使用可以参考模板指南部分。内置的模板引擎也可以直接支持在模板文件中采用PHP原生代码和模板标签的混合使用,如果需要完全使用PHP本身作为模板引擎,可以配置:
- 'TMPL_ENGINE_TYPE' =>'PHP'
如果你使用了其他的模板引擎,只需要设置TMPL_ENGINE_TYPE参数为相关的模板引擎名称即可。
模板引擎:
ThinkPHP内置了一个基于XML的性能卓越的模板引擎 ThinkTemplate,这是一个专门为ThinkPHP服务的内置模板引擎。ThinkTemplate是一个使用了XML标签库技术的编译型模板引擎,支持两种类型的模板标签,使用了动态编译和缓存技术,而且支持自定义标签库。
内置的模板引擎支持普通标签和XML标签方式两种标签定义,分别用于不同的目的:
普通标签 | 主要用于输出变量和做一些基本的操作 |
XML标签 | 主要完成一些逻辑判断、控制和循环输出,并且可扩展 |
1. 变量输出
如果我们在Action中赋值了一个name模板变量:- $name = 'ThinkPHP';
- $this->assign('name',$name);
- {$name}
普通标签默认开始标记是 {,结束标记是 }。也可以通过设置TMPL_L_DELIM和TMPL_R_DELIM进行更改。例如,我们在项目配置文件中定义:
- 'TMPL_L_DELIM'=>'<{',
- 'TMPL_R_DELIM'=>'}>',
那么,上面的变量输出标签就应该改成:
- <{$name}>
{$user.name}和{$user['name']}等效,也就是输出数组变量。
如果TMPL_VAR_IDENTIFY设置为obj,那么
{$user.name}和{$user:name}等效,也就是输出对象的属性。
如果TMPL_VAR_IDENTIFY留空的话,系统会自动判断要输出的变量是数组还是对象,这种方式会一定程度上影响效率,而且只支持二维数组和两级对象属性。
如果TMPL_VAR_IDENTIFY留空的话,系统会自动判断要输出的变量是数组还是对象,这种方式会一定程度上影响效率,而且只支持二维数组和两级对象属性。
如果是多维数组或者多层对象属性的输出,可以使用下面的定义方式:
- {$user.sub.name}// 使用点语法输出
或者使用
- {$user['sub']['name']}// 输出三维数组的值
- {$user:sub:name}// 输出对象的多级属性
2. 系统变量
除了常规变量的输出外,模板引擎还支持系统变量和系统常量、以及系统特殊变量的输出。它们的输出不需要事先赋值给某个模板变量。系统变量的输出必须以$Think.打头,并且仍然可以支持使用函数。常用的系统变量输出包括下面:
用法 | 含义 | 例子 |
---|---|---|
$Think.server | 获取$_SERVER | {$Think.server.php_self} |
$Think.get | 获取$_GET | {$Think.get.id} |
$Think.post | 获取$_POST | {$Think.post.name} |
$Think.request | 获取$_REQUEST | {$Think.request.user_id} |
$Think.cookie | 获取$_COOKIE | {$Think.cookie.username} |
$Think.session | 获取$_SESSION | {$Think.session.user_id} |
$Think.config | 获取系统配置参数 | {$Think.config.app_status} |
$Think.lang | 获取系统语言变量 | {$Think.lang.user_type} |
$Think.const | 获取系统常量 | {$Think.const.app_name}或{$Think.APP_NAME} |
$Think.env | 获取环境变量 | {$Think.env.HOSTNAME} |
$Think.version | 获取框架版本号 | {$Think.version} |
$Think.now | 获取当前时间 | {$Think.now} |
$Think.template | 获取当前模板 | {$Think.template} |
$Think.ldelim | 获取模板左界定符 | {$Think.ldelim} |
$Think.rdelim | 获取模板右界定符 | {$Think.rdelim} |
4、配置参数:输出项目的配置参数值
- {$Think.config.db_charset}
输出的值和C('db_charset') 的返回结果是一样的。
也可以输出二维的配置参数,例如:
- {$Think.config.user.user_name}
- {$Think.lang.page_error}
3. 使用函数
- {$varname|function1|function2=arg1,arg2,### }
- {$webTitle|md5|strtoupper|substr=0,3}
编译后的PHP代码就是:
注意函数的定义和使用顺序的对应关系,通常来说函数的第一个参数就是前面的变量或者前一个函数调用的返回结果,如果你的变量并不是函数的第一个参数,需要使用定位符号,例如:
- {$create_time|date="y-m-d",###}
编译后的PHP是:
- TMPL_DENY_FUNC_LIST=>"echo,exit,halt"
并且还提供了在模板文件中直接调用函数的快捷方法,这种方式更加直接明了,而且无需通过模板变量,包括两种方式:
1、执行函数并输出返回值:
格式:{:function(…)}
例如,输出U函数的返回值:
- {:U('User/insert')}
编译后的PHP代码是
2、执行函数但不输出:
格式:{~function(…)}
例如,调用say_hello函数:
- {~say_hello('ThinkPHP')}
编译后的PHP代码是:
4. 默认值输出
如果输出的模板变量没有值,但是我们需要在显示的时候赋予一个默认值的话,可以使用default语法,格式:{$变量|default="默认值"}
这里的default不是函数,而是系统的一个语法规则,例如:
- {$user.nickname|default="这家伙很懒,什么也没留下"}
- {$Think.post.name|default="名称为空"}
5. 使用运算符
内置模板引擎包含了运算符的支持,包括对“+”“ –” “*” “/”和“%”的支持。
- {$user.score+10} 是错误的
- {$user['score']+10} 是正确的
- {$user['score']*$user['level']} 正确的
- {$user['score']|myFun*10} 错误的
- {$user['score']+myFun($user['level'])} 正确的
6. 内置标签
系统内置标签库的所有标签无需引入标签库即可直接使用。XML标签有两种,包括闭合标签和开放标签,一个标签在定义的时候就已经决定了是否是闭合标签还是开放标签,不可混合使用,例如:
闭合标签:
开放标签:
value
内置支持的标签和属性列表如下:
标签名 | 作用 | 包含属性 |
---|---|---|
include | 包含外部模板文件(闭合) | file |
import | 导入资源文件(闭合 包括js css load别名) | file,href,type,value,basepath |
volist | 循环数组数据输出 | name,id,offset,length,key,mod |
foreach | 数组或对象遍历输出 | name,item,key |
for | For循环数据输出 | name,from,to,before,step |
switch | 分支判断输出 | name |
case | 分支判断输出(必须和switch配套使用) | value,break |
default | 默认情况输出(闭合 必须和switch配套使用) | 无 |
compare | 比较输出(包括eq neq lt gt egt elt heq nheq等别名) | name,value,type |
range | 范围判断输出(包括in notin between notbetween别名) | name,value,type |
present | 判断是否赋值 | name |
notpresent | 判断是否尚未赋值 | name |
empty | 判断数据是否为空 | name |
notempty | 判断数据是否不为空 | name |
defined | 判断常量是否定义 | name |
notdefined | 判断常量是否未定义 | name |
define | 常量定义(闭合) | name,value |
assign | 变量赋值(闭合) | name,value |
if | 条件判断输出 | condition |
elseif | 条件判断输出(闭合 必须和if标签配套使用) | condition |
else | 条件不成立输出(闭合 可用于其他标签) | 无 |
php | 使用php代码 | 无 |
7. 包含文件
可以使用Include标签来包含外部的模板文件,使用方法如下:
include标签(包含外部模板文件) | |
---|---|
闭合 | 闭合标签 |
属性 | file(必须):要包含的模板文件,支持变量 |
1、 使用完整文件名包含
格式:
2、包含当前模块的其他操作模板文件
格式:
操作模板无需带后缀。
4、包含其他模板主题的模块操作模板
格式:
格式:
3.1版本开始,include标签支持导入多个模板,用逗号分割即可,例如:
8. 导入文件
import标签(包含外部模板文件) | |
---|---|
闭合 | 闭合标签 |
属性 | file(必须):要包含的模板文件,支持变量 |
导入外部CSS文件必须指定type属性的值,例如:
上面的方式默认的import的起始路径是网站的Public目录,如果需要指定其他的目录,可以使用basepath属性,例如:
load标签(采用url方式引入资源文件) | |
---|---|
闭合 | 闭合标签 |
属性 | href(必须):要引入的资源文件url地址,支持变量 |
系统还提供了两个标签别名js和css 用法和load一致,例如:
9. Volist标签
Volist标签主要用于在模板中循环输出数据集或者多维数组。
volist标签(循环输出数据) | |
---|---|
闭合 | 非闭合标签 |
属性 | name(必须):要输出的数据模板变量 id(必须):循环变量 offset(可选):要输出数据的offset length(可选):输出数据的长度 key(可选):循环的key变量,默认值为i mod(可选):对key值取模,默认为2 empty(可选):如果数据为空显示的字符串 |
通常模型的select方法返回的结果是一个二维数组,可以直接使用volist标签进行输出。
在Action中首先对模版赋值:
- $User = M('User');
- $list = $User->select();
- $this->assign('list',$list);
在模版定义如下,循环输出用户的编号和姓名:
- {$vo.id}
- {$vo.name}
输出循环变量
- {$k}.{$vo.name}
如果没有指定key属性的话,默认使用循环变量i,例如:
- {$i}.{$vo.name}
如果要输出数组的索引,可以直接使用key变量,和循环变量不同的是,这个key是由数据本身决定,而不是循环控制的,例如:
- {$key}.{$vo.name}
从2.1版开始允许在模板中直接使用函数设定数据集,而不需要在控制器中给模板变量赋值传入数据集变量,如:
{$vo.name}
10. Foreach标签
foreach标签也是用于循环输出foreach标签(循环输出数据) | |
---|---|
闭合 | 非闭合标签 |
属性 | name(必须):要输出的数据模板变量 item(必须):循环单元变量 key(可选):循环的key变量,默认值为key |
- {$vo.id}
- {$vo.name}
11. For标签
For标签用于实现for循环,格式为:
for标签(循环输出数据) | |
---|---|
闭合 | 非闭合标签 |
属性 | start(必须):循环变量开始值 end(必须):循环变量结束值 name(可选):循环变量名,默认值为i step(可选):步进值,默认值为1 comparison(可选):判断条件,默认为lt |
- {$i}
解析后的代码是
- for ($i=1;$i<100;$i+=1){
- echo $i;
- }
12. Switch标签
-
输出内容1 -
输出内容2 -
默认情况
其中name属性可以使用函数以及系统变量,例如:
-
admin -
default
对于case的value属性可以支持多个条件的判断,使用”|”进行分割,例如:
-
图像格式 -
其他格式
Case标签还有一个break属性,表示是否需要break,默认是会自动添加break,如果不要break,可以使用:
-
admin -
admin -
default
-
admin -
member -
default
13. 比较标签
- <比较标签 name="变量" value="值">内容比较标签>
系统支持的比较标签以及所表示的含义分别是:
eq或者 equal | 等于 |
neq 或者notequal | 不等于 |
gt | 大于 |
egt | 大于等于 |
lt | 小于 |
elt | 小于等于 |
heq | 恒等于 |
nheq | 不恒等于 |
例如,要求name变量的值等于value就输出,可以使用:
value
当name变量的值不小于5就输出
value
当vo对象的属性(或者数组,或者自动判断)等于5就输出
{$vo.name}
而且还可以支持对变量使用函数
当vo对象的属性值的字符串长度等于5就输出
{$vo.name}
变量名可以支持系统变量的方式,例如:
相等 不相等
14. 三元运算
模板可以支持三元运算符,例如:- {$status?'正常':'错误'}
- {$info['status']?$info['msg']:$info['error']}
15. 范围判断标签
Range标签用于判断某个变量是否在某个范围之内:
范围判断标签(包括innotinbetween notbetween) | |
---|---|
闭合 | 非闭合标签 |
属性 | name(必须):变量名 value(必须):要比较的范围值,支持变量 |
可以使用in标签来判断模板变量是否在某个范围内,例如:
输出内容1
如果判断不再某个范围内,可以使用:
输出内容2
可以把上面两个标签合并成为:
输出内容1 输出内容2
输出内容1
输出内容1
输出内容1
也可以直接使用range标签,替换in和notin的用法:
输出内容1
16. Present标签和Empty标签
可以使用present标签来判断模板变量是否已经赋值,
present标签和notpresent标签 | |
---|---|
闭合 | 非闭合标签 |
属性 | name(必须):变量名 |
配合 | 可以结合else标签一起使用 |
name已经赋值
如果判断没有赋值,可以使用:
name还没有赋值
可以把上面两个标签合并成为:
name已经赋值 name还没有赋值
可以使用empty标签判断模板变量是否为空,
empty标签和notempty标签 | |
---|---|
闭合 | 非闭合标签 |
属性 | name(必须):变量名 |
配合 | 可以结合else标签一起使用 |
name为空值
如果判断没有赋值,可以使用:
name不为空
可以把上面两个标签合并成为:
name为空 name不为空
17. Defined标签和Define标签
可以使用defined标签判断常量是否已经有定义:
defined标签和notdefined标签 | |
---|---|
闭合 | 非闭合标签 |
属性 | name(必须):变量名 |
NAME常量已经定义
如果判断没有被定义,可以使用:
NAME常量未定义
可以把上面两个标签合并成为:
NAME常量已经定义 NAME常量未定义
defined标签和notdefined标签 | |
---|---|
闭合 | 闭合标签 |
属性 | name(必须):常量名 value(必须):常量值,支持变量 |
配合 | 可以结合else标签一起使用 |
18. Assign标签
可以使用assign标签进行赋值:assign标签(在模板中给变量赋值) | |
---|---|
闭合 | 闭合标签 |
属性 | name(必须):模板变量名 value(必须):变量值,支持变量 |
19. IF标签
用法示例:
value1 -
value2 -
value3
除此之外,我们可以在condition属性里面使用php代码,例如:
ThinkPHP -
other Framework
自动判断user变量是数组还是对象
ThinkPHP -
other Framework
ThinkPHP -
other Framework
20. 标签嵌套
模板引擎支持标签的多层嵌套功能,可以对标签库的标签指定可以嵌套。系统内置的标签中,volist、switch、if、elseif、else、foreach、compare(包括所有的比较标签)、(not)present、(not)empty、(not)defined等标签都可以嵌套使用。例如:
-
- {$sub.name}
- 'TAG_NESTED_LEVEL' =>5
21. 使用PHP代码
echo 'Hello,world!';
注意:php标签或者php代码里面就不能再使用标签(包括普通标签和XML标签)了,因此下面的几种方式都是无效的:
value
如果设置了TMPL_DENY_PHP参数为true,就不能在模板中使用原生的PHP代码,但是仍然支持PHP标签输出。
22. 模板布局
第一种方式是 以布局模板为入口的方式该方式需要配置开启LAYOUT_ON 参数(默认不开启),并且设置布局入口文件名LAYOUT_NAME(默认为layout)。
开启LAYOUT_ON后,我们的模板渲染流程就有所变化,例如:
- Class UserAction extends Action {
- Public function add() {
- $this->display('add');
- }
- }
- {__CONTENT__}
采用这种布局方式的情况下,一旦User/add.html 模板文件或者layout.html布局模板文件发生修改,都会导致模板重新编译。
如果项目需要使用不同的布局模板,可以动态的配置LAYOUT_NAME参数实现。
如果某些页面不需要使用布局模板功能,可以在模板文件开头加上 {__NOLAYOUT__} 字符串。
如果上面的User/add.html 模板文件里面包含有{__NOLAYOUT__},则即使当前开启布局模板,也不会进行布局模板解析。
以前面的输出模板为例,这种方式的入口还是在User/add.html 模板,但是我们可以修改下add模板文件的内容,在头部增加下面的布局标签:
如果需要使用其他的布局模板,可以改变layout的name属性,例如:
-
- {__CONTENT__}
-
也可以采用两种布局方式的结合,可以实现更加复杂的模板布局以及嵌套功能。
23. 模板继承
模板继承的优势其实是设计基础模板中的区块(block)和子模板中替换这些区块。每个区块由
下面就是基础模板中的一个典型的区块设计(用于设计网站标题):
网站标题
block标签必须指定name属性来标识当前区块的名称,这个标识在当前模板中应该是唯一的,block标签中可以包含任何模板内容,包括其他标签和变量,例如:
{$web_title}
你甚至还可以在区块中加载外部文件:
24. 原样输出
literal标签(保持原样输出) | |
---|---|
闭合 | 非闭合标签 |
属性 | 无 |
-
value1 -
value2 -
value3
总之,所有可能和内置模板引擎的解析规则冲突的地方都可以使用literal标签处理。
25. 模板注释
模板支持注释功能,该注释文字在最终页面不会显示,仅供模板制作人员参考和识别。
- {// 这是模板注释内容 }
- {/* 这是模板
- 注释内容*/ }
26. 引入标签库
格式:可以同时导入多个标签库,用逗号分隔,例如:
- 'TAGLIB_PRE_LOAD' => 'html' ,
假设你确信Html标签库无论在现在还是将来都不会和系统内置的标签库存在相同的标签,那么可以配置TAGLIB_BUILD_IN的值把Html标签库作为内置标签库引入,例如:
- 'TAGLIB_BUILD_IN' => 'cx,html' ,
27. 修改定界符
要更改普遍模板的起始标签和结束标签,请使用下面的配置参数:
- TMPL_L_DELIM //模板引擎普通标签开始标记
- TMPL_R_DELIM //模板引擎普通标签结束标记
例如在项目配置文件中增加下面的配置:
- 'TMPL_L_DELIM'=>'',
普通模板标签主要用于模板变量输出和模板注释。如果要使用其它功能,请使用XML模板标签。XML模板标签可以用于模板变量输出、文件包含、条件控制、循环输出等功能,而且完全可以自己扩展功能。如果你觉得XML标签无法在正在使用的编辑器里面无法编辑,还可以更改XML标签库的起始和结束标签,请修改下面的配置参数:
- TAGLIB_BEGIN //标签库标签开始标签
- TAGLIB_END //标签库标签结束标记
例如在项目配置文件中增加下面的配置:
- 'TAGLIB_BEGIN'=>'[',
- 'TAGLIB_END'=>']',
28. 避免JS混淆
如果使用内置的模板引擎,而且采用默认的标签设置的话,在某些情况下,如果不注意,{$('name').value} 这样的JS代码很容易被内置模板引擎误解析。有三个方法可以解决类似的混淆问题:
1、{$('name').value}改成{ $('name').value}
因为内置模板引擎的解析规则是"{"后面紧跟"$"符号才会解析变量 因此只要在"{" 和"$"之间添加空格就不会被误解析了
2、使用内置的literal标签包含JS代码
3、定制模板引擎标签的定界符
例如:'TMPL_L_DELIM'=>'<{','TMPL_R_DELIM'=>'}>'这样就和JS代码区别开来了。
日志:
日志的处理工作是由系统自动进行的,在开启日志记录的情况下,会记录下允许的日志级别的所有日志信息。其中,为了性能考虑,SQL日志级别必须在调试模式开启下有效,否则就不会记录。系统的日志记录由核心的Log类完成,提供了多种方式记录了不同的级别的日志信息。
1. 日志级别
EMERG | 严重错误,导致系统崩溃无法使用 |
ALERT | 警戒性错误, 必须被立即修改的错误 |
CRIT | 临界值错误, 超过临界值的错误 |
ERR | 一般性错误 |
WARN | 警告性错误, 需要发出警告的错误 |
NOTICE | 通知,程序可以运行但是还不够完美的错误 |
INFO | 信息,程序输出信息 |
DEBUG | 调试,用于调试信息 |
SQL | SQL语句,该级别只在调试模式开启时有效 |
要开启日志记录,必须在配置中开启LOG_RECORD参数,以及可以在项目配置文件中配置需要记录的日志级别,例如:
- 'LOG_RECORD' => true, // 开启日志记录
- 'LOG_LEVEL' =>'EMERG,ALERT,CRIT,ERR', // 只记录EMERG ALERT CRIT ERR 错误
2. 记录方式
记录方式 | 说明 | 常量标识 |
---|---|---|
SYSTEM | 日志发送到PHP的系统日志记录 | 0 |
日志通过邮件方式发送 | 1 | |
FILE | 日志通过文件方式记录(默认方式) | 3 |
SAPI | 日志通过SAPI方式记录 | 4 |
其中的时间显示可以动态配置,默认是采用 [ c ],例如我们可以改成:
- Log::$format = '[ Y-m-d H:i:s ]';
- [2012-01-15T18:09:22+08:00] /Index/index|NOTIC: [8] Undefined variable: verify PublicAction.class.php 第 162 行.
- [2012-01-15T18:09:22+08:00] /Index/index | SQL: RunTime:0.214238s SQL = SHOW COLUMNS FROM think_user
- [2012-01-15T18:09:22+08:00] /Index/index | SQL: RunTime:0.039159s SQL = SELECT * FROM `think_user` WHERE ( `account` = 'admin' ) AND ( `status` > 0 ) LIMIT 1
默认采用文件方式记录日志信息,日志文件的命名格式是:年(简写)_月_日.log,例如:09_10_01.log 表示2009年10月1日的日志文件
可以设置LOG_FILE_SIZE参数来限制日志文件的大小,超过大小的日志会形成备份文件。备份文件的格式是在当前文件名前面加上备份的时间戳,例如:
1189571417-07_09_12.log 备份的日志文件
- 'LOG_TYPE' =>1, // 采用邮件方式记录日志
- 'LOG_DEST' =>'[email protected]', // 要发送日志的邮箱
- 'LOG_EXTRA' =>'From: [email protected]', // 邮件的发件人设置
3. 手动记录
http://doc.thinkphp.cn/manual/log_record.html错误:
1. 异常处理
和PHP默认的异常处理不同,ThinkPHP抛出的不是单纯的错误信息,而是一个人性化的错误页面。调试模式下面一旦系统发生严重错误会自动抛出异常,也可以用ThinkPHP定义的throw_exception方法手动抛出异常。
- throw_exception('新增失败');
- throw_exception('信息录入错误','InfoException');
同样也可以使用throw 关键字来抛出异常,下面的写法是等效的:
- throw new ThinkException('新增失败');
- throw new InfoException('信息录入错误');
如果需要,我们建议在项目的类库目录下面增加Exception目录用于专门存放异常类库,以更加精确地定位异常。
2. 异常模板
系统内置的异常模板在系统目录的Tpl/think_exception.tpl,可以通过修改系统模板来修改异常页面的显示。也通过设置TMPL_EXCEPTION_FILE 配置参数来修改系统默认的异常模板文件, 例如:
- 'TMPL_EXCEPTION_FILE' => APP_PATH.'/Public/exception.tpl'
异常模板中可以使用的异常变量有:$e['file']异常文件名
$e['line'] 异常发生的文件行数
$e['message'] 异常信息
$e['trace'] 异常的详细Trace信息
因为异常模板使用的是原生PHP代码,所以还可以支持任何的PHP方法和系统变量使用。
3. 异常显示
抛出异常后通常会显示具体的错误信息,如果不想让用户看到具体的错误信息,可以设置关闭错误信息的显示并设置统一的错误提示信息,例如:
- 'SHOW_ERROR_MSG' =>false,
- 'ERROR_MESSAGE' =>'发生错误!'
设置之后,所有的异常页面只会显示“发生错误!”这样的提示信息,但是日志文件中仍然可以查看具体的错误信息。新版如果关闭调试模式的话,为了安全起见,默认就是关闭异常信息提示。另外一种方式是配置ERROR_PAGE参数,把所有异常和错误都指向一个统一页面,从而避免让用户看到异常信息,通常在部署模式下面使用。ERROR_PAGE参数必须是一个完整的URL地址,例如:
- 'ERROR_PAGE' =>'/Public/error.html'
如果不在当前域名,还可以指定域名:
- 'ERROR_PAGE' =>'http://www.myDomain.com/Public/error.html'
注意ERROR_PAGE所指向的页面不能再使用异常的模板变量了。