admin 管理员组文章数量: 887629
2024年1月12日发(作者:vibration motor)
TP开发手册完整版:
系统特性
ThinkPHP是一个性能卓越并且功能丰富的轻量级PHP开发框架,本身具有很多的原创特性,并且倡导大道至简,开发由我的开发理念,用最少的代码完成更多的功能,宗旨就是让WEB应用开发更简单、更快速。从1.*版本开始就放弃了对PHP4的兼容,因此整个框架的架构和实现能够得以更加灵活和简单。2.0版本更是在之前的基础上,经过全新的重构和无数次的完善以及改进,达到了一个新的阶段,足以达到企业级和门户级的开发标准。
ThinkPHP值得推荐的特性包括:
类库导入:ThinkPHP是首先采用基于类库包和命名空间的方式导入类库,让类库导入看起来更加简单清晰,而且还支持冲突检测和别名导入。为了方便项目的跨平台移植,系统还可以严格检查加载文件的大小写。
URL模式:系统支持普通模式、PATHINFO模式、REWRITE模式和兼容模式的URL方式,支持不同的服务器和运行模式的部署,配合URL路由功能,让你随心所欲的构建需要的URL地址和进行SEO优化工作。
编译机制:独创的核心编译和项目的动态编译机制,有效减少OOP开发中文件加载的性能开销。ALLINONE模式更是让你体验飞一般的感觉。
ORM:简洁轻巧的ORM实现,配合简单的CURD以及AR模式,让开发效率无处不在。
查询语言:内建丰富的查询机制,包括组合查询、复合查询、区间查询、统计查询、定位查询、动态查询和原生查询,让你的数据查询简洁高效。
动态模型:无需创建任何对应的模型类,轻松完成CURD操作,支持多种模型之间的动态切换,让你领略数据操作的无比畅快和最佳体验。
高级模型:可以轻松支持序列化字段、文本字段、只读字段、延迟写入、乐观锁、数据分表等高级特性。
视图模型:轻松动态地创建数据库视图,多表查询不再烦恼。
关联模型:让你以出乎意料的简单、灵活的方式完成多表的关联操作。
分组模块:不用担心大项目的分工协调和部署问题,分组模块帮你解决跨项目的难题。
模板引擎:系统内建了一款卓越的基于XML的编译型模板引擎,支持两种类型的模板标签,融合了Smarty和JSP标签库的思想,支持标签库扩展。通过驱动还可以支持Smarty、EaseTemplate、TemplateLite、Smart等第三方模板引擎。
AJAX支持:内置AJAX数据返回方法,支持JSON、XML和EVAL格式返回客户端,并且系统不绑定任何AJAX类库,可随意使用自己熟悉的AJAX类库进行操作。
多语言支持:系统支持语言包功能,项目和模块都可以有单独的语言包,并且可以自动检测浏览器语言自动载入对应的语言包。
模式扩展:除了标准模式外,系统内置了Lite、Thin和Cli模式,针对不同级别的应用开发提供最佳核心框架,还可以自定义模式扩展。
自动验证和完成:自动完成表单数据的验证和过滤,生成安全的数据对象。
字段类型检测:字段类型强制转换,确保数据写入和查询更安全。
数据库特性:系统支持多数据库连接和动态切换机制,支持分布式数据库。犹如企业开发的一把利刃,跨数据库应用和分布式支持从此无忧。
缓存机制:系统支持包括文件方式、APC、Db、Memcache、Shmop、Eaccelerator和Xcache在内的多种动态数据缓存类型,以及可定制的静态缓存规则,并提供了快捷方法进行存取操作。
扩展机制:系统支持包括类库扩展、驱动扩展、应用扩展、模型扩展、控制器扩展、标签库扩展、模板引擎扩展、Widget扩展、行为扩展和模式扩展在内的强大灵活的扩展机制,让你不再受限于核心的不足和无所适从,随心DIY自己的框架和扩展应用。
目录结构
新版的目录结构在原来的基础上进行了调整,更加清晰。
一、系统目录(ThinkPHP框架目录)
Think 框架的公共入口文件
Common包含框架的一些公共文件、系统定义、系统函数和惯例配置等
Lang系统语言文件
Lib系统基类库目录
Tpl系统模板目录
Mode框架模式扩展目录
Vendor第三方类库目录
二、应用目录(项目目录)
index.php 项目入口文件(可以使用其他名称或者放置于其他位置)
Common项目公共文件目录,一般放置项目的公共函数
Conf项目配置目录,所有的配置文件都放在这里。
Lang 项目语言包目录(可选)
Lib项目类库目录,通常包括Action和Model子目录
Tpl项目模板目录,支持模板主题
Runtime项目运行时目录,包括Cache(模板缓存)、Temp(数据缓存)、Data(数据目录)和Logs(日志文件)子目录
上面的只是默认方式,项目下面的目录名称和结构是可以重新定义的。其实项目目录并不需要开发人员手动创建,只需要定义好项目的入口文件之后,系统会在第一次执行的时候自动生成项目必须的所有目录结构(前提是项目目录具有可写权限,这点在Linux环境下面需要注意)。
可以看出新版的目录结构更加便于部署和配置,因为只有Runtime目录才是需要具备可写权限的,在Linux环境下面可以更加快速的部署和配置目录权限。
三、部署目录
当我们实际部署网站的时候,目录结构往往由于项目的复杂而变得复杂。我们推荐的部署目录结构如下:
ThinkPHP系统目录(下面的目录结构同上面的系统目录)
Home项目目录(下面的目录结构同上面的应用目录)
Admin后台管理项目目录
……更多的项目目录
index.php 网站的入口文件
admin.php 网站的后台入口文件
如果采用分组模块的话 可以简化为一个项目目录
ThinkPHP系统目录(下面的目录结构同上面的系统目录)
App项目目录
Public网站公共目录
index.php 网站的入口文件
项目的模板文件还是放到项目的Tpl目录下面,只是将外部调用的资源文件,
包括图片 JS 和CSS统一放到网站的公共目录Public下面,分Images、Js和Css子目录存放,如果有可能的话,甚至也可以把这些资源文件单独放一个外部的服务器远程调用,并进行优化。
这样部署的好处是系统目录和项目目录可以放到非WEB访问目录下面,网站目录下面可以只需要放置Public公共目录和i入口文件(如果是多个项目的话,每个项目的入口文件都需要放到WEB目录下面),从而提高网站的安全性。
MVC分层
MVC 是一种将应用程序的逻辑层和表现层进行分离的方法。ThinkPHP 也是基于MVC设计模式的。MVC只是一个抽象的概念,并没有特别明确的规定,ThinkPHP中的MVC分层大致体现在:
模型(M):模型的定义由Model类来完成。
控制器(C):应用控制器(核心控制器App类)和Action控制器都承担了控制器的角色,Action控制器完成业务过程控制,而应用控制器负责调度控制。
视图(V):由View类和模板文件组成,模板做到了100%分离,可以独立预览和制作。
有些时候,ThinkPHP并不依赖M或者V ,也就是说没有模型或者视图也一样可以工作。甚至也不依赖C,这是因为ThinkPHP在Action之上还有一个总控制器,即App控制器,负责应用的总调度。在没有C的情况下,必然存在视图V,否则就不再是一个完整的应用。
总而言之,ThinkPHP的MVC模式只是提供了一种敏捷开发的手段,而不是拘泥于MVC本身。
执行流程
基于ThinkPHP框架的应用程序组成和执行过程,(见附件)
命名规范
框架必然有其自身的一定规范,在ThinkPHP中亦然。下面是使用ThinkPHP应该尽量遵循的命名规范:
类文件都是以.class.php为后缀(这里是指的ThinkPHP内部使用的类库文件,不代表外部加载的类库文件),使用驼峰法命名,并且首字母大写,例如DbM.php。
函数、配置文件等其他类库文件之外的一般是以.php为后缀(第三方引入的不做要求)。
确保文件的命名和调用大小写一致,是由于在类Unix系统上面,对大小写是敏感的(而ThinkPHP在调试模式下面,即使在Windows平台也会严格检查大小写)。
类名和文件名一致(包括上面说的大小写一致),例如 UserAction类的文件命名是UserActi.php, InfoModel类的文件名是InfoM.php,
函数的命名使用小写字母和下划线的方式,例如 get_client_ip
Action控制器类以Action为后缀,例如 UserAction、InfoAction
模型类以Model为后缀,例如UserModel、InfoModel
方法的命名使用驼峰法,并且首字母小写,例如 getUserName
属性的命名使用驼峰法,并且首字母小写,例如 tableName
以双下划线“__”打头的函数或方法作为魔法方法,例如 __call 和 __autoload
常量以大写字母和下划线命名,例如 HAS_ONE和 MANY_TO_MANY
配置参数以大写字母和下划线命名,例如HTML_CACHE_ON
语言变量以大写字母和下划线命名,例如MY_LANG,以下划线打头的语言变量通常用于系统语言变量,例如 _CLASS_NOT_EXIST_。
数据表和字段采用小写加下划线方式命名,例如 think_user 和 user_name
特例:
在ThinkPHP里面,有一个函数命名的特例,就是单字母大写函数,这类函数通常是某些操作的快捷定义,或者有特殊的作用。例如,ADSL方法等等,他们有着特殊的含义,后面会有所了解。
另外一点,ThinkPHP默认使用UTF-8编码,所以请确保你的程序文件采用UTF-8编码格式保存,并且去掉BOM信息头(去掉BOM头信息有很多方式,不同的编辑器都有设置方法,也可以用工具进行统一检测和处理)。
入口文件
ThinkPHP采用单一入口模式进行项目部署和访问,无论完成什么功能,一个项目只有一个统一(但不一定是唯一)的入口。并且所有的项目的入口文件是类似的,入口文件主要完成的作用是:
路径定义 项目名称定义(可选)
额外参数定义(可选)
载入框架入口文件(必须)
实例化一个App应用(必须)
下面是一个标准的入口文件的写法:
// 定义ThinkPHP框架路径(相对于入口文件)
define('THINK_PATH', '../ThinkPHP');
//定义项目名称和路径
define('APP_NAME', 'Myapp');
define('APP_PATH', '.');
// 加载框架入口文件
require(THINK_PATH."/Think");
//实例化一个网站应用实例
App::run();
>
项目编译
ThinkPHP 正式版本开始引入了新的项目编译机制,所谓的项目编译机制是指系统第一次运行的时候会自动生成核心缓存文件~runti和项目编译缓存文件~,这些编译缓存文件把核心和项目必须的文件打包到一个文件中,并且去掉所有空白和注释代码,因为存在一个预编译的过程,所以还会进行一些相关的目录检测,对于不存在的目录可以自动生成,这个自动生成机制后面还会提到。当第二次执行的时候就会直接载入编译过的缓存文件,从而省去很多IO开销,加快执行速度。项目编译机制对运行没有任何影响,预编译操作和其他的目录检测机制只会执行一次,因此无论在预编译过程中做了多少复杂的操作,对后面的执行没有任何效率的缺失。
编译缓存文件,默认是自动生成在项目目录下面的Runtime目录下面。如果希望自己设置目录,可以在入口文件里面设置RUNTIME_PATH进行更改,例如
define('RUNTIME_PATH','./MyApp/temp/');
注意在Linux环境下面需要对RUNTIME_PATH目录设置可写权限。
核心编译缓存文件~runti包含的文件由系统的cor文件决定,如果是采用了模式扩展的话,就由模式扩展入口文件决定。默认的核心模式下面包含了下面的一些文
件:系统定义文件defi、系统函数库funct、系统基类Think、异常基类ThinkException、日志类 Log、应用类 App、控制器基类 Action、视图类 View。
其他类库可以在操作方法中使用系统导入机制或者自动加载机制完成加载。
项目编译缓存文件~ 通常包含了下面的一些文件:项目配置文件(由惯例配置、项目配置合并而成)、项目公共函数文件co。每个项目还可以单独添加自己的项目编译文件列表,只需要在项目配置目录下面定义a文件,返回需要额外添加到项目编译缓存的文件列表数组即可。
注意在调试模式下面不会生成项目编译缓存,但是依然会生成核心缓存。如果不希望生成核心缓存文件的话,可以在项目入口文件里面设置NO_CACHE_RUNTIME,例如:
define('NO_CACHE_RUNTIME',True);
以及设置对编译缓存的内容是否进行去空白和注释,例如:
define('STRIP_RUNTIME_SPACE',false);
则生成的编译缓存文件是没有经过去注释和空白的,仅仅是把文件合并到一起,这样的好处是便于调试的错误定位,建议部署模式的时候把上面的设置为True或者删除该定义。
URL访问
ThinkPHP框架基于模块和操作的方式进行访问,由于ThinkPHP框架的应用采用单一入口文件来执行,因此网站的所有的模块和操作都通过URL的参数来访问和执行。这样一来,传统方式的文件入口访问会变成由URL的参数来统一解析和调度。
ThinkPHP强大的URL解析、调度以及路由功能为这个功能实现提供了有力的保证,并且可以在绝大多数的服务器环境里面部署成功。
ThinkPHP支持的URL模式包括普通模式、PATHINFO模式、REWRITE模式和兼容模式,并且都提供路由支持。默认为PATHINFO 模式,提供最好的用户体验和搜索引擎友好支持。
例如普通模式下面的URL为:
localhost/appName/index.php?m=moduleName&a=actionName&id=1
如果使用PATHINFO模式的话,URL成为:
localhost/appName/index.php/moduleName/actionName/id/1/
PATHINFO模式对以往的编程方式没有影响,GET 和POST方式传值依然有效,因为系统会对PATHINFO 方式自动处理,例如上面URL地址中的id的值可以通过 $_GET['id'] 的方式正常获取到。
如果使用REWRITE模式,通过配置URL可以成为:
localhost/appName/moduleName/actionName/id/1/
例如上面生成的myApp项目如果我们通过下面的URL访问:
localhost/myApp/
其实是定位到myApp项目的Index模块的index操作,因为系统在没有指定模块和操作的时候,会执行默认的模块和操作,这个在ThinkPHP的惯例配置里面是Index模块和index操作。因此下面的URL和上面的结果是相同的:
localhost/myApp/index.php/Index/index/
通过项目配置参数,我们可以改变这个默认配置。
系统还支持分组模式和URL路由的功能,这些都能够带来URL的不同体验。
控制器
ThinkPHP的控制器就是模块类,通常位于项目的LibAction目录下面。类名就是模块名加上Action后缀,例如IndexAction类就表示了Index模块。控制器类必须继承系统的Action基础类,这样才能确保使用Action类内置的方法。而index操作其实就是IndexAction类的一个公共方法,所以我们在浏览器里面输入URL:
localhost/myApp/index.php/Index/index/
其实就是执行了IndexAction类的index(公共)方法。
每个模块的操作并非一定需要有定义操作方法,如果我们只是希望输出一个模板,既没有变量也没有任何的业务逻辑,那么只需要按照规则定义好操作对应的模板文件即可,而不需要定义操作方法。例如,我们在IndexAction中如果没有定义help方法,但是存在对应的Index/ 模板文件,那么下面的URL访问依然可以正常运作:
localhost/myApp/index.php/Index/help/
因为系统找不到IndexAction类的help方法,会自动定位到Index模块的模板目录中查找help.html模板文件,然后直接渲染输出。
控制器中还设计了模块分组、空操作、空模块、前置和后置操作、操作链等功能,后面会有详细的描述。
模型
在ThinkPHP中基础的模型类就是Model类,该类完成了基本的CURD、ActiveRecord模式、连贯操作和统计查询,一些高级特性被封装到另外的模型类中,例如AdvModel高级模型类完成了一些包括文本字段、只读字段、序列化字段、乐观锁、多数据库连接等模型的高级特性,ViewModel视图模型类完成了模型的视图操作,RelationModel关联模型类完成了模型的关联操作。
基础模型类Model的设计非常灵活,甚至可以无需进行任何模型定义,就可以进行相关数据表的ORM和CURD操作,只有在需要封装单独的业务逻辑的时候,模型类才是必须被定义的。
新版实现了动态模型的设计,可以从基础模型类切换到其他模型类进行方法操作而不会丢失现有的数据属性。这是一个真正的按需加载的思想,而不再是必须要事先继承需要操作的模型类。
数据库抽象层
ThinkPHP内置了抽象数据库访问层,把不同的数据库操作封装起来,而使用了统一的操作接口。我们只需要使用公共的Db类进行操作,而无需针对不同的数据库写不同的代码和底层实现,Db类会自动调用相应的数据库适配器来处理。目前支持Mysql、MsSQL、PgSQL、Sqlite、Oracle、Ibase以及PDO等多种数据库和连接。
数据库抽象层也支持分布式数据库的连接,包括对等和主从方式两种的支持,而且也支持多数据库连接和切换,为企业级应用保驾护航。
视图
ThinkPHP的视图主要由View视图类和模板文件构成。视图类负责Action控制器类和模板文件之间沟通,Action类把数据通过View类传递到模板文件,而模板文件把
接收到的数据转换成相应的数据格式显示。在特殊的情况下面,视图类会缓存模板文件的输出结果,这个时候缓存文件也纳入了视图层的概念之中了。
如果模板文件使用了某些模板引擎进行标签定义,而不是使用原生的PHP语法,那么在模板输出的过程中还需要引入模板解析,如果是编译型的模板引擎例如ThinkPHP内置的模板引擎和Smarty之类的,那么模板文件会有一个编译的过程,通常编译后的模板文件会生成一个编译后的模板缓存文件,第二次输出模板文件的时候就是直接输出编译后的模板缓存。如果是解释型的模板引擎,就会在每次输出模板的过程中进行解析操作。
无论如何,视图应该仅仅是进行数据的输出显示,通常在视图渲染过程是不会改变数据本身的,而只是进行格式化输出和显示。
模板引擎
ThinkPHP内置了一个基于XML的性能卓越的模板引擎 ThinkTemplate,这是一个专门为ThinkPHP服务的内置模板引擎,无论在功能或是性能还有易用性方面都比Smarty优秀。ThinkTemplate是一个使用了XML标签库技术的编译型模板引擎,使用了动态编译和缓存技术,支持两种类型的模板标签,支持PHP原生代码和模板标签的混合使用。而且支持自定义标签库在基于内置,模板引擎的基础上,扩展更多更强大更适合自己项目所使用的模板标签,任何想达到的功能皆有可能。
系统函数库
系统函数库位于系统的Common目录下面,函数库文件名为function,该文件会在执行过程自动加载,系统函数库中的大部分方法是核心所依赖或者经常被使用的,因此系统函数库的所有函数都可以在任何时候直接使用。
除了系统函数库外,系统还内置了一个扩展函数库ex,供项目开发的过程中加载调用,扩展函数库中的函数通常是核心不依赖的,但却有很好的辅助
作用,能够为应用开发提供进一步的方便。需要使用扩展函数库中的方法,可以直接拷贝到你的项目函数库中。
快捷方法
ThinkPHP 为一些常用的操作定义了快捷方法,这些方法以单字母命名,具有比较容易记忆的特点。非常有意思的是,这些快捷方法的字母包含了 ADSL 字母,所以我们称之为 ADSL 方法,但是并不局限于 ADSL 四个方法,包括下面的:
A快速实例化Action类库
B执行行为类
C配置参数存取方法
D快速实例化Model类库
F快速简单文本数据存取方法
L 语言参数存取方法
M快速高性能实例化模型
R快速远程调用Action类方法
S快速缓存存取方法
U URL动态生成和重定向方法
W 快速Widget输出方法
由上可知,快捷方法的命名方式,一般是以该方法所对应的符合其功能意义的英文单词首字母进行命名,至于每个快捷方法的详细使用,我们会在具体的章节中有针对的描述或者参考附录部分。
项目函数库
项目函数库通常位于项目的Common目录下面,文件名为common.php,该文件会在执行过程中自动加载,并且合并到项目编译统一缓存,如果使用了分组部署方式,并且该目
录下存在"分组名称/funct"文件,也会根据当前分组执行时对应进行自动加载,因此项目函数库的所有函数也都可以无需手动载入而直接使用。
基类库
ThinkPHP框架通过基类库的概念把所有系统类库都集中在一起管理,包括ThinkPHP的核心类库。
基类库目录位于系统目录下面的Lib目录,框架内置的有Think核心类库,还可以扩展ORG 、Com扩展类库。核心基类库的作用是完成框架的通用性开发而必须的基础类和常用工具类等,包含有:
Think.Core 核心类库包
Think.Db 数据库类库包
Think.Exception 异常处理类库包
Think.Template 内置模板引擎类库包
Think.Util 系统工具类库包
扩展类库
官方网站额外提供了很多的基类库扩展,可以直接带路径拷贝类库文件到系统的基类库目录就可以使用了。例如,我们要使用扩展类库的ORG/Util/.php的话,把Page类库拷贝到系统目录下面的Lib/ORG/Util/目录即可。
目前可以支持的扩展类库包,包括ORG和Com。所有扩展类库必须放置于上面两个类库包之下管理。
应用类库
应用类库是指项目中自己定义或者使用的类库,这些类库也是遵循ThinkPHP的命名规范。应用类库目录位于项目目录下面的Lib目录。应用类库的范围很广,包括Action类库、Model类库或者其他的工具类库。
类库导入
ThinkPHP模拟了Java的类库导入机制,统一采用import方法进行类文件的加载。import方法是ThinkPHP内建的类库和文件导入方法,提供了方便和灵活的文件导入机制,完全可以替代PHP的require和include方法。例如:
import("Think.on");
import(".UserModel");
import方法具有缓存和检测机制,相同的文件不会重复导入,如果发现导入了不同的位置下面的同名类库文件,系统会提示冲突,例如:
import("Think.");
import("");
上面的情况导入会产生引入两个同名的Arr.php 类,即使实际上的类名可能不存在冲突,但是按照ThinkPHP的规范,类名和文件名是一致的,所以系统会抛出类名冲突的异常,并终止执行。
注意:在Unix或者Linux主机下面是区别大小写的,所以在使用import方法的时候要注意目录名和类库名称的大小写,否则会引入文件失败。
对于import方法,系统会自动识别导入类库文件的位置,ThinkPHP的约定是Think、ORG、Com包的导入以系统基类库为相对起始目录,否则就认为是项目应用类库为起始目录。
import("Think.on");
import("");
上面两个方法分别导入了系统目录Lib/ORG/Util/.php类文件。
要导入项目的应用类库文件也很简单,使用下面的方式就可以了,和导入基类库的方式看起来差不多:
import("MyApp.Actioction");
import("MyApp.Model.InfoModel");
上面的方式分别表示导入MyApp项目下面的Lib/Model/InfoM.phpLib/Action/UserAction.class.php和下的Lib/Think/Util/Sessi.php和类文件。通常我们都是在当前项目里面导入所需的类库文件,所以,我们可以使用下面的方式来简化代码
import("@.Actioction");
import("@.Model.InfoModel");
除了看起来简单一些外,还可以方便项目类库的移植。
如果要在当前项目下面导入其他项目的类库,必须保证两个项目的目录是平级的,否则无法使用
import("Other.GroupModel");
的方式来加载其他项目的类库。
我们知道,按照系统的规则,import方法是无法导入具有点号的类库文件的,因为点号会直接转化成斜线,例如我们定义了一个名称为Use.php 的文件的话,采用:
import("");
方式加载的话就会出现错误,导致加载的文件不是ORG/.php 文件,而是ORG/User/.php 文件,这种情况下,我们可以使用:
import("#Info");
来导入。
对于import方法,系统会自动识别导入类库文件的位置,如果是其它情况的导入,需要指定baseUrl参数,也就是import方法的第二个参数。例如,要导入当前文件所在目录下面的
RBAC/AccessDecisionManage.php 文件,可以使用:
import("sDecisionManager",dirname(__FILE__));
导入第三方类库
我们知道 ThinkPHP 的基类库都是以.class.php 为后缀的,这是系统内置的一个约定,当然也可以通过 import 的参数来控制, 为了更加方便引入其他框架和系统的类库, 系统增加了导入第三方类库的功能, 第三方类库统一放置在系统的Vendor 目录下面,并且使用vendor 方法导入,其参数和 import 方法是 一致的,只是默认的值有针对变化。
例如,我们把 Zend 的 Filte 放到 Vendor 目录下面,这个时候 Dir 文件的路径就是
VendorZendFilte,我们使用vendor 方法导入只需要使用:
Vendor('');
就可以导入Dir类库了。
别名导入
新版ThinkPHP引入了别名导入功能,可以预先定义好相关类库的路径,在需要使用的时候根据定义的别名进行快速导入。别名导入功能已经和import方法整合,所以我们可以统一使用import方法进行导入,例如:
import('AdvModel');
如果有定义AdvModel别名,则import方法会自动加载定义的别名导入。
系统默认的别名定义文件位于系统的Commonalias.php,每个模式和项目都可以定义自己的别名定义文件。
自动加载
在很多情况下,我们可以利用框架的自动加载功能,完成类库的加载工作,而无需我们手动导入所需要使用的类库。这些情况包括:
系统和项目中已经定义的别名导入;
当前项目下面的Action类库和Model类库文件;
自动加载路径中的类库文件;
这里的自动加载路径,是指ThinkPHP的配置参数APP_AUTOLOAD_PATH所定义的路径。
APP_AUTOLOAD_PATH参数是用于设置框架的自动导入的搜索路径的,默认的配置是Thin.,因此才会实现自动导入Think.Util工具类库。例如,我们需要增加.路径作为类库搜索路径,可以使用:
'APP_AUTOLOAD_PATH'=> 'Think.Util.,.',
多个搜索路径之间用逗号分割,并且注意定义的顺序代表了搜索的顺序。
开发流程
使用ThinkPHP创建应用的一般开发流程是:
创建数据库和数据表;(没有数据库操作可略过)
项目命名并创建项目入口文件;
完成项目配置;(无需额外配置可以忽略)
创建控制器类;
创建模型类;(如果只是简单的模型类可以不必创建)
创建模板文件;
运行和调试。
为了顺利完成下面的操作,我们首先在数据库创建一个测试表,以MySQL为例:
CREATE TABLE `think_demo` (
`id` int(11) unsigned NOT NULL auto_increment,
`title` varchar(255) NOT NULL default '',
`content` longtext NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
入口文件
我们给项目命名为Myapp,并且在WWW目录下面创建一个Myapp目录(项目目录),并且把下载的ThinkPHP核心目录放到该目录下面。
然后在Myapp目录下面创建一个入口文件index.php,其中内容如下:
// 定义ThinkPHP框架路径
define('THINK_PATH', './ThinkPHP/');
//定义项目名称和路径
define('APP_NAME', 'Myapp');
define('APP_PATH', '.');
// 加载框架入口文件
require(THINK_PATH."/Think");
//实例化一个网站应用实例
App::run();
>
注意,APP_PATH的路径指的是项目目录所在路径,而不是项目入口文件所在的路径。APP_NAME通常都必须和项目目录名称一致。
如果你的项目入口文件放到项目目录下面的话,可以无需定义APP_NAME和APP_PATH,系统可以自动识别。THINK_PATH通常也不是必须的。
因为我们的入口文件位于项目目录下面,因此,上面的入口文件可以简化为:
// 加载框架入口文件
require(" ./ThinkPHP/Think");
//实例化一个网站应用实例
App::run();
>
自动生成
ThinkPHP具备项目目录自动生成功能,并且不需要使用任何命令行工具。我们只需要简单的浏览器里面访问刚才创建的应用入口文件。
打开浏览器,访问该项目的入口文件:127.0.0.1/Myapp/index.php
这时可以看到项目构建成功后的提示画面,并且在Myapp目录下,已为您构建好了项目目录。
注意:ThinkPHP框架的所有文件都是采用UTF-8编码保存,但是这不影响你的项目中使用其他编码开发和浏览。请注意确保文件保存的时候去掉UTF-8的BOM头信息,防止因产生隐藏的输出而导致程序运行不正常。
注意:如果你是在Linux环境下,要确保项目目录的自动生成,请设置Myapp目录的权限为可写,否则请自行创建相关目录。然后设置Runtime目录为可写权限(通常都是设置目录属性为777)。
项目配置
自动生成的项目目录下面已经为我们创建了一个空的项目配置文件,位于项目的Conf目录下面,名称是co。我们打开这个配置文件,加入我们的数据库配置信息。
return array(
'APP_DEBUG' => true, // 开启调试模式
'DB_TYPE'=> 'mysql', // 数据库类型
'DB_HOST'=> 'localhost', // 数据库服务器地址
'DB_NAME'=>'demo', // 数据库名称
'DB_USER'=>'root', // 数据库用户名
'DB_PWD'=>'', // 数据库密码
'DB_PORT'=>'3306', // 数据库端口
'DB_PREFIX'=>'think_', // 数据表前缀
);?>
根据你本地的数据库连接信息修改上面的配置内容,修改完成后,保存项目配置文件。
业务逻辑
接下来,我们需要实现一个数据添加和查询操作的简单应用,来领略下ThinkPHP的快速开发。
在项目的LibAction目录下面找到自动生成的IndexAct.php文件,这个文件就是ThinkPHP的控制器,也就是Index模块的实现。删除IndexAction类默认生成的index方法。添加新的insert方法和index方法,代码如下:
// 数据写入操作
public function insert() {
$Demo = new Model('Demo'); // 实例化模型类
$Demo->Create(); // 创建数据对象
$result = $Demo->add(); // 写入数据库
$this->redirect('index'); // 成功后重定向到index操作页面
}
// 数据查询操作
public function index() {
$Demo = new Model('Demo'); // 实例化模型类
$list = $Demo->select(); // 查询数据
$this->assign('list',$list); // 模板变量赋值
$this->display(); // 输出模板
}
以上定义后,Index模块就具有了insert和index两个操作,操作方法的定义不需要使用任何参数,而且必须定义为public类型,否则无法访问。
由于只是简单的数据操作应用,所以我们根本不需要创建任何的模型类也同样可以进行CURD操作,这就是新版的魅力所在。^_^
模板定义
控制器和操作方法已经创建完毕,接下来就是定义模板文件了。
项目的自动生成已经为我们生成了Tpl/default目录,我们只需要在default目录下面创建Index目录,表示存放Index模块的模板文件。由于insert操作是后台操作,并不涉及模板输出,因此不需要定义模板文件,所以我们只要为index操作定义模板即可,内容如下:
编号:{$}
标题: {$}
内容: {$nt}
把上面的内容保存为Tpl/default/Index/index.html 即可。
action="__URL__/insert" 表示提交表单到当前模块的insert操作。
运行应用
模板定义完成后,我们就可以运行应用了。我们在浏览器里面输入:
localhost/Myapp/ 就可以看到页面的表单输出了。
由于我们开启了调试模式,所以在页面的最下面还会看到一些额外的调试信息,并且可以很清楚的看到当前页面的请求信息和执行时间、SQL日志,最后还有加载的文件列表,事实上,页面Trace信息的显示完全是可以定制的,而这些内容不需要在模板里面定义。
在ThinkPHP中,我们称之为页面Trace信息,这是为了在开发过程中调试用的,关闭调试模式后,这些信息会自动消失。另外在调试模式下面,由于开启了日志记录,并且关闭了所有缓存,所以执行效率会有一定影响,但是关闭调试模式后,效率会有非常显著的提高。
可以尝试在页面新增数据,会看到页面下面有列表数据输出。到目前为止,我们已经完成了一个完整的数据操作应用了。
配置
ThinkPHP提供了灵活的全局配置功能,采用最有效率的PHP返回数组方式定义,支持惯例配置、项目配置、调试配置和模块配置,并且会自动生成配置缓存文件,无需重复解析的开销。对于有些简单的应用,你无需配置任何配置文件,而对于复杂的要求,你还可以增加模块配置文件,另外ThinkPHP的动态配置使得你在开发过程中可以灵活的动态调整配置参数。
ThinkPHP在项目配置上面创造了自己独有的分层配置模式,其配置层次体现在:
惯例配置项目配置调试配置分组配置模块配置操作(动态)配置
以上是配置文件的加载顺序,但是因为后面的配置会覆盖之前的配置(在没有生效的前提下),所以优先顺序从右到左。系统的配置参数是通过静态变量全局存取的,存取方式非常简单高效。
配置格式
ThinkPHP框架中所有配置文件的定义格式均采用返回PHP数组的方式,格式为:
'APP_DEBUG' => true,
'URL_MODEL' => 2,
// 更多的配置参数
// ……
);?>
配置参数不区分大小写(因为无论大小写定义都会转换成小写),所以下面的配置等效:
'app_debug' => true,
'url_model' => 2,
);?>
但是习惯上保持大写定义的原则。
还可以在配置文件中可以使用二维数组来配置更多的信息,例如:
'APP_DEBUG' => true,
'USER_CONFIG' => array(
'USER_AUTH' => true,
'USER_TYPE' => 2,
),
);?>
系统目前最多支持二维数组的配置级别,每个项目配置文件除了定义ThinkPHP所需要的配置参数之外,开发人员可以在里面添加项目需要的一些配置参数,用于自己的应用。项目配置文件的位置默认位于项目的Conf目录。
惯例配置
惯例重于配置是ThinkPHP的一个重要思想,系统内置有一个惯例配置文件(位于ThinkCommonconvention.php),按照大多数的使用对常用参数进行了默认配置。所以,对于应用项目的配置文件,往往只需要配置和惯例配置不同的或者新增的配置参数,如果你完全采用默认配置,甚至可以不需要定义任何配置文件。
(如果需要了解惯例配置中的详细配置列表请参考附录的配置参考部分。)
项目配置
这里的项目配置指的是项目的全局配置,因为一个项目除了可以定义项目配置文件之外,还可以定义模块配置文件用于针对某个特定的模块进行特殊的配置。他们的定义格式都是一致的,区别只是配置文件命名的不同。系统会自动在不同的阶段读取配置文件。
项目配置文件位于项目的配置文件目录(默认是Conf)下面,文件名是config.php。
在项目配置文件里面除了添加内置的参数配置外,还可以额外添加项目需要的配置参数。
后面的开发指南中提及的配置参数设置如未特别说明,都是指在项目配置文件中定义。
调试配置
如果启用了调试模式的话,那么会导入框架默认的调试配置文件,默认的调试配置文件位于ThinkCommondebug.php,如果没有检测到项目的调试配置文件,就会直接使用默认的调试配置参数。项目定义了自身的调试配置文件的话,则会和默认的调试配置文件合并,也就是说,项目配置文件也只 需要配置和默认调试配置不同的参数或者新增的参数。
调试配置文件也位于项目配置目录下面,文件名是d。
通常情况下,调试配置文件里面可以进行一些开发模式所需要的配置。例如,配置额外的数据库连接用于调试,开启日志写入便于查找错误信息、开启页面Trace输出更多的调试信息等等。系统默认的调试配置文件中设置了:
开启日志记录
关闭模板缓存
记录SQL日志
关闭字段缓存
开启运行时间详细显示(包括内存、缓存情况)
开启页面Trace信息显示
严格检查文件大小写(即使是Windows平台)
由于以上的设置涉及到较多的文件IO操作和模板实时编译,所以在开启调试模式的情况下,性能会有一定的下降,不过不用担心,一旦关闭调试模式,性能即可恢复理想的效果。
分组配置
分组配置用于系统启用了分组模式的情况之下,对于每个分组可以单独定义自己的配置文件。
分组配置文件位于:项目配置目录/分组名称/confi
分组配置的定义格式和项目配置是一样的。分组名称区分大小写。
模块配置
ThinkPHP支持对某些参数进行动态配置,针对这一特性,ThinkPHP还特别引入了模块配置文件的支持,这其实也是动态配置的体现。模块配置文件位于:
项目配置目录/模块名(小写)_conf // 用于不使用分组的情况
或者
项目配置目录/分组名/模块名(小写)_conf // 用于使用分组的情况
模块配置文件的定义格式和项目配置相同。需要注意的是,有些配置参数在读取模块配置之前已经生效,因此可能会发生定义后不起作用的情况。
读取配置
定义了配置文件之后,可以使用系统提供的C方法来读取已有的配置:
C('参数名称') // 获取已经设置的参数值
例如,C('APP_DEBUG')可以读取到系统的调试模式的设置值,同样,由于配置参数不区分大小写,因此C('app_debug') 是等效的,但是建议使用大写方式的规范。
如果APP_DEBUG尚未存在设置,则返回NULL。
C方法同样可以用于读取二维配置:
C('USER_CONFI_TYPE') //获取用户配置的用户类型设置
因为配置参数是全局有效的,因此C方法可以在任何地方读取任何配置,哪怕某个设置参数已经生效过期了。后面我们还会了解到C方法同样还具有给配置参数赋值的作用。(如果对C方法的命名比较奇怪的话,可以借助Config单词来帮助记忆)
动态配置
之前的方式都是通过预先定义配置文件的方式,而在具体的Action方法里面,我们仍然可以对某些参数进行动态配置,主要是指那些还没有被使用的参数。
设置新的值:
C('参数名称','新的参数值');
例如,我们需要动态改变数据缓存的有效期的话,可以使用
C(' DATA_CACHE_TIME','60');
动态改变配置参数的方法和读取配置的方法在使用上面非常接近,都是使用C方法,只是参数的不同。因此掌握C方法的使用对于掌握配置有着关键的作用。
也可以支持二维数组的读取和设置,使用点语法进行操作,如下:
获取已经设置的参数值:
C('USER_CONFI_TYPE')
设置新的值:
C('USER_CONFI_TYPE','1');
扩展配置
新版的配置文件都具有扩展能力,以往的项目配置文件只有一个配置文件(调试配置和模块配置文件除外),但是新版可以增加任何需要的配置文件定义,在真正执行的过程中会自动汇总到项目配置缓存里面去,而且都可以通过C方法来调用。
通常扩展配置文件的定义是为了某个特殊的需要,而分离出来的配置文件,这样的目的是为了便于维护和便于管理。系统也内置了一些扩展配置文件的定义,其中包括标签库定义,路由定义,静态定义,扩展模块定义,扩展操作定义,标签定义。惯例配置如下:
'APP_CONFIG_LIST' => array('taglibs','routes','htmls','modules','actions','tags'),
对于已经定义好的扩展配置文件系统会自动导入,并加入项目配置的缓存文件里面。例如:
路由配置文件rout的定义会自动并入:
C('_routes_');
后面怎么用这个扩展配置,就完全看应用自己的需要了,扩展配置对于扩展配置文件的某个配置项的获取,使用下面的方式:
C('_扩展配置名称_.configName');
// 例如
C('_modules_.extend');
如果需要增加额外的扩展配置文件,只需要在项目的配置文件里面增加额外的配置文件名称即可,例如:
'APP_CONFIG_LIST' => array('taglibs','routes','htmls','modules','actions','tags','myconfig')
注意事项:
扩展配置文件更改后,需要删除项目编译缓存文件才会生效;
对于没有定义的扩展配置文件系统不会自动加载;
注意扩展配置文件里面的配置参数的获取方式有别于一般的项目配置参数。
考虑到扩展配置的特殊需要,扩展配置里面的设置项是有大小写区分的。
模块和操作
ThinkPHP采用模块和操作的方式来执行,首先,用户的请求会通过入口文件生成一个应用实例,应用控制器(我们称之为核心控制器)会管理整个用户执行的过程,并负责模块的调度和操作的执行,并且在最后销毁该应用实例。任何一个WEB行为都可以认为是一个模块的某个操作,系统会根据当前的URL来分析要执行的模块和操作。这个分析工作由URL调度器来实现,官方内置了Dispatcher类来完成该调度。
在Dispatcher调度器中,会根据
servername/appName/moduleName/actionName/params
来获取当前需要执行的项目(appName)、模块(moduleName)和操作(actionName),在某些情况下,appName可以不需要(通常是网站的首页,因为项目名称可以在入口文件中指定,这种情况下,appName就会被入口文件替代)。在复杂一点的情况下面,可能还会出现分组(groupName)。
每个模块是一个Action文件,类似于我们平常所说的控制器,系统会自动寻找项目类库Action目录下面的相关类,如果没有找到,则会定位到空模块,否则抛出异常。
而actionName操作是首先判断是否存在Action类的公共方法,如果不存在则会继续寻找父类中的方法,如果依然不存在,就会寻找是否存在自动匹配的模版文件。如果存在模版文件,那么就直接渲染模版输出。
因此应用开发中的一个重要过程就是给不同的模块定义具体的操作。一个应用如果不需要和数据库交互的时候可以不需要定义模型类,但是必须定义Action控制器。
Action控制器的定义非常简单,只要继承Action基础类就可以了,例如:
Class UserAction extends Action{
}
如果我们要执行下面的URL
servername/index.php/User/add
你需要增加一个add方法就可以了,例如
Class UserAction extends Action{
// 定义一个add操作方法,注意操作方法不需要任何参数
Public function add(){
// add操作方法的逻辑实现
//……
$this->display(); // 输出模板页面
}
}
操作方法必须定义为Public类型,否则会报错。并注意操作方法的命名不要和内置的Action类的方法重复。系统会自动定位当前操作的模板文件,而默认的模板文件应该位于项目目录下面的Tpldefau。
默认模块和操作
如果使用http://
可以在项目配置文件中修改默认模块和默认操作的名称。
模块分组
模块分组功能是为了更好的组织已有的模块,并且增加项目容量的一个有效机制。分组功能可以把以往的多项目合并到一个项目中去,这样一来,之前需要采用跨项目操作的地方,现在因为在一个项目中从而免去了不少麻烦,并且公共文件的重用也方便了,并且每个分组都可以有自己独立的配置文件、公共文件、语言包,在URL的访问上面也非常清晰。
要启用分组模块非常简单,配置下APP_GROUP_LIST参数和DEFAULT_GROUP参数即可。
例如我们把当前的项目分成Home和Admin两个组,分别表示前台和后台功能,那么只需要进行下面的配置:
'APP_GROUP_LIST'=>'Admin,Home',
'DEFAULT_GROUP'=>'Home',
需要注意的是,一定要把上面的配置参数放入项目的配置文件,而不是项目的分组配置或者模块配置文件。多个分组之间用逗号分隔即可,默认分组只允许设置一个。
在我们启用项目分组之前,由于使用的两个项目,所以URL地址分别是:
采用了分组模式后,URL地址变成:
如果Home是默认分组的话 还可以变成
如果设置了隐藏ind的话,两者的URL表现效果基本上是一致的,但是从管理和公共调用的角度来看,确实方便了不少。当使用分组模式时,目录结构只是做了一点小小的扩展,主要区别在于项目类库目录和模板目录下面多了一层分组目录。
如果不使用分组模式的话,Action目录下面应该是所有的Action类库,现在我们可以在Action目录下面创建自己的分组目录,例如我们把当前项目分成了Home和Admin两个组,那么就需要在Action目录下面创建Home和Admin目录,然后把属于各自的Action类库放到对应的目录下面。如果某个Action类库是每个分组都需要使用或者公共继承的话,可以把这个公共Action类库放到分组目录之外,并且利用ThinkPHP的自动加载机制无需手动引入。
使用了模块分组后,如果需要实例化其他分组的模块类,可以使用:
A('');// 实例化Home分组的UserAction类
对于分组模式下面的Model类库是否需要分组完全看项目的需要,由于通常不同的分组对应的数据表是相同的,因此,我们推荐Model类库不分组存放,仍然保留之前的方式,无论是什么分组都公共调用Model类库。如果确实需要分组的话,仍然可以按照Action的方式,在Model目录下面创建Home和Admin目录,然后放入对应的Model类库,采用这种方式的话,模型类的调用方法有所区别。
如果模型类也分组存放,在使用D方法调用的时候需要使用:
$User = D('');// 实例化Home分组下面的UserModel类
模板文件的分组和Action类库分组也基本类似,在原来的模板主题目录下面增加一个分组目录即可。
例如:
Tpl/default/Home/Index/index.html
Tpl/default/Admin/User/index.html
相比之前的模板文件位置就是多了一个分组目录Home和Admin,如果觉得目录结构太深了,可以配置 TMPL_FILE_DEPR参数 来减少目录层次,该参数默认是
“/”,如果改成
'TMPL_FILE_DEPR'=>'_'
那么分组的模板文件就变成了
Tpl/default/Home/Index_inde
Tpl/default/Admin/User_index.html
分组模块的概念,并不局限于将项目区分为前台和后台。你可以按自己所需类型,进行明确细致的区分,这样非常方便于项目管理和开发部署。
分组模块下面的具体模块和之前的模块功能没有任何区别,已有的URL和模块功能都可以很好的支持,例如空模块、空操作、伪静态等等。
更多的关于分组模式下面URL方面的区别可以查看URL生成部分的U方法的使用。
URL模式
我们在上面的执行过程里面看到的URL是默认情况下,其实ThinkPHP支持四种URL模式,可以通过设置URL_MODEL参数来定义,包括普通模式、PATHINFO、REWRITE和兼容模式。
一、普通模式 :设置URL_MODEL 为0
采用传统的URL参数模式
普通URL模式和在关闭URL_DISPATCH_ON的情况下面的效果是一样的,只是普通URL模式还具有路由功能。如果你并不需要使用路由功能,而且还在使用普通模式的话,建议直接关闭URL_DISPATCH_ON,效率会更高。
二、PATHINFO模式 :设置URL_MODEL 为1
默认情况使用PATHINFO模式,ThinkPHP内置强大的PATHINFO支持,提供灵活和友好URL支持。PATHINFO模式根据不同的设置还包括普通模式和智能模式两种:
普通模式 设置URL_PATHINFO_MODEL参数为1
该模式下面URL参数没有顺序,例如
以上URL等效
智能模式 设置URL_PATHINFO_MODEL参数为2 (系统默认的模式)
自动识别模块和操作,例如
在智能模式下面,第一个参数会被解析成模块名称(或者路由名称,下面会有描述),第二个参数会被解析成操作(在第一个参数不是路由名称的前提下),后面的参数是显式传递的,而且必须成对出现,例如:
其中参数之间的分割符号由URL_PATHINFO_DEPR参数设置,默认为”/”,例如我们设置URL_PATHINFO_DEPR为“-”的话,就可以使用下面的URL访问
注意不要使用”:” 和”&”符号进行分割,该符号有特殊用途。
略加修改,就可以展示出富有诗意的URL,呵呵~
如果想要简化URL的形式可以通过路由功能(后面会有描述)以及空模块和空操作。
在PATH_INFO模式下面,会把相关参数转换成GET变量,以及并入REQUEST变量,因此不妨碍URL里面的GET和REQUEST变量获取。
三、REWRITE模式: 设置URL_MODEL 为2
该URL模式和PATHINFO模式功能一样,除了可以不需要在URL里面写入口文件,和可以定义.htaccess 文件外。在开启了Apache的URL_REWRITE模块后,就可以启用REWRITE模式了,具体参考下面的URL重写部分。
四、兼容模式: 设置URL_MODEL 为3
兼容模式是普通模式和PATHINFO模式的结合,并且可以让应用在需要的时候直接切换到PATHINFO模式而不需要更改模板和程序。兼容模式URL可以支持任何的运行环境。
兼容模式的效果是:
并且也可以支持参数分割符号的定义,例如在URL_PATHINFO_DEPR为~的情况下,下面的URL有效:
其实是利用了VAR_PATHINFO参数,用普通模式的实现模拟了PATHINFO的模式。但是兼容模式并不需要自己传s变量,而是由系统自动完成URL部分。正是由于这个特性,兼容模式可以和PATHINFO模式之间直接切换,而不需更改模板文件里面的URL地址连接。
某些服务器环境不能良好的支持PATHINFO,或者需要进行额外的配置才可以支持,如果你确认你的服务器环境不支持PATHINFO,可以选择普通模式或者兼容模式URL运行。
URL路由
ThinkPHP支持URL路由功能,要启用路由功能,需要设置URL_ROUTER_ON 参数为true。开启路由功能后,系统会自动进行路由检测,如果在路由定义里面找到和当前URL匹配的路由名称,就会进行路由解析和重定向。路由功能需要定义路由定义文件,位于项目的配置目录下面,文件名为routes.php,定义格式:
return array(
// 第一种方式 常规路由
'RouteName'=>array('模块名称', '操作名称', '参数定义', '额外参数'),
// 第二种方式 泛路由
'RouteName@'=>array(
array('路由匹配正则', '模块名称', '操作名称', '参数定义', '额外参数'),
),
…更多的路由名称定义
)
系统在执行Dispatch解析的时候,会判断当前URL是否存在定义的路由名称,如果有就会按照定义的路由规则来进行URL解析。例如,我们启用了路由功能,并且定义了下面的一个路由规则:
'blog'=>array('Blog', 'archive', 'year,month,day', 'userId=1&status=1')
那么我们在执行
另外一种路由参数的传入是
如果需要路由到分组模块的话,可以定义成
'blog'=>array('', 'archive', 'year,month,day', 'userId=1&status=1')
就可以指定路由到Home分组的Blog模块。
泛路由支持
泛路由指的是对同一个路由名称提供了多个规则的支持,使得URL的设置更加灵活,例如,我们对Blog路由名称需要有多个规则的路由:
'Blog@'=>array(
array('/^(d+)(pd)?$/','Blog','read','id'),
array('/^(d+)(d+)/','Blog','archive','year,month'),
),
第一个路由规则表示解析 Blog/123 这样的URL到Blog模块的read操作
第二个路由规则表示解析 Blog/2009/10 这样的URL到Blog模块的archive操作
泛路由的定义难度就在路由正则的定义上面,其它参数和常规路由的使用一致。
举个简单路由的例子,如果我们有一个City模块,而我们希望能够通过类似下面这样的URL地址来访问具体某个城市的操作:
shanghai这个操作方法是不存在的,我们给相关的城市操作定义了一个city方法,如下:
Class CityAction extends Action{
public function city(){
// 读取城市名称
$cityName = $_GET['name'];
Echo ('当前城市: '.$cityName);
}
}
接下来我们来定义路由文件,实现类似于
这样的解析,路由文件名称是
return array(
'City'=>array('City', 'city', 'name');
);
这样,URL里面所有的City模块都会被路由到City模块的city操作,而后面的第二个参数会被解析成 $_GET['name']
接下来,我们就可以在浏览器里面输入
会看到依次输出的结果是:
当前城市:beijing
当前城市:shanghai
当前城市:shenzhen
URL伪静态
系统支持伪静态URL设置,可以通过设置URL_HTML_SUFFIX参数随意在URL的最后增加你想要的静态后缀,而不会影响当前操作的正常执行。例如,我们设置URL_HTML_SUFFIX
为 .shtml 的话,我们可以把下面的URL
变成
后者更具有静态页面的URL特征,但是具有和前面的URL相同的执行效果,并且不会影响原来参数的使用。注意配置设置时要包含后缀中的“.”。
伪静态设置后,如果需要动态生成一致的URL,可以使用U方法在模板文件里面生成URL。
关于U方法的使用请参考后面的URL生成部分。
URL重写
通常的URL里面含有index.php,为了达到更好的SEO效果可能需要去掉URL里面的i ,通过URL重写的方式可以达到这种效果,通常需要服务器开启URL_REWRITE模块才能支持。
下面是Apache的配置过程,可以参考下:
1、httpd.conf配置文件中加载了mod_rewr模块
2、AllowOverride None 将None改为 All
3、确保URL_MODEL设置为2
4、把.htaccess文件放到入口文件的同级目录下
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
URL生成
为了配合所使用的URL模式,我们需要能够动态的根据当前的URL设置生成对应的URL地址,为此,ThinkPHP内置提供了U方法,用于URL的动态生成,可以确保项目在移植过程中不受环境的影响。
U方法的定义规则如下(方括号内参数根据实际应用决定):
U('[项目://][路由@][分组名-模块/]操作? 参数1=值1[&参数N=值N]')
或者用数组的方式传入参数
U('[项目://][路由@][分组名-模块/]操作',array('参数1'=>'值1' [,'参数N'=>'值N']))
如果不定义项目和模块的话 就表示当前项目和模块名称,下面是一些简单的例子:
U('Myapp://User/add') // 生成Myapp项目的User模块的add操作的URL地址
U('Blog/read?id=1') // 生成Blog模块的read操作 并且id为1的URL地址
U('Admin-User/select') // 生成Admin分组的User模块的select操作的URL地址
参数请确保使用 ?id=1&name=tp或者数组的方式来定义,虽然有些情况下
U('Blog/read/id/1')和U('Blog/read?id=1')的效果一样,但是在不同的URL设置情况下,会导致解析的错误。
根据项目的不同URL设置,同样的U方法调用可以智能地对应产生不同的URL地址效果,例如针对
U('Blog/read?id=1')这个定义为例。
如果当前URL设置为普通模式的话,最后生成的URL地址是:
如果当前URL设置为PATHINFO模式的话,同样的方法最后生成的URL地址是:
如果当前URL设置为REWRITE模式的话,同样的方法最后生成的URL地址是:
如果当前URL设置为REWRITE模式,并且设置了伪静态后缀为.html的话,同样的方法最后生成的URL地址是:
U方法还可以支持路由,如果我们定义了一个名称为View的路由,指向Blog模块的read操作,参数是id,那么U('View@?id=1')生成的URL地址是:
URL大小写
我们知道,系统默认的规范是根据URL里面的moduleName和actionName来定位到具体的模块类,从而执行模块类的操作方法,如果在Linux环境下面,就会发生URL里面使用小写模块名不能找到模块类的情况,例如在Linux环境下面,我们访问下面的URL是正常的:
但是,如果使用
就会出现user模块不存在的错误。因为,我们定义的模块类是UserAction而不是userAction,但是后者显然不符合ThinkPHP的命名规范,显然这样的问题会造成用户体验的下降。
其实,系统本身已经提供了一个很好的解决方案,可以通过配置简单实现。
只要在项目配置中,增加:
'URL_CASE_INSENSITIVE' => true
就可以实现URL访问不再区分大小写了。
将等效于
这里需要注意一个地方,如果我们定义了一个UserTypeAction的模块类,那么URL的访问应该是:
而不是
如果设置
'URL_CASE_INSENSITIVE' => false
的话,URL就又变成:
空操作
空操作是指系统在找不到指定的操作方法的时候,会定位到空操作(_empty)方法来执行,利用这个机制,我们可以实现错误页面和一些URL的优化。
例如,我们前面用URL路由实现了一个城市切换的功能,下面我们用空操作功能来重新实现。
我们只需要给CityAction类定义一个_emtpy(空操作)方法:
Class CityAction extends Action{
Public function _empty(){
// 把所有城市的操作都解析到city方法
$cityName = ACTION_NAME;
$this->city($cityName);
}
// 注意city方法本身是protected方法
Protected function city($name){
// 和$name 这个城市相关的处理
Echo ('当前城市: '.$name);
}
}
接下来,我们就可以在浏览器里面输入
会看到依次输出的结果是:
当前城市:beijing
当前城市:shanghai
当前城市:shenzhen
可以看出来,和用URL路由实现的效果是一样的,而且不需要定义路由定义文件。
空模块
空模块的概念是指当系统找不到指定的模块名称的时候,系统会尝试定位空模块(EmptyAction),利用这个机制我们可以用来定制错误页面和进行URL的优化。
现在我们把前面的需求进一步,把URL由原来的
变成
这样更加简单的方式,如果按照传统的模式,我们必须给每个城市定义一个Action类,然后在每个Action类的index方法里面进行处理。 可是如果使用空模块功能,这个问题就可以迎刃而解了。 我们可以给项目定义一个EmptyAction类
Class EmptyAction extends Action{
Public function index(){
// 根据当前模块名称来判断要执行哪个城市的操作
$cityName = MODULE_NAME;
$this->city($cityName);
}
Protected function city($name){
// 和$name 这个城市相关的处理
Echo ('当前城市: '.$name);
}
}
接下来,我们就可以在浏览器里面输入
会看到依次输出的结果是:
当前城市:beijing
当前城市:shanghai
当前城市:shenzhen
前置和后置操作
系统会检测当前操作是否具有前置和后置操作,如果存在就会按照顺序执行,例如,我们在UserAction类里面定义了_before_insert() 和 _after_insert() 操作,那么执行User模块的insert操作的时候,会按照顺序执行下面的操作:
_before_insert
insert
_after_insert
特殊情况是,当前的add操作并没有定义操作方法,而是直接渲染模板文件,那么如果定义了_before_add 和 _after_add 方法的话,依然会生效,也会按照这个顺序来执行add操作。真正有模板输出的可能仅仅是当前的add操作,前置和后置操作一般情况是没有任何输出的。前置和后置操作的方法名是在要执行的方法前面加 _before_和_after_,例如:
Class CityAction extends Action{
public function _before_index() {
echo 'before';
}
public function index(){
echo 'index';
}
public function _after_index() {
echo 'after';
}
}
执行结果会先输出before 然后输出index 最后输出after。对于任何操作方法我们都可以按照这样的规则来定义前置和后置方法。
需要注意的是,在有些方法里面使用了exit或者错误输出之类的话 有可能不会再执行after后置方法了。
操作链
ThinkPHP支持使用操作链的方式,例如,我们访问下面的URL:
serverName/appName/User/action1:action2:action3/
那么会依次执行UserAction的action1 action2 action3方法,并且当前操作名称是最后一个操作。在进行默认模板输出的时候会用到。如果确实需要在不同的操作方法中都进行输出,请确保在Action的display方法中指定需要渲染的模板文件名。否则,只能输出最后的操作模板。使用了操作链后,前置和后置方法会失效。
跨模块调用
在开发过程中经常会在当前模块调用其他模块的方法,这个时候就涉及到跨模块调用,我们还可以了解到A和R两个快捷方法的使用。
$User = A("User"); // 实例化UserAction控制器对象
$User->importUser(); // 调用User模块的importUser操作方法
这里的A("User") 是一个快捷方法,和下面的代码等效:
import("@.Actioction");
$User = new UserAction();
事实上,在这个例子里面还有比A方法更简单的调用方法,例如:
R("User","importUser"); // 远程调用UserAction控制器的importUser操作方法
上面只是在当前项目中调用,如果你有需要在多个项目之间调用方法,一样可以完成:
$User = A("User","App2"); // 实例化App2项目的UserAction控制器对象
$User->importUser();
// 远程调用App2项目的UserAction控制器的importUser操作方法
R("User","importUser","App2");
页面跳转
在应用开发中,经常会遇到一些带有提示信息的跳转页面,例如操作成功或者操作错误页面,并且自动跳转到另外一个目标页面。系统的Action类内置了两个跳转方法success和error,用于页面跳转提示,而且可以支持ajax提交。使用方法很简单,举例如下:
$User = M("User"); // 实例化User对象
$result = $User->add($data);
if ($result){
// 设置成功后的跳转页面地址 默认的返回页面是$_SERVER["HTTP_REFERER"]
$this->assign("jumpUrl","/User/list/");
$this->success("新增成功!");
}else{
// 错误页面的默认跳转页面是返回上一页 通常可以不用设置
$this->error("新增错误!");
}
Success和error方法都有对应的模板,并且是可以设置的,默认的设置Public:success和Public:error,模板文件可以使用模板标签,并且可以使用下面的模板变量:
$msgTitle:操作标题
$message:页面提示信息
$status:操作状态 1表示成功 0 表示失败 具体还可以由项目本身定义规则
$waitSecond:跳转等待时间 单位为妙
$jumpUrl:跳转页面地址
如果是AJAX方式提交的话,success和error方法会调用ajaxReturn方法返回信息,具体可以参考后面的AJAX返回部分。
重定向
Action类的redirect方法可以实现页面的重定向功能。
redirect方法的参数用法和U函数的用法一致(参考上面的URL生成部分),例如:
$this->redirect('User/list', array('cate_id'=>2), 5,'页面跳转中~')
上面的用法是停留5秒后跳转到User模块的list操作,并且显示页面跳转中字样,重定向后会改变当前的URL地址。
AJAX返回
系统支持任何的AJAX类库,提供了ajaxReturn方法用于AJAX调用后返回数据给客户端。
并且支持JSON、XML和EVAL三种方式给客户端接受数据,通过配置DEFAULT_AJAX_RETURN进行设置,在选择不同的AJAX类库的时候可以使用不同的方式返回数据。
要使用ThinkPHP的ajaxReturn方法返回数据的话,需要遵守一定的返回数据的格式规范。ThinkPHP返回的数据格式包括:
status操作状态
info提示信息
data返回数据
返回数据data可以支持字符串、数字和数组、对象,返回客户端的时候根据不同的返回格式进行编码后传输。如果是JSON格式,会自动编码成JSON字符串,如果是XML方式,会自动编码成XML字符串,如果是EVAL方式的话,只会输出字符串data数据,并且忽略status和info信息。
下面是一个简单的例子:
$User = M("User"); // 实例化User对象
$result = $User->add($data);
if ($result){
// 成功后返回客户端新增的用户ID,并返回提示信息和操作状态
$this->ajaxReturn($result,"新增成功!",1);
}else{
// 错误后返回错误的操作状态和提示信息
$this->ajaxReturn(0,"新增错误!",0);
}
注意,确保你是使用AJAX提交才使用ajaxReturn方法。
在客户端接受数据的时候,根据使用的编码格式进行解析即可。
定义和实例化
在ThinkPHP2.0版本中,可以无需进行任何模型定义。只有在需要封装单独的业务逻辑的时候,模型类才是必须被定义的,因此ThinkPHP在模型上有很多的灵活和方便性,让你无需因为表太多而烦恼。
根据不同的模型定义,我们有几种实例化模型的方法,下面来分析下什么情况下用什么方法:
1、实例化基础模型(Model) 类
在没有定义任何模型的时候,我们可以使用下面的方法实例化一个模型类来进行操作:
$User = new Model('User');
或者使用M快捷方法实例化是等效的
$User = M('User');
$User->select(); // 进行其他的数据操作
这种方法最简单高效,因为不需要定义任何的模型类,所以支持跨项目调用。缺点也是因为没有自定义的模型类,因此无法写入相关的业务逻辑,只能完成基本的CURD操作。
2、实例化其他模型类
第一种方式实例化因为没有模型类的定义,因此很难封装一些额外的逻辑方法,不过大多数情况下,也许只是需要扩展一些通用的逻辑,那么就可以尝试下面一种方法。
M方法默认是实例化Model类,如果需要实例化其他模型类,可以使用
$User = M('User', 'CommonModel');
上面的方法等效于
$User = new CommonModel('User');
因为系统的模型类都能够自动加载,因此我们不需要在实例化之前手动进行类库导入操作。模型类commonModel必须继承Model,如果没有定义别名导入的话,需要放在项目Model下。我们可以在CommonModel类里面定义一些通用的逻辑方法,就可以省去为每个数据表定义具体的模型类,如果你的项目已经有超过100个数据表了,而大多数情况都是一些基本的CURD操作的话,只是个别模型有一些复杂的业务逻辑需要封装,那么第一种方式和第二种方式的结合是一个不错的选择。
3、实例化用户定义的模型(×××Model)类
这种情况是使用的最多的,一个项目不可避免的需要定义自身的业务逻辑实现,就需要针对每个数据表定义一个模型类,例如UserModel 、InfoModel等等。
定义的模型类通常都是放到项目的LibModel目录下面。例如,
class UserModel extends Model{
Public function myfun(){
// 添加自己的业务逻辑
// ………
}
}
其实模型类还可以继承一个用户自定义的公共模型类,而不是只能继承Model类。
要实例化自定义模型类,可以使用下面的方式:
$User = new UserModel();
或者使用D快捷方法实例化是等效的
$User = D('User');
$User->select(); // 进行其他的数据操作
D方法可以自动检测模型类,不存在时系统会抛出异常,同时对于已实例化过的模型,不会重复去实例化。默认的D方法只能支持调用当前项目的模型,如果需要跨项目调用,需要使用:
$User = D('User', 'Admin'); // 实例化Admin项目下面的User模型
$User->select();
如果启用了模块分组功能,可使用:
$User = D('Admin.User');
4、实例化空模型类
如果你仅仅是使用原生SQL查询的话,不需要使用额外的模型类,实例化一个空模型类即可进行操作了,例如:
$Model = new Model();
// 或者使用M快捷方法实例化是等效的
// $Model = M();
$Model->query('SELECT * FROM think_user where status=1');
空模型类也支持跨项目调用。
在后面的内容中,针对M方法或者D方法将不再具体说明,请自行分析。
模型命名
当我们创建一个UserModel类的时候,其实已经遵循了系统的约定。ThinkPHP要求数据库的表名和模型类的命名遵循一定的规范,首先数据库的表名和字段全部采用小写形式,模型类的命名规则是除去表前缀的数据表名称,并且首字母大写,然后加上模型类的后缀定义,例如:
UserModel 表示User数据对象,(假设数据库的前缀定义是 think_)其对应的数据表应该是
think_user
UserTypeModel 对应的数据表是 think_user_type
如果你的规则和系统的约定不符合,那么需要设置Model类的tableName属性。
在ThinkPHP的模型里面,有两个数据表名称的定义:
1、tableName不包含表前后缀的数据表名称,一般情况下默认和模型名称相同,只有当你的表名和当前的模型类的名称不同的时候才需要定义。
2、trueTableName包含前后缀的数据表名称,也就是数据库中的实际表名,该名称无需设置,只有当上面的规则都不适用的情况或者特殊情况下才需要设置。
下面举个例子来加深理解:
例如,在数据库里面有一个think_categories表,而我们定义的模型类名称是CategoryModel,按照系统的约定,这个模型的名称是Category,对应的数据表名称应该是think_category(全部小写),但是现在的数据表名称是think_categories,因此我们就需要设置tableName属性来改变默认的规则(假设我们已经在配置文件里面定义了DB_PREFIX
为 think_)。
protected $tableName = 'categories';
注意这个属性的定义不需要加表的前缀think_
而对于另外一种特殊情况,数据库中有一个表(top_depts)的前缀和其它表前缀不同,不是think_ 而是 top_,这个时候我们就需要定义 trueTableName 属性了
protected $trueTableName = 'top_depts';
注意trueTableName需要完整的表名定义
除了数据表的定义外,还可以对数据库进行定义:
dbName定义模型当前对应的数据库名称,只有当你当前的模型类对应的数据库名称和配置文件不同的时候才需要定义,例如:
protected $dbName = 'top';
另外,我们来了解下表后缀的含义。表后缀通常情况下用处不大,因为这个和表的设计有关。但是个别情况下也是有用,例如,我们在定义数据表的时候统一采用复数形式定义,下面是我们设计的几个表名 think_users、think_categories、think_blogs,我们定义的模型类分别是UserModel 、CategoryModel 、BlogModel,按照上面的方式,我们必须给每个模型类定义tableName属性。其实我们可以通过设置表后缀的方式来实现相同的效果,我们可以设置DB_SUFFIX 配置参数为s,那么系统在获取真实的表名的时候就会自动加上这个定义的表后缀,我们就不必给每个模型类定义tableName属性了,而只是对categories这样的复数情况单独定义trueTableName属性就可以了。
获取字段
我们在UserModel类里面根本没有定义任何User表的字段信息,但是系统是如何做到属性对应数据表的字段呢?这是因为ThinkPHP可以在运行时自动获取数据表的字段信息(确切的说,是在第一次运行的时候,而且只需要一次,以后会永久缓存字段信息,除非设置不缓存或者删除),包括数据表的主键字段和是否自动增长等等,如果需要显式获取当前数据表的字段信息,可以使用模型类的getDbFields方法来获取。如果你在开发过程中
版权声明:本文标题:Thinkphp开发手册(完整版) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/free/1705047758h471134.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论