结构

框架目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
├─ThinkPHP.php     框架入口文件
├─Common 框架公共文件
├─Conf 框架配置文件
├─Extend 框架扩展目录
├─Lang 核心语言包目录
├─Lib 核心类库目录
│ ├─Behavior 核心行为类库
│ ├─Core 核心基类库
│ ├─Driver 内置驱动
│ │ ├─Cache 内置缓存驱动
│ │ ├─Db 内置数据库驱动
│ │ ├─TagLib 内置标签驱动
│ │ └─Template 内置模板引擎驱动
│ └─Template 内置模板引擎
└─Tpl 系统模板目录

使项目的入口文件移动到app目录的外面

修改入口文件index.php的内容为

1
2
3
4
<?php
define('APP_NAME','app'); //定义项目名
define('APP_PATH','./app/'); //项目目录
require '/ThinkPHP框架所在目录/ThinkPHP.php';

项目目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
├─index.php     项目入口文件
├─Common 项目公共文件目录
├─Conf 项目配置目录
├─Lang 项目语言目录
├─Lib 项目类库目录
│ ├─Action Action类库目录
│ ├─Behavior 行为类库目录
│ ├─Model 模型类库目录
│ └─Widget Widget类库目录
├─Runtime 项目运行时目录
│ ├─Cache 模板缓存目录
│ ├─Data 数据缓存目录
│ ├─Logs 日志文件目录
│ └─Temp 临时缓存目录
└─Tpl 项目模板目录

项目配置简介

配置

每个项目都有一个独立的配置文件(位于项目目录的Conf/config.php),配置文件的定义格式均采用PHP返回数组的方式,例如:

1
2
3
4
5
6
// 项目配置文件
return array(
'配置参数' => '配置值',
// 更多配置参数
//...
);

一旦有需要,我们就可以在项目配置文件中添加相关配置项目。通常我们提到的添加配置项目,就是指在项目配置文件中添加:

1
'配置参数' => '配置值', 

控制器

需要为每个模块定义一个控制器类,控制器类的命名规范是:
模块名+Action.class.php (模块名采用驼峰法并且首字母大写)
系统的默认模块是Index,对应的控制器就是项目目录下面的Lib/Action/IndexAction.class.php,类名和文件名一致。默认操作是index,也就是控制器的一个public方法。初次生成项目目录结构的时候,系统已经默认生成了一个默认控制器(就是之前看到的欢迎页面),我们把index方法改成下面的代码:

1
2
3
4
5
class IndexAction extends Action {
public function index(){
echo 'hello,world!';
}
}

URL请求

入口文件是项目的单一入口,对项目的所有请求都定向到项目的入口文件,系统会从URL参数中解析当前请求的模块和操作,我们之前访问的URL地址中没有任何参数,因此系统会访问默认模块(Index)的默认操作(index),因此下面的访问和之前是等效的:

1
http://localhost/app/index.php/Index/index

这种URL模式就是系统默认的PATHINFO模式,不同的URL模式获取模块和操作的方法不同,ThinkPHP支持的URL模式有四种:普通模式PATHINFOREWRITE兼容模式

普通模式

也就是传统的GET传参方式来指定当前访问的模块和操作,例如:

1
http://localhost/app/?m=module&a=action&var=value

视图

ThinkPHP内置了一个编译型模板引擎,也支持原生的PHP模板,并且还提供了包括Smarty在内的模板引擎驱动。和Smarty不同,ThinkPHP在渲染模板的时候如果不指定模板,则会采用系统默认的定位规则,其定义规范是 Tpl/模块名/操作名.html,所以,Index模块的index操作的默认模板文件位于项目目录下面的Tpl/Index/index.html。

1
2
3
4
5
6
7
8
<html>
<head>
<title>hello {$name}</title>
</head>
<body>
hello, {$name}!
</body>
</html>

要输出视图,必须在控制器方法中进行模板渲染输出操作,例如:

1
2
3
4
5
6
class IndexAction extends Action {
public function index(){
$this->name = 'thinkphp'; // 进行模板变量赋值
$this->display();
}
}

display方法中我们没有指定任何模板,所以按照系统默认的规则输出了Index/index.html模板文件。

变量使用

变量获取

传统获取方式

1
2
3
4
5
$id = $_GET['id']; // 获取get变量
$name = $_POST['name']; // 获取post变量
$value = $_SESSION['var']; // 获取session变量
$name = $_COOKIE['name']; // 获取cookie变量
$file = $_SERVER['PHP_SELF']; // 获取server变量

Action类动态获取

系统的Action类提供了对系统变量的增强获取方法,包括对GET、POST、PUT、REQUEST、SESSION、COOKIE、SERVER和GLOBALS参数,除了获取变量值外,还提供变量过滤和默认值支持,用法很简单,只需要在Action中调用下面方法:

1
2
3
4
5
$id = $this->_get('id'); // 获取get变量
$name = $this->_post('name'); // 获取post变量
$value = $this->_session('var'); // 获取session变量
$name = $this->_cookie('name'); // 获取cookie变量
$file = $this->_server('PHP_SELF'); // 获取server变量

调用格式为:

1
$this->方法名("变量名",["过滤方法"],["默认值"])

方法名可支持:

方法名 含义
_get 获取GET参数
_post 获取POST参数
_param 自动判断请求类型获取GET、POST或者PUT参数
_request 获取REQUEST参数
_put 获取PUT参数
_session 获取$_SESSION参数
_cookie 获取$_COOKIE参数
_server 获取$_SERVER参数
_globals 获取$GLOBALS参数

获取URL参数

在某些情况下面,我们还有一种获取URL参数的特殊需求,一般来说,获取URL参数是采用get变量的方式就够用了,但是对于我们定制过的URL,或者采用了路由的情况下面,URL的参数可能会没有规律,这个时候,我们可以采用另外一种方式来获取。
例如,当前的URL地址是:

1
http://localhost/index.php/news/hello_world/thinkphp

要获取其中的参数,可以用:

1
2
3
$this->_param(0); // 获取news
$this->_param(1); // 获取hello_world
$this->_param(2); // 获取thinkphp

输出

在控制器中给模板变量赋值:

1
2
3
$name = 'ThinkPHP';
$this->assign('name',$name);
$this->display();

在模板中使用该变量:

1
Hello,{$name}!

系统变量

普通的模板变量需要首先赋值后才能在模板中输出,但是系统变量则不需要,可以直接在模板中输出,系统变量的输出通常以{$Think 打头,例如:

1
2
3
4
{$Think.server.script_name} // 输出$_SERVER['SCRIPT_NAME']变量
{$Think.session.user_id} // 输出$_SESSION['user_id']变量
{$Think.get.pageNumber} // 输出$_GET['pageNumber']变量
{$Think.cookie.name} // 输出$_COOKIE['name']变量

支持输出$_SERVER、$_ENV、 $_POST、 $_GET、 $_REQUEST、$_SESSION和 $_COOKIE变量

使用函数

简单用法

1
{$data.name|md5} 

编译后:

1
<?php echo (md5($data['name'])); ?>

多个参数

1
{$create_time|date="y-m-d",###}

表示date函数传入两个参数,每个参数用逗号分割,这里第一个参数是y-m-d,第二个参数是前面要输出的create_time变量,因为该变量是第二个参数,因此需要用###标识变量位置,编译后的结果是:

1
<?php echo (date("y-m-d",$create_time)); ?>

默认值

1
{$user.nickname|default="这家伙很懒,什么也没留下"}

控制器使用

Action参数绑定的原理是把URL中的参数(不包括分组、模块和操作名)和控制器的操作方法中的参数(按变量名)进行绑定。

简单使用

例如,我们给Blog模块定义了两个操作方法read和archive方法,并且给read操作需要指定一个id参数,archive方法指定年份(year)和月份(month)两个参数。为了演示方便,我们省去了具体操作方法的业务代码,仅仅用echo 输出当前的参数。

1
2
3
4
5
6
7
8
class BlogAction extends Action{
public function read($id){
echo 'id='.$id;
}
public function archive($year='2012',$month='01'){
echo 'year='.$year.'&month='.$month;
}
}

URL的访问地址分别是

1
2
http://serverName/index.php/Blog/read/id/5
http://serverName/index.php/Blog/archive/year/2012/month/03

输出的结果

1
2
id=5
year=2012&month=03

文件上传

UploadFile.class.php位于ThinkPHP/Extend/Library/ORG/Net/

功能

  • 基本上传功能
  • 支持批量上传
  • 支持生成图片缩略图
  • 自定义参数上传
  • 上传检测(包括大小、后缀和类型)
  • 支持覆盖方式上传
  • 支持上传类型、附件大小、上传路径定义
  • 支持哈希或者日期子目录保存上传文件
  • 支持动态定义子目录保存文件
  • 上传图片的安全性检测
  • 支持上传文件命名规则
  • 支持对上传文件的Hash验证

简单的上传

表单

1
2
3
4
<form id="upload" method='post' action="!-URL-!/upload/" enctype="multipart/form-data">
<input name="image" type="file" />
<input type="submit" value="提交" >
</form>

注意,表单必须添加**enctype="multipart/form-data"**属性才能支持文件上传功能。

操作

在Action控制器中添加upload操作方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
// 文件上传
public function upload() {
import('ORG.Net.UploadFile');
$upload = new UploadFile();// 实例化上传类
$upload->maxSize = 3145728 ;// 设置附件上传大小
$upload->allowExts = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型
$upload->savePath = './Public/Uploads/';// 设置附件上传目录
if(!$upload->upload()) {// 上传错误提示错误信息
$this->error($upload->getErrorMsg());
}else{// 上传成功
$this->success('上传成功!');
}
}

参数设置

实例化上传类

1
2
import('ORG.Net.UploadFile');
$upload = new UploadFile();// 实例化上传类

常用参数

maxSize 文件上传的最大文件大小(以字节为单位)默认为-1 不限大小
savePath 文件保存路径(必须)
saveRule 上传文件的保存规则,必须是一个无需任何参数的函数名,例如可以是 time、 uniqid com_create_guid 等,但必须能保证生成的文件名是唯一的,默认是uniqid
hashType 上传文件的哈希验证方法,默认是md5_file
autoCheck 是否自动检测附件,默认为自动检测
uploadReplace 存在同名文件是否是覆盖
allowExts 允许上传的文件后缀(留空为不限制),使用数组设置,默认为空数组
allowTypes 允许上传的文件类型(留空为不限制),使用数组设置,默认为空数组
thumb 是否需要对图片文件进行缩略图处理,默认为false
thumbMaxWidth 缩略图的最大宽度,多个使用逗号分隔
thumbMaxHeight 缩略图的最大高度,多个使用逗号分隔
thumbPrefix 缩略图的文件前缀,默认为thumb_
thumbSuffix 缩略图的文件后缀,默认为空
thumbPath 缩略图的保存路径,留空的话取文件上传目录本身
thumbFile 指定缩略图的文件名
thumbExt 指定缩略图的扩展名
thumbRemoveOrigin 生成缩略图后是否删除原图
autoSub 是否使用子目录保存上传文件
subType 子目录创建方式,默认为hash,可以设置为hash、date或者custom
subDir 子目录名称 subType为custom方式后有效
dateFormat 子目录方式为date的时候指定日期格式
hashLevel 子目录保存的层次,默认为一层

设置方法

1
2
3
4
5
6
7
8
9
10
 //设置附件上传目录
$upload->savePath = './Uploads/';
//设置需要生成缩略图,仅对图像文件有效
$upload->thumb = true;
//设置需要生成缩略图的文件后缀
$upload->thumbPrefix = 'm_,s_'; //生产2张缩略图
//设置缩略图最大宽度
$upload->thumbMaxWidth = '200,50';
//设置缩略图最大高度
$upload->thumbMaxHeight = '200,50';

或者在实例化的同时传入上传参数,例如:

1
2
3
4
5
6
7
import('ORG.Net.UploadFile');
$config['savePath'] = './Uploads/';
$config['thumb'] = true;
$config['thumbPrefix'] = 'm_,s_';
$config['thumbMaxWidth'] = '200,50';
$config['thumbMaxHeight'] = '200,50';
$upload = new UploadFile($config);// 实例化上传类并传入参数

单\多文件上传

单文件

uploadOne方法表示每次执行只上传指定的一个文件,并且如果上传成功的话uploadOne方法的返回值就是成功上传的文件信息,和getUploadFileInfo方法不同的是,这个文件信息是一个仅包含单个文件信息的一维数组。如果发生错误,依然是通过getErrorMsg方法获取错误信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import("ORG.Net.UploadFile");
$upload = new UploadFile();
foreach ($_FILES as $key=>$file){
if(!empty($file['name'])) {
$upload->autoSub = true;
$upload->subType = 'date';
$info = $upload->uploadOne($file);
if($info){ // 保存附件信息
M('Photo')->add($info);
}else{ // 上传错误
$this->error($upload->getErrorMsg());
}
}
}

多文件

上传类默认就支持多文件上传,只需要修改表单页面:
如果需要使用多个文件上传,只需要修改表单,把

1
<input type='file'  name='image'>

改为

1
2
3
<input type='file'  name='image1'>
<input type='file' name='image2'>
<input type='file' name='image3'>

模型内数据操作

连接数据库

首先在数据库thinkphp中创建一个think_data数据表(以mysql数据库为例):

1
2
3
4
5
6
7
8
9
CREATE TABLE IF NOT EXISTS `think_data` (
`id` int(8) unsigned NOT NULL AUTO_INCREMENT,
`data` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
INSERT INTO `think_data` (`id`, `data`) VALUES
(1, 'thinkphp'),
(2, 'php'),
(3, 'framework');

如果我们需要读取数据库中的数据,就需要在项目配置文件中(App/home/conf/config.php)添加数据库连接信息如下:

1
2
3
4
5
6
7
8
// 添加数据库配置信息
'DB_TYPE' => 'mysql', // 数据库类型
'DB_HOST' => 'localhost', // 服务器地址
'DB_NAME' => 'thinkphp', // 数据库名
'DB_USER' => 'root', // 用户名
'DB_PWD' => '', // 密码
'DB_PORT' => 3306, // 端口
'DB_PREFIX' => 'think_', // 数据库表前缀

或者:

1
'DB_DSN' => 'mysql://root@localhost:3306/thinkphp'

读取数据

修改控制器方法,添加读取数据的代码:

1
2
3
4
5
6
7
class IndexAction extends Action {
public function index(){
$Data = M('Data'); // 实例化Data数据模型
$this->data = $Data->select();
$this->display();
}
}

M('Data') 实例化后,就可以对think_data数据表(think_ 是我们在项目配置文件中定义的数据表前缀)进行操作(包括CURD)了
定义好控制器后,我们修改模板文件,添加数据输出标签如下:

1
2
3
4
5
6
7
8
9
10
<html>
<head>
<title>Select Data</title>
</head>
<body>
<volist name="data" id="vo">
{$vo.id}--{$vo.data}<br/>
</volist>
</body>
</html>

volist标签是内置模板引擎用于输出数据集的标签。{$vo.id}{$vo.data} 的用法和Smarty类似,就是用于输出数据的字段,这里就表示输出think_data表的id和data字段的值。
我们访问

1
http://localhost/app/

输出:

1
2
3
1--thinkphp
2--php
3--framework

CURD

即数据库的Create.Update.Read.Delete,与ThinkPHP中的 add.save.select.delete一致。

创建数据(Create)

Create操作通常会通过表单来提交数据,首先,我们在项目的Tpl/Form 目录下面创建一个add.html 模板文件,内容为

1
2
3
4
5
<FORM method="post" action="__URL__/insert">
标题:<INPUT type="text" name="title"><br/>
内容:<TEXTAREA name="content" rows="5" cols="45"></TEXTAREA><br/>
<INPUT type="submit" value="提交">
</FORM>

然后再项目中Action目录下创建FormAction.class.php文件,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class FormAction extends Action{
public function insert(){
$Form = D('Form');
if($Form->create()) {
$result = $Form->add();
if($result) {
$this->success('操作成功!');
}else{
$this->error('写入错误!');
}
}else{
$this->error($Form->getError());
}
}
}
}

如果主键是自增类型的话,add方法的返回值就是该主键的值。不是自增主键的话,返回值表示插入数据的个数。如果返回false则表示写入出错。

读取数据

1
2
3
4
5
6
7
8
9
10
11
public function read($id=0){
$Form = M('Form');
// 读取数据
$data = $Form->find($id);
if($data) {
$this->data = $data;// 模板变量赋值
}else{
$this->error('数据错误');
}
$this->display();
}

更新数据

数据的更新操作在ThinkPHP使用save方法,可以看到,我们同样可以使用create方法创建表单提交的数据,而save方法则会自动把当前的数据对象更新到数据库,而更新的条件其实就是表的主键,这就是我们在编辑页面要把主键的值作为隐藏字段一起提交的原因。
如果更新操作不依赖表单的提交的话,就可以写成:

1
2
3
4
5
6
$Form = M("Form"); 
// 要修改的数据对象属性赋值
$data['id'] = 5;
$data['title'] = 'ThinkPHP';
$data['content'] = 'ThinkPHP3.1版本发布';
$Form->save($data); // 根据条件保存修改的数据

删除数据

1
2
3
4
$User = M("User"); // 实例化User对象
$User->where('id=5')->delete(); // 删除id为5的用户数据
$User->delete('1,2,5'); // 删除主键为1,2和5的用户数据
$User->where('status=0')->delete(); // 删除所有状态为0的用户数据

查询

三个基础查询

以字符串为条件

不够安全

1
2
3
$User = M("User"); // 实例化User对象
$User->where('type=1 AND status=1')->select();
//SELECT * FROM think_user WHERE type=1 AND status=1

以数组为条件

较为常用

1
2
3
4
5
6
$User = M("User"); // 实例化User对象
$condition['name'] = 'thinkphp';
$condition['status'] = 1;
// 把查询条件传入查询方法
$User->where($condition)->select();
//SELECT * FROM think_user WHERE `name`='thinkphp' AND status=1

以对象方式来查询

以stdClass内置对象为例

1
2
3
4
5
6
7
$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

快捷查询

不同字段的相同查询

1
2
3
4
$User = M("User"); // 实例化User对象
$map['name|title'] = 'thinkphp';
// 把查询条件传入查询方法
$User->where($map)->select();

不同字段的不同查询

1
2
3
4
$User = M("User"); // 实例化User对象
$map['name|title'] = 'thinkphp';
// 把查询条件传入查询方法
$User->where($map)->select();

SQL查询

内置的ORM和ActiveRecord模式实现了方便的数据存取操作,而且新版增加的连贯操作功能更是让这个数据操作更加清晰,但是ThinkPHP仍然保留了原生的SQL查询和执行操作支持,为了满足复杂查询的需要和一些特殊的数据操作,SQL查询的返回值因为是直接返回的Db类的查询结果,没有做任何的处理。主要包括下面两个方法:

query方法

1
query($sql,$parse=false)

sql:要查询的SQL语句

parse:是否需要解析SQL

1
2
$Model = new Model() // 实例化一个model对象 没有对应任何数据表
$Model->query("select * from think_user where status=1");

execute方法

1
execute($sql,$parse=false)

sql:要查询的SQL语句

parse:是否需要解析SQL

1
2
$Model = new Model() // 实例化一个model对象 没有对应任何数据表
$Model->execute("update think_user set name='thinkPHP' where status=1");

连贯操作

例如$User->where('status=1')->order('create_time')->limit(10)->select();

常用用法

WHERE

where 用于查询或者更新条件的定义

用法 where($where)
参数 where(必须):查询或者操作条件,支持字符串、数组和对象
返回值 当前模型实例
备注 如果不调用where方法,默认不会执行更新和删除操作

TABLE

table 定义要操作的数据表名称,动态改变当前操作的数据表名称,需要写数据表的全名,包含前缀,可以使用别名和跨库操作

用法 table($table)
参数 table(必须):数据表名称,支持操作多个表,支持字符串、数组和对象
返回值 当前模型实例
备注 如果不调用table方法,会自动获取模型对应或者定义的数据表

DATA

data 可以用于新增或者保存数据之前的数据对象赋值

用法 data($data)
参数 data(必须):数据,支持数组和对象
返回值 当前模型实例
备注 如果不调用data方法,则会取当前的数据对象或者传入add和save的数据

FIELD

field 用于定义要查询的字段

用法 field($field,$except=false)
参数 field(必须):字段名,支持字符串和数组,支持指定字段别名;如果为true则表示显式或者数据表的所有字段。 except(可选):是否排除,默认为false,如果为true表示定义的字段为数据表中排除field参数定义之外的所有字段。
返回值 当前模型实例
备注 如果不调用field方法,则默认返回所有字段,和field(’*’)等效

ORDER

order 用于对操作结果排序

用法 order($order)
参数 order(必须):排序的字段名,支持字符串和数组,支持多个字段排序
返回值 当前模型实例
备注 如果不调用order方法,按照数据库的默认规则
1
\app\common\entity\Article::where('category', $category)->order('sort', 'asc')->select();

LIMIT

limit 用于定义要查询的结果限制(支持所有的数据库类型)

用法 limit($limit)
参数 limit(必须):限制数量,支持字符串
返回值 当前模型实例
备注 如果不调用limit方法,则表示没有限制

GROUP

group 用于数据库的group查询支持

用法 group($group)
参数 group(必须):group的字段名,支持字符串
返回值 当前模型实例
备注

UNION

union 用于数据库的union查询支持

用法 union($union,$all=false)
参数 union(必须):union操作,支持字符串、数组和对象 all(可选):是否采用UNION ALL 操作,默认为false
返回值 当前模型实例
备注 Union方法支持多次调用

常用方法

方法 作用 支持的参数类型
where 用于查询或者更新条件的定义 字符串、数组和对象
table 用于定义要操作的数据表名称 字符串和数组
alias 用于给当前数据表定义别名 字符串
data 用于新增或者更新数据之前的数据对象赋值 数组和对象
field 用于定义要查询的字段(支持字段排除) 字符串和数组
order 用于对结果排序 字符串和数组
limit 用于限制查询结果数量 字符串和数字
group 用于对查询的group支持 字符串
filter 用于数据过滤 字符串