1.3 获取ThinkPHP
学习网站:http://document.thinkphp.cn/manual_3_2.html#directory_structure
├─ThinkPHP 框架系统目录(可以部署在非web目录下面)
│ ├─Common 核心公共函数目录
│ ├─Conf 核心配置目录
│ ├─Lang 核心语言包目录
│ ├─Library 框架类库目录
│ │ ├─Think 核心Think类库包目录
│ │ ├─Behavior 行为类库目录
│ │ ├─Org Org类库包目录
│ │ ├─Vendor 第三方类库目录
│ │ ├─ … 更多类库目录
│ ├─Mode 框架应用模式目录
│ ├─Tpl 系统模板目录
│ ├─LICENSE.txt 框架授权协议文件
│ ├─logo.png 框架LOGO文件
│ ├─README.txt 框架README文件
│ └─ThinkPHP.php 框架入口文件
命名规范
类文件都是以.class.php为后缀(这里是指的ThinkPHP内部使用的类库文件,不代表外部加载的类库文件),使用驼峰法命名,并且首字母大写,例如 DbMysql.class.php;
类的命名空间地址和所在的路径地址一致,例如 Home\Controller\UserController类所在的路径应该是 Application/Home/Controller/UserController.class.php;
确保文件的命名和调用大小写一致,是由于在类Unix系统上面,对大小写是敏感的(而ThinkPHP在调试模式下面,即使在Windows平台也会严格检查大小写);
类名和文件名一致(包括上面说的大小写一致),例如 UserController类的文件命名是UserController.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 这样的数据表字段可能会被过滤。
加载配置
应用配置文件也就是调用所有模块之前都会首先加载的公共配置文件(默认位于Application/Common/Conf/config.php)。
return array(
‘DEFAULT_MODULE’ => ‘Index’, //默认模块
‘URL_MODEL’ => ‘2’, //URL模式
‘SESSION_AUTO_START’ => true, //是否开启session,
);
1)读取配置
model=C(‘URLMODEL′);我们需要动态改变数据缓存的有效期的话,可以使用C(‘DATACACHETIME′,60);2)读取数据库中的配置参数
config = M(‘Config’)->getField(‘name,value’);
// config是一个关联数组 键值就是配置参数 值就是配置值
// 例如: array(‘config1’=>’val1’,’config2’=>’val2’,…)
C($config); // 合并配置参数到全局配置
Common公共模块
Common模块是一个特殊的模块,是应用的公共模块,访问所有的模块之前都会首先加载公共模块下面的配置文件(Conf/config.php)和公共函数文件(Common/function.php)。但Common模块本身不能通过URL直接访问,公共模块的其他文件则可以被其他模块继承或者调用。
公共模块的位置可以通过COMMON_PATH常量改变,我们可以在入口文件中重新定义COMMON_PATH如下:
define('COMMON_PATH','./Common/');
define('APP_PATH','./Application/');
require './ThinkPHP/ThinkPHP.php';
www WEB部署目录(或者子目录)
├─index.php 入口文件
├─README.md README文件
├─Common 应用公共模块目录
├─Application 应用模块目录
├─Public 应用资源文件目录
└─ThinkPHP 框架目录
定义之后,Application目录下面就不再需要Common目录了。
Behavior
在Thinkphp Behavior中,我们把这些行为发生作用的位置(陷阱)称之为标签(位)(tag),当应用程序运行到这个标签的时候,就会被拦截下来,统一执行相关的行为。
开发人员还可以在应用中添加自己的应用标签,在任何需要拦截的位置添加如下代码即可:
标签调用:
定义行为:
namespace Home\Behavior;
use Think\Behavior;
class TestBehavior extends Behavior {
// 行为扩展的执行入口必须是run
public function run(&$params){
if(C('TEST_PARAM')) {
echo 'RUNTEST BEHAVIOR '.$params;
}
}
}
标签与行为绑定:
return array(
'标签名称1'=>array('行为名1','行为名2',...),
'标签名称2'=>array('行为名1','行为名2',...),
);
通过tag函数调用:
tag('my_tag'); // 添加my_tag 标签侦听
// 下面的写法作用一致
\Think\Hook::listen('my_tag');
行为的调用不一定要放到标签才能调用,如果需要的话,我们可以在控制器中或者其他地方直接调用行为。例如,我们可以把用户权限检测封装成一个行为类,例如:
namespace Home\Behavior;
use Think\Behavior;
class AuthCheckBehavior extends Behavior {
// 行为扩展的执行入口必须是run
public function run(&$return){
if(C('USER_AUTH_ON')) {
// 进行权限认证逻辑 如果认证通过 $return = true;
// 否则用halt输出错误信息
}
}
定义了AuthCheck行为后,我们可以在控制器的_initialize方法中直接用下面的方式调用:
B('Home\Behavior\AuthCheck');
命名空间:
Test类保存在 ThinkPHP/Library/My/Test.class.php,我们就可以直接实例化和调用:
$Test = new \My\Test();
$Test->sayHello();
我们定义了一个类 Org\Util\Auth 类保存到 ThinkPHP/Library/Org/Util/Auth.class.php。接下来,我们就可以直接实例化了。new \Org\Util\Auth();
namespace Org\Util;
class Auth {
}
Library目录下面的命名空间都可以自动识别和定位,你可以在Library目录下面任意增加新的目录,就会自动注册成为一个新的根命名空间。
手动加载第三方类库
我们可以使用import方法导入任何类库,用法如下:
// 导入Org类库包 Library/Org/Util/Date.class.php类库
import("Org.Util.Date");
// 导入Home模块下面的 Application/Home/Util/UserUtil.class.php类库
import("Home.Util.UserUtil");
// 导入当前模块下面的类库
import("@.Util.Array");
// 导入Vendor类库包 Library/Vendor/Zend/Server.class.php
import('Vendor.Zend.Server');
如果你在Library目录下面创建了一个Test子目录,并且创建了一个UserTest.class.php类库,那么可以这样导入:import(‘Test.UserTest’);
路由
静态路由:
'URL_ROUTER_ON' => true,
'URL_MAP_RULES'=>array(
'new/top' => 'news/index?type=top'
)
定义之后,如果我们访问: http://serverName/Home/new/top
其实是访问: http://serverName/Home/news/index/type/top
'my' => 'Member/myinfo', // 静态地址路由
'blog/:id' => 'Blog/read', // 静态地址和动态地址结合
'new/:year/:month/:day'=>'News/read', // 静态地址和动态地址结合
':user/:blog_id' =>'Blog/read',// 全动态地址
每个参数中以“:”开头的参数都表示动态参数,并且会自动对应一个GET参数,例如:id表示该处匹配到的参数可以使用
GET[′id′]方式获取,:year、:month、:day则分别对应
_GET[‘year’]、
GET[′month′]和
_GET[‘day’]。
‘blog/:id\d|md5’=>’Blog/read’,表示对匹配到的id变量进行md5处理,也就是说,实际传入read操作方法的
GET[′id′]其实是md5(
_GET[‘id’])。
‘blog/:year\d/[:month\d]’=>’Blog/archive’,[:month\d]变量用[ ]包含起来后就表示该变量是路由匹配的可选变量。可选参数只能放到路由规则的最后,如果在中间使用了可选参数的话,后面的变量都会变成可选参数。
控制器
系统会检测当前操作是否具有前置和后置操作,如果存在就会按照顺序执行,前置和后置操作的定义方式如下:
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller{
//前置操作方法
public function _before_index(){
echo 'before<br/>';
}
public function index(){
echo 'index<br/>';
}
//后置操作方法
public function _after_index(){
echo 'after<br/>';
}
}
如果我们访问 http://serverName/index.php/Home/Index/index:
before
index
after
namespace Home\Controller;
use Think\Controller;
class BlogController extends Controller{
public function read($id){
echo 'id='.$id;
}
public function archive($year='2013',$month='01'){
echo 'year='.$year.'&month='.$month;
}
}
URL的访问地址分别是:
http://serverName/index.php/Home/Blog/read/id/5
http://serverName/index.php/Home/Blog/archive/year/2013/month/11
或者
http://serverName/index.php/Home/Blog/read/5
http://serverName/index.php/Home/Blog/archive/2013/11
success和error方法的第一个参数表示提示信息,第二个参数表示跳转地址,第三个参数是跳转时间(单位为秒),例如:
// 操作完成3秒后跳转到 /Article/index
$this->success('操作完成','/Article/index',3);
// 操作失败5秒后跳转到 /Article/error
$this->error('操作失败','/Article/error',5);
//默认错误跳转对应的模板文件
'TMPL_ACTION_ERROR' => THINK_PATH . 'Tpl/dispatch_jump.tpl',
//默认成功跳转对应的模板文件
'TMPL_ACTION_SUCCESS' => THINK_PATH . 'Tpl/dispatch_jump.tpl'
重定向
//重定向到New模块的Category操作
$this->redirect('New/category', array('cate_id' => 2), 5, '页面跳转中...');
//重定向到指定的URL地址
redirect('/New/category/cate_id/2', 5, '页面跳转中...')
AJAX-get-post:
$id = $_GET['id']; // 获取get变量
$name = $_POST['name']; // 获取post变量
$value = $_SESSION['var']; // 获取session变量
$name = $_COOKIE['name']; // 获取cookie变量
$file = $_SERVER['PHP_SELF']; // 获取server变量
建议:
echo I('get.id'); // 相当于 $_GET['id']
I('get.'); // 获取整个$_GET 数组
echo I('get.name'); // 相当于 $_GET['name']
// 采用htmlspecialchars方法对$_GET['name'] 进行过滤,如果不存在则返回空字符串
echo I('get.name','','htmlspecialchars');
param变量类型是框架特有的支持自动判断当前请求类型的变量获取方式,例如:
echo I(‘param.id’);//如果当前请求类型是GET,那么等效于
GET[′id′],如果当前请求类型是POST或者PUT,那么相当于获取
_POST[‘id’] 或者 PUT参数id。
I('id'); // 等同于 I('param.id')
I('name'); // 等同于 I('param.name')
http://serverName/index.php/New/2013/06/01
echo I('path.1'); // 输出2013
echo I('path.2'); // 输出06
echo I('path.3'); // 输出01
值过滤:如果正则匹配不通过的话,则返回默认值。
// 采用正则表达式进行变量过滤
I('get.name','','/^[A-Za-z]+$/');
I('get.id',0,'/^\d+$/');
保存数据
$User = M("User"); // 实例化User对象
// 根据表单提交的POST数据创建数据对象
$User->create();
$User->save(); // 根据条件保存修改的数据
模型
自定义, User=D(′User′);//相当于 User = new \Home\Model\UserModel();会先检查是否有相应的,当 \Home\Model\UserModel 类不存在的时候,D函数会尝试实例化公共模块下面的 \Common\Model\UserModel 类:
namespace Home\Model;
use Think\Model;
class UserModel extends Model{
protected $insertFields = 'name,email'; // 新增数据的时候允许写入name和email字段
protected $updateFields = 'email'; // 编辑数据的时候只允许写入email字段
}
自动验证
namespace Home\Model;
use Think\Model;
class UserModel extends Model{
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{
// 验证通过 可以进行其他数据操作
}
namespace Home\Model;
use Think\Model;
class UserModel extends Model{
protected $connection = array(
'db_type' => 'mysql',
'db_user' => 'root',
'db_pwd' => '1234',
'db_host' => 'localhost',
'db_port' => '3306',
'db_name' => 'thinkphp',
'db_charset' => 'utf8',
);
$New = M('new','think_',$connection);
// 等效于 $New = new \Think\Model('new','think_',$connection);
}
$User = M('User','other_','mysql://root:1234@localhost/demo#utf8');
视图模型
namespace Home\Model;
use Think\Model\ViewModel;
class BlogViewModel extends ViewModel {
public $viewFields = array(
'Blog'=>array('id','name','title'),
'Category'=>array('title'=>'category_name', '_on'=>'Blog.category_id=Category.id'),
'User'=>array('name'=>'username', '_on'=>'Blog.user_id=User.id'),
);
}
$Model = D("BlogView");
$Model->field('id,name,title,category_name,username')->where('id>10')->order('id desc')->select();
等同于:
$Model = D("Blog");
$Model->table('think_blog Blog,think_category Category,think_user User')
->field('Blog.id,Blog.name,Blog.title,Category.title as category_name,User.name as username')
->order('Blog.id desc')
->where('Blog.category_id=Category.id AND Blog.user_id=User.id')
->select();
数据操作类
$Model = M('User');
$Model->alias('a')->join('__DEPT__ b ON b.user_id= a.id')->select();
SELECT * FROM think_user a INNER JOIN think_dept b ON b.user_id= a.id
$Model->field('id,SUM(score)')->select();
SELECT id,SUM(score) FROM table
$Model->field(array('id','concat(name,'-',id)'=>'truename','LEFT(title,7)'=>'sub_title'))->select();
SELECT id,concat(name,'-',id) as truename,LEFT(title,7) as sub_title FROM table
排除content字段查询
$Model->field('content',true)->select();
指定字段保存
$Model->field('title,email,content')->save($data);
分页查询
$Article->page('1,10')->select(); // 查询第一页数据$Article->page(1,10)->select();
$Article->page('2,10')->select(); // 查询第二页数据
分组
$this->field('username,max(score)')->group('user_id')->select();
having
$this->field('username,max(score)')->group('user_id')->having('count(test_time)>3')->select();
SELECT username,max(score) FROM think_score GROUP BY user_id HAVING count(test_time)>3
$result = M('User')->fetchSql(true)->find(1);
输出result结果为: SELECT * FROM think_user where id = 1
创建数据,我们熟悉的令牌验证、自动验证和自动完成功能,其实都必须通过create方法才能生效。:
创建完成的数据可以直接读取和修改,Create方法创建的数据对象是保存在内存中,并没有实际写入到数据库中,直到使用add或者save方法才会真正写入数据库。
$data['name'] = 'ThinkPHP';
$data['email'] = 'ThinkPHP@gmail.com';
$User->create($data);
// 创建完成数据对象后可以直接读取数据
echo $User->name;
echo $User->email;
// 也可以直接修改创建完成的数据
$User->name = 'onethink'; // 修改name字段数据
$User->status = 1; // 增加新的字段数据
// 批量添加数据
$dataList[] = array('name'=>'thinkphp','email'=>'thinkphp@gamil.com');
$dataList[] = array('name'=>'onethink','email'=>'onethink@gamil.com');
$User->addAll($dataList);
更新
$User = M("User"); // 实例化User对象
// 更改用户的name值
$User-> where('id=5')->setField('name','ThinkPHP');
对于统计字段:
$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
execute用于更新和写入数据的sql操作,如果数据非法或者查询错误则返回false ,否则返回影响的记录数。
$Model = new \Think\Model() // 实例化一个model对象 没有对应任何数据表
$Model->execute("update think_user set name='thinkPHP' where status=1");
模型命名规范
namespace Home\Model;
use Think\Model;
class NewsModel extends Model {
protected $_scope = array(
// 命名范围normal
'normal'=>array(
'where'=>array('status'=>1),
),
// 命名范围latest
'latest'=>array(
'order'=>'create_time DESC',
'limit'=>10,
),
);
}
$Model = D('News'); // 这里必须使用D方法 因为命名范围在模型里面定义
$Model->scope('normal')->select();
$Model->scope('latest')->select();
既
SELECT * FROM think_news WHERE status=1
SELECT * FROM think_news ORDER BY create_time DESC LIMIT 10
$Model->scope('normal,latest')->select();
SELECT * FROM think_news WHERE status=1 ORDER BY create_time DESC LIMIT 10
$Model->scope('normal',array('limit'=>5))->select();增加条件
问题解决
更新或者环境改变后遇到问题首要问题是清空Runtime目录;
如果你在部署模式下面修改了数据表的字段信息,可能需要清空Data/_fields目录下面的缓存文件,让系统重新获取更新的数据表字段信息,否则会发生新增的字段无法写入数据库的问题。
thinkphp创建对象及数据操作
ThinkPHP有三种创建控制器对象的方式:
通过命名空间的路径找到类然后new出来例如:
dx=new\Home\Controller\IndexController();通过A快捷函数创建对象A(“模块/控制器”)例如:
dx = A(“Home\Index”);
通过R快捷函数创建对象并调用方法;R(“Index/ShuChu”)
ThinkPHP操作数据库:
首先需要在配置文件中配置数据库信息
在创建模型对象执行sql语句
创建模型对象有三种方式:
操作数据库:
原始方式需要模型中建模型类,类名必须是数据库表名,例如:$m = new \Home\Model\InfoModel();
使用快捷函数D:$m = D("Info");需要注意的是如果没有模型类可以不加参数,对象就是model父类的对象
3、使用快捷函数M:$m = M("Nation");
首先介绍一下连贯函数,就是返回值是:$this,也就是说返回自身对象,可以继续调用函数
数据库操作中基本的函数:select("主键值【,主键值】")、find("主键值"),聚合函数不是连贯函数。
具体用法如下如下:(可以参考手册)
操作数据库
$attr = $m->select(); //查询所有数据
$attr = $m->select("p001,p002,p003");
$attr = $m->find("p001"); //找特定的数据根据主键值找
where可以加查询条件
$attr = $m->where("code='p001' or sex=true")->select();
table可以切换要操作的表
$attr = $m->table("Nation")->select();
alias可以设置表的别名
$attr = $m->alias("人员")->select();
field可以指定查询的字段
$attr = $m->field("code,name")->select();
order可以加排序条件
$attr = $m->order("Nation desc")->select();
group可以分组
$attr = $m->field("Nation")->group("Nation")->select();
having可以加分组后的条件
$attr = $m->field("Nation")->group("Nation")->having("count(*)>5")->select();
join可以连接多个表,在field里面要给字段加别名
$attr = $m->field("Info.Code as 代号,Info.Name as 姓名,Sex as 性别,Nation.Name as 民族名称")->join("Nation on Info.Nation = Nation.Code")->select();
union联合查询
$attr = $m->field("name")->union("select name from nation")->select();
distinct去重
$attr = $m->field("Nation")->distinct(true)->select();
limit可以分页,参数第一个代表跳过多少条,第二个代表取多少条
$attr = $m->limit(10,5)->select();
page可以分页。第一个参数代表是当前页,第二个参数代表每页多少条
$attr = $m->page(3,5)->select();
取数据总条数
$attr = $m->count("*");
取某一列的和
$attr = $m->table("Car")->sum("Price");
取平均值
$attr = $m->table("Car")->avg("Price");
取最大值
$attr = $m->table("Car")->max("Price");
取最小值
$attr = $m->table("Car")->min("Price");
$sql = "select * from Info where Nation='n001'";
$attr = $m->query($sql);//查询数据
$sql = "insert into Nation values('n104','按实际')";
$attr = $m->execute($sql);//插入数据
$attr = $m->field("Info.Code as code,Info.Name as name,sex,Nation.Name as nationname,birthday")->join("Nation on Info.Nation = Nation.Code")->select();
$this->assign("info",$attr);
$this->assign("test",10);
$this->display();
增删查改
thinkphp对数据库增删改查进行了封装操作,使得使用更加方便,但是不一定灵活。
可以用封装的用,需要写sql,可以执行sql。
1.原始的
$Model = new Model(); // 实例化一个model对象 没有对应任何数据表
$insert_sql = "INSERT INTO sh_wxuser_collection (user_id,store_id,good_id,addtime) VALUES('".$user_id."','".$store_id."','".$good_id."','".$addtime."');";
$Model - >query($insert_sql);
2.针对表实例化的,这里的表原名是sh_wxuser_collection。sh是前缀。
$model = M('wxuser_collection'); //自动省去sh
$insert_sql = "INSERT INTO __TABLE__ (user_id,store_id,good_id,addtime) VALUES('".$user_id."','".$store_id."','".$good_id."','".$addtime."');";
$model - >query($insert_sql);
另一种写法,_可以写成大写,它会自动转化成_
$model = M('WxuserCollection'); //自动省去sh
$insert_sql = "INSERT INTO __TABLE__ (user_id,store_id,good_id,addtime) VALUES('".$user_id."','".$store_id."','".$good_id."','".$addtime."');";
$model - >query($insert_sql);
3. 封装的add语句
$model = M('WxuserCollection');
$data = array('user_id' = >$user_id, 'store_id' = >$store_id, 'good_id' = >$good_id, 'addtime' = >$addtime);
$model - >data($data) - >add();
4.封装的修改edit语句
$model = M('WxuserCollection');
$data = array('user_id' = >$user_id, 'store_id' = >$store_id, 'good_id' = >$good_id, 'addtime' = >$addtime);
$model - >data($data) - >where('id=3') - >save();
确实挺方便的,但是方便之余,别忘了原始的sql,原汁原味的sql,才最有意思。
5.find()
$model = M('WxuserCollection');
$res1 = $model - >find(1);
$res2 = $model - >find(2);
$res3 = $model - >where('good_id=1105 AND store_id = 1 AND user_id = 20') - >find();
find获取一条数据,find(1)获取id为1的数据,find(2)获取id为2的数据。最后一个是获取条件为where的中的第一条数据。
5.select()
$model = M('WxuserCollection');
$res = $model - >where('good_id=1105 AND store_id = 1 AND user_id = 20') - >field('id,good_id as good') - >select();
获取所有数据。这里的好处就是,不用考虑sql语句的顺序了,随心所欲调用函数就可以了。
6.delete()
$model = M('WxuserCollection');
$res = $model - >where('id=1') - >delete(); // 成功返回1 失败返回0
根据条件进行删除操作