MODULE_NAME
CONTROLLER_NAME
ACTION_NAME
tp3开发规范:
目录名: 全部小写+下划线 文件名: 类文件采用首字母大写的驼峰命名法定义 其他文件为小写+下划线命名 类名: 和类文件名保持一致(采用首字母大写驼峰命名法) 5之前有UserController这样的Controller、model等后缀 函数名、属性: 首字母小写的驼峰命名法 常量: 大写和下划线 配置名: 小写+下划线 数据库 表和字段命名: 必须小写+下划线 类库函数文件以.php结尾而5之前是.class.php 类的文件名以命名空间定义。且命名空间和类文件所在的路径一致
tp5开发规范:
需要大写的 类文件名:类文件采用驼峰法命名(首字母大写) 类名:类名和类文件名保持一致,统一采用驼峰法命名(首字母大写) 类方法:命名使用驼峰法(首字母小写),例如 getUserName; 类属性:命名使用驼峰法(首字母小写),例如 tableName 常量:以大写字母和下划线命名,例如 APP_PATH 函数:小写+下划线 目录:目录使用小写+下划线 文件:除类文件其他均以小写+下划线
模板提替换
tp5.0-:(包含5.0)
'view_replace_str' => [
'__PUBLIC__'=>'xxx',
],
注意:5.0.24可直接使用如下模板替换变量:只针对根目录为public时没有问题(__STATIC__ 的值为 /static ,如果更目录改成与tp3默认的一样的话就少一个public了)
__ROOT__ :项目目录
__STATIC__ :项目目录下的static目录
__JS__ :项目目录下的static/js目录
__CSS__:项目目录下的static/css目录
tp5.1+(5.1及以上)
'tpl_replace_string' => [
'__PUBLIC__' => 'xxx',
],
安全
异常封装:
日志记录
开发单独开启sql日志
在index.php定义:
\think\Log::init([ 'type'=>'File', 'path'=>APP_PATH.'/sql_log', 'level'=>['sql'] ]);
tp5数据库操作:
mode lORM
原生:
use think\Db; Db:query('select * from user where id=?',[$id])
构造器:前面的方法是不会真正的执行sql的只有select或者find之后才能调用
除了find、select之外还有update(更新)、delete(删除)、insert(删除)
还有聚合查询(count、max、min、avg、sum),。。。等等
use think\Db; $result=Db::table('banner_item')->where('banner_id','=',$id)->select();
除了上面的执行语句的方法还有许多辅助的方法、他们也被成为链式操作方法
table、where、alias、field、strict、limit、page、order、group、having、join、union、distinct、lock、cache、comment、fethSql、force、partition、failException、sequence
带*表示可以多次调用
连贯操作 | 作用 | 支持的参数类型 |
---|---|---|
where* | 用于AND查询 | 字符串、数组和对象 |
whereOr* | 用于OR查询 | 字符串、数组和对象 |
wheretime* | 用于时间日期的快捷查询 | 字符串 |
table | 用于定义要操作的数据表名称 | 字符串和数组 |
alias | 用于给当前数据表定义别名 | 字符串 |
field* | 用于定义要查询的字段(支持字段排除) | 字符串和数组 |
order* | 用于对结果排序 | 字符串和数组 |
limit | 用于限制查询结果数量 | 字符串和数字 |
page | 用于查询分页(内部会转换成limit) | 字符串和数字 |
group | 用于对查询的group支持 | 字符串 |
having | 用于对查询的having支持 | 字符串 |
join* | 用于对查询的join支持 | 字符串和数组 |
union* | 用于对查询的union支持 | 字符串、数组和对象 |
view* | 用于视图查询 | 字符串、数组 |
distinct | 用于查询的distinct支持 | 布尔值 |
lock | 用于数据库的锁机制 | 布尔值 |
cache | 用于查询缓存 | 支持多个参数 |
relation* | 用于关联查询 | 字符串 |
with* | 用于关联预载入 | 字符串、数组 |
bind* | 用于数据绑定操作 | 数组或多个参数 |
comment | 用于SQL注释 | 字符串 |
force | 用于数据集的强制索引 | 字符串 |
master | 用于设置主服务器读取数据 | 布尔值 |
strict | 用于设置是否严格检测字段名是否存在 | 布尔值 |
sequence | 用于设置Pgsql的自增序列名 | 字符串 |
failException | 用于设置没有查询到数据是否抛出异常 | 布尔值 |
partition | 用于设置分表信息 | 数组 字符串 |
import()方法
import 语法:
boolen import(class, baseUrl, ext)
参数 | 说明 |
---|---|
class | 必须,表示要导入的类库,采用命名空间的方式。 |
baseUrl | 可选,表示导入的基础路径,省略的话系统采用默认的规则,具体见下文。 |
ext | 可选,表示导入的类库后缀,默认是 .class.php 。 |
这个方法默认会以ThinkPHP\Library为相对起始目录 (这个目录包含了'Think','Org','Behavior','Com','Vendor'系统基类)
import("ORG.Util.Page"); // 导入 ThinkPHP系统目录/Lib/ORG/Util/Page.class.php 文件
如果是当前项目(),可以简化为:import("@.Action.UserAction");
逻辑:如果import截取第一个参数以@开头代表是当前项目那么他的起始目录为 APP_PATH.MODULE_NAME.'/' 即application/Home/
如果截取的第一个参数是'Think','Org','Behavior','Com','Vendor'中的其中一个那么他的起始目录为ThinkPHP/Library
如果以上两个都不是那么它的起始目录为APP_PATH 即 application/
所以:import('Home.Test.Alipay');和import('@.Test.Alipay');都能定位到aplication/Home/Test/Alipay.class.php
要想放在ThinkPHP/Library 那么Test文件夹必须放到library下的'Think','Org','Behavior','Com','Vendor’这几个目录里我们用import调用时可以
import("ORG.Test.Alipay");import("Behavior.Test.Alipay");等等
非Composer安装的第三方库一般在extends目录 需要定义命名空间,否则不会自动加载
//该文件那么实际的类文件位置应该是: 1.extend/first/second/Foo.php namespace first\second; class Foo { } //使用first.second.Foo类的时候,直接实例化即可使用,例如: $foo = new \first\second\Foo(); //或者: use first\second\Foo; $foo = new Foo(); //无命名空间则用: Loader::import('first.second.Foo'); $foo = new \Foo();
vendor()导入第三方类库 第一个参数中的.会解析成/ #会解析成.
vendor('alipaycustom.AopEncrypt'); //vendor/alipaycustom/AopEncrypt.php
vendor('alipaycustom.alipaycustom#function'); //vendor/alipaycustom/alipaycustom.function.php
配置:
改变配置文件的放置目录
publuc/index.php文件定义 define("CONF_PATH",__DIR__."/../conf/"); 配置文件就从默认的 application/config.php 变成了 application/conf/config.php
扩展配置:
将配置文件放入extra目录:如aplipay.php
获取:config(文件名.配置的键名) 如 config("aplipay.public_key")
场景配置: 比如 家里和公司
在公司环境中,我们在应用配置文件中配置: 'app_status'=>'office' 那么就会自动加载该状态对应的配置文件(默认位于
application/office.php
)
场景配置文件和应用配置文件config.php
是一样的定义。
如果我们回家后,我们修改定义为:'app_status'=>'home' 那么就会自动加载该状态对应的配置文件(位于
application/home.php
)。
thinkphp 架构URL访问中有这么一句 RewriteRule ^(.*)$ index.php/$1
url生成
thinkphp5- U()
thinkphp5+ url('模块/控制器/方法','参数')
注意多级控制器的情况下url的写法
application/admin/Controller/user/AdminUser.php controller下多了一个目录 正确的写法 admin/user.adminuser/getuser')}">aaaaaaaaaa 错误的写法 admin/user/adminuser/getuser')}">bbbbb
同理:路由也是按上面的方式获取:Route::("hello/:id","admin/user.adminuser/getuser");
路由:
参数配置:
// 是否开启路由
'url_route_on' => true,
// 路由配置文件(支持配置多个)
'route_config_file' => ['route'],
// 路由使用完整匹配
'route_complete_match' => false,
// 是否强制使用路由 为true的话必须要为每个url配置路由才能访问哦 所以才开发阶段可以false 这样原url和路由url都可以访问到
'url_route_must' => false,
路由配置:
application/route.php
return [ //'news/:id' => '控制器/模块/方法' 'news/:id' => 'index/index/info', ]; //配置以后访问localhost/index.php/index/index/info/id/5.html就访问不到了 //必须访问localhost/index.php/news/5.html //怎么获取路由后的url呢? //thinkphp5 的url函数现在可以自动生成路由的路径啦! //但是之前的版本必须自己写代码实现
获取php全局变量
thinkphp5- :
I()
thinkphp5+ :
input()
获取所有:input("param.");
获取所有post:input("post.");
获取get:input("get.");
模板输出:
thinkphp5-:
$this->display()
thinkphp5+:
return $this->fetch()
模板使用函数
{$data.name|md5} echo (md5($data['name'])); ?> {$name|md5|strtoupper|substr=0,3} {:substr(strtoupper(md5($name)),0,3)} echo (substr(strtoupper(md5($name)),0,3)); ?> 多个参数: {$create_time|date="y-m-d",###} echo (date("y-m-d",$create_time)); ?> {$data.name|substr=0,3} {$data.name|substr=###,0,3} echo (substr($data['name'],0,3)); ?>
tp表达式查询
自动验证
thinkphp5-:
格式:
array(
①验证字段1,
② 验证规则,
③错误提示,
④验证条件(0:存在字段就验证(默认);1:必须验证;2:值不为空就验证),
⑤附加规则,
⑥验证时间(1:新增 2:更新 3:新增和更新(默认)),
⑦额外参数 数组形式 这个数组的每个值都是函数的一个参数(为callbck和function的时候)
),
额外参数试例
$create_data['identifier']=$identifier; $extraArr=array( 'ext'=>array('1','2','3','4'), 'size'=>array(100,200,300), 88 ) $rules=array( array('identifier','checkedIdentifier','格式不正确',1,'function',3,),$extraArr), ); function checkedIdentifier($param,$val1,$val2,$val3){ dump($param);//$create_data['identifier']的值 dump($val1);//第七个参数数组的第一个键值ext'=>array('1','2','3','4') dump($val2);//'size'=>array(100,200,300) dump($val3);//88 }
静态验证:在model里定义$_validate属性;定义好验证规则后,就可以在使用create方法创建数据对象的时候自动调用:
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'), // 自定义函数验证密码格式
array('password','md5','密码格式不正确',0,'function'), // 自定义函数验证密码格式
array('img','checkImg','图片格式不正确',1,'callback',3,array('ext'=>array('jpg','jpeg','png','gif'))), ); /** * 自定义图片后缀验证 */ public function checkImg($param,$value){ $exts=''; foreach($value as $val){ $exts.="[".$val."]{1}|"; } $exts=substr($exts,0,-1);; $str=$param; $preg="/^[\S]+[.]?(".$exts.")$/"; if( preg_match($preg ,$str)){ return true; }else{ return false; } } }
动态验证:动态验证不依赖模型类的定义,所以通常用M函数实例化模型就可以
$rules = 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'), // 自定义函数验证密码格式
array('tel','/^1[34578]\d{9}$/','手机号码不对..1!',0,'regex',3),// 必填
); $User = M("User"); // 实例化User对象 if (!$User->validate($rules)->create()){ // 如果创建失败 表示验证没有通过 输出错误提示信息 exit($User->getError()); }else{ // 验证通过 可以进行其他数据操作 }
tp3验证规则:系统内置或自定义的规则
//内置验证require不能为空 array('require','require','数据不能为空!',1), //内置验证email,验证邮箱格式 array('email','email','邮箱格式不正确',1), //内置验证url,验证网址 array('url','url','URL地址不正确',1), //内置验证currency,验证货币 array('currency','currency','货币格式不正确',1), //内置验证zip,验证邮编 array('zip','zip','邮政编码不正确',1), //内置验证number,验证是不是正整数 array('number','number','不是正整数',1), //内置验证integer,验证是不是整数 array('integer','integer','不是整数',1), //内置验证double,验证是不是浮点数,正负均可 array('double','double','不是浮点数',1), //内置验证english,验证是不是纯英文 array('english','english','不是纯英文',1),
tp3附加规则:配合验证规则使用
规则 | 说明 |
---|---|
regex | 正则验证,定义的验证规则是一个正则表达式(默认) array('tel','/^1[34578]\d{9}$/','手机号码不对!',0,'regex',3),// 必填 array('name','/^[A-Za-z0-9\x{4e00}-\x{9fa5}]{2,20}$/u','不是汉字字母数字组成',1,'regex',3), |
function | 函数验证,定义的验证规则是一个函数名 array('password','checkPwd','密码格式不正确',0,'function'), // 自定义函数验证密码格式 |
callback | 方法验证,定义的验证规则是当前模型类的一个方法 array('user', 'checkLength', '用户名必须在 3-5 位', 0, 'callback', 3,array(3,5)), |
confirm | 验证表单中的两个字段是否相同,定义的验证规则是一个字段名 array('user', 'name', '两个用户名对比不同!',0,'confirm'),
|
equal | 验证是否等于某个值,该值由前面的验证规则定义 array('user', '李炎恢', '值不对等', 0, 'equal'),
|
notequal | 验证是否不等于某个值,该值由前面的验证规则定义(3.1.2版本新增) array('user', '李炎恢', '值不能相等', 0, 'notequal'),
|
in | 验证是否在某个范围内,定义的验证规则可以是一个数组或者逗号分割的字符串 array('user', array(1,2,3), '不在指定范围', 0, 'in'), |
notin | 验证是否不在某个范围内,定义的验证规则可以是一个数组或者逗号分割的字符串(3.1.2版本新增) array('user', array(1,2,3), '不得在指定范围', 0, 'notin'), |
length | 验证长度,定义的验证规则可以是一个数字(表示固定长度)或者数字范围(例如3,12 表示长度从3到12的范围) array('user', '3,5', '不得小于 3 位,不得大于 5 位', 0, 'length'),
|
between | 验证范围,定义的验证规则表示范围,可以使用字符串或者数组,例如1,31或者array(1,31) array('user', array(3,5), '必须是 3-5 之间的数字', 0, 'between'), array('user', '3,5', '必须是 3-5 之间的数字', 0, 'between'), |
notbetween | 验证不在某个范围,定义的验证规则表示范围,可以使用字符串或者数组(3.1.2版本新增) array('user', array(3,5), '必须不是 3-5 之间的数字', 0, 'notbetween'), array('user', '3,5', '必须不是 3-5 之间的数字', 0, 'notbetween'), |
expire | 验证是否在有效期,定义的验证规则表示时间范围,可以到时间,例如可以使用 2012-1-15,2013-1-15 表示当前提交有效期在2012-1-15到2013-1-15之间,也可以使用时间戳定义
array('user', '2014-1-10,2015-10-10', '时间已过期', 0, 'expire'),
|
ip_allow | 验证IP是否允许,定义的验证规则表示允许的IP地址列表,用逗号分隔,例如201.12.2.5,201.12.2.6 array('user', '127.0.0.1', '当前 IP 没有被允许', 0, 'ip_allow'),
|
ip_deny | 验证IP是否禁止,定义的验证规则表示禁止的ip地址列表,用逗号分隔,例如201.12.2.5,201.12.2.6 array('user', '127.0.0.1', '当前 IP 被禁止', 0, 'ip_deny'),
|
unique | 验证是否唯一,系统会根据字段目前的值查询数据库来判断是否存在相同的值,当表单数据中包含主键字段时unique不可用于判断主键字段本身
array('name','','帐号名称已经存在!',0,'unique',1), // 在新增的时候验证name字段是否唯一 |
thinkphp5+:
对某个字段多次验证
namespace app\index\validate; use think\Validate; class Comments extends Validate { protected $regex = [ 'zip' => '/^1[3|4|5|8][0-9]{9}$/']; protected $rule = [ 'name' => 'require', 'phone' => "require|regex:zip", 'phone_time' => 'require', ]; protected $message = [ 'name.require' => '姓名不能为空', 'phone.require' => '联系方式不能为空', 'phone.regex' => '联系方式格式不正确', 'phone_time.require' => '接听时段不能为空', ]; }
验证进阶
自动完成
protected $_auto=array( array('create_time','time',self::MODEL_INSERT,'function'), // 对create_time字段在新增的时候写入当前时间戳 array('update_time','time',self::MODEL_UPDATE,'function'), // 对update_time字段在更新的时候写入当前时间戳 array('pwd','md5',3,'function') , // 对password字段在新增和编辑的时候使md5函数处理 );
字段映射
/** * 字段映射 * @var array */ protected $_map=array( 'member'=>'name', 'password'=>'pwd', );
参数绑定:
手动绑定:
$Model = M('User'); $where['id'] = ':id'; $where['name'] = ':name'; $bind[':id'] = array(I('id'),\PDO::PARAM_INT); $bind[':name'] = array(I('name'),\PDO::PARAM_STR); $list = $Model->where($where)->bind($bind)->select();
自动绑定:
自动绑定不支持参数类型等额外设置,如果有必要请使用上面的手动绑定方式。
'DB_BIND_PARAM' => true //需要开启这个配置 然后 如下: $Model = M('User'); $Model->name = 'thinkphp'; $Model->email = '[email protected]'; $Model->add(); 等效于: $Model = M('User'); $Model->name = ':name'; $Model->email = ':email'; $bind[':name'] = 'thinkphp'; $bind[':email'] = '[email protected]'; $Model->bind($bind)->add();
关联模型:
ThinkPHP 3.2.3 关联模型的使用
HSA_ONE:一对一,一个员工只有一个档案表(BELONGS_TO 也可用作一对一 可以理解他是son BELONGS_TO它的parent)
支持:
属性 | 描述 |
---|---|
class_name | 要关联的模型类名 |
mapping_name | 关联的映射名称,用于获取数据用 该名称不要和当前模型的字段有重复,否则会导致关联数据获取的冲突。 |
foreign_key | 关联的外键名称 |
mapping_fields | 关联要查询的字段 |
condition | 关联条件 |
as_fields | 直接把关联的字段值映射成数据对象中的某个字段 |
mapping_type | 关联类型 |
BELONGS_TO:多对一,一个员工只属于一个部门,但是部门里有多个员工
属性 | 描述 |
---|---|
class_name | 要关联的模型类名 |
mapping_name | 关联的映射名称,用于获取数据用 该名称不要和当前模型的字段有重复,否则会导致关联数据获取的冲突。 |
foreign_key | 关联的外键名称 |
mapping_fields | 关联要查询的字段 |
condition | 关联条件 |
parent_key | 自引用关联的关联字段 默认为parent_id 自引用关联是一种比较特殊的关联,也就是关联表就是当前表。 |
as_fields | 直接把关联的字段值映射成数据对象中的某个字段 |
mapping_type | 关联类型 |
HAS_MANY:一对多,一个员工有多张银行卡,但是一张银行卡只属于一个员工
属性 | 描述 |
---|---|
class_name | 要关联的模型类名 |
mapping_name | 关联的映射名称,用于获取数据用 该名称不要和当前模型的字段有重复,否则会导致关联数据获取的冲突。 |
foreign_key | 关联的外键名称 |
parent_key | 自引用关联的关联字段 默认为parent_id |
condition | 关联条件 关联查询的时候会自动带上外键的值,如果有额外的查询条件,可以通过定义关联的condition属性。 |
mapping_fields | 关联要查询的字段 默认情况下,关联查询的关联数据是关联表的全部字段,如果只是需要查询个别字段,可以定义关联的mapping_fields属性。 |
mapping_limit | 关联要返回的记录数目 |
mapping_order | 关联查询的排序 |
MANY_TO_MANY:多对多,一个员工可以属于多个组,每个组可以有多个员工
属性 | 描述 |
---|---|
class_name | 要关联的模型类名 |
mapping_name | 关联的映射名称,用于获取数据用 该名称不要和当前模型的字段有重复,否则会导致关联数据获取的冲突。 |
foreign_key | 关联的外键名称 外键的默认规则是当前数据对象名称_id |
relation_foreign_key | 关联表的外键名称 默认的关联表的外键名称是表名_id |
mapping_limit | 关联要返回的记录数目 |
mapping_order | 关联查询的排序 |
relation_table | 多对多的中间关联表名称 |
thinkphp5+
查询:
//原生 $result=Db::query('select * from banner_item where img_id=?',[3]); //var_dump($result); $result=Db::table('banner_item')->where('banner_id','=',$id)->select();
一对一:
hasOne('关联模型名','外键名','主键名',['模型别名定义'],'join类型');
belongsTo('关联主表模型名','从表外键名','关联表主表键名',['模型别名定义'],'join类型');
一对多:
hasMany('关联模型名','外键名','主键名',['模型别名定义']);
多对多:
belongsToMany('关联模型名','中间表名','外键名','当前模型关联键名',['模型别名定义']);
banner(记录有个项目有多少个轮播) banner_item(每个轮播的轮换个数) image(图片)
id id banner_id img_id id
1 1 4 4
2 1 5 5
3 1 6 6
banner:banneritem 一对多 (hasMany)
banner_item:img 一对一 (belongsTo,它也是多对一) 这里用hasOne会报错 因为外键在banner_item
注意:
一对一关系,存在主从关系(主表和从表 ),主表不包含外键,从表包含外键。
hasOne 和 belongsTo 都是一对一关系,区别:
在主表(不包含外键)的模型中建立关联关系,用 hasOne
在从表(包含外键)模型中建立关联关系,用 belongsTo
model/Banner.php
namespace app\api\model; use think\Model; use think\Db; class Banner extends Model{ public function items(){ //banner关联 Banner_item表 return $this->hasMany('BannerItem','banner_id','id'); } }
model/BannerItem.php
php namespace app\api\model; use think\Model; class BannerItem extends Model{ public function img(){ //bannner_item 关联image表 //return $this->belongsTo('Image','img_id','id'); return $this->hasOne('Image','img_id','id'); } }
model/Image.php
php namespace app\api\model; use think\Model; class Image extends Model{ }
在控制器方法中调用:
需引入 use app\api\Model\Banner as BannerModel;//这里的Banner和Model的Banner重名
//$banner=BannerModel::with('items')->find($id);//单表关联 $banner=BannerModel::with(['items','items.img'])->find($id);//多表关联(参数是定义的方法名 )
参数表明Banner类里有一个封装关联关系items方法 items.img 表示items方法里关联的表模型又关联了一个封装关联关系的img方法
模型之with()关联预载入
改造:将关联查询封装到Banner.php中
model/Banner.php
php namespace app\api\model; use think\Model; use think\Db; class Banner extends Model{ public function items(){ return $this->hasMany('BannerItem','banner_id','id'); } public static function getBannerById($id){ //TODO:根据bannerid号获取banner信息 $result=self::with(['items','items.img'])->find($id); return $result; } }
控制器方法里这么调用:
php namespace app\api\controller\v1; use think\Controller; use app\api\Model\Banner as BannerModel;//这里的Banner和Model的Banner重名 class Banner extends controller{ public function getBanner($id){ $banner=BannerModel::getBannerById($id); //$banner->toArray();//拿到$data查出来的对象转数组 还有toJson() //unsetc($data['delete_time']);//隐藏数据中的delete_time,不推荐 $banner->hidden(['delete_time','update_time']);//隐藏数据中的某些数据 $banner->visiable(['id','name']);//只显示数据中的某些值 return $banner;
return json($banner);
} }
图片路径不全,我们怎么和查询出来的数据进行拼接
tp5可以用读取器(获取器)来解决
如image表的url字段有个数据为/woman.jpg 并且我们将图片存到public/images目录下的
application/setting.php 定义
return [ 'img_prefix'=>'http://www.tp5.lichihua.com/images' ];
获取可以
$img_prefix=config('setting.img_prefix');
组装:我么可以在model/Image.php中定义一个读取器的方法格式为 get+当前模型的某个字段名+Attr
php namespace app\api\model; use think\Model; class Image extends Model{ protected $hidden=['id','from','delete_time','update_time']; /** * url读取器 组装之后用model查询出来的url就是完整的路径了 * from字段为1表示本站图片 2为网络资源即带有http:完整url路径 * @param max $value 自动获取的image表中url字段的值 * @param max $data image的所有字段数据 * @return string 返回组装好图片的url地址 */ public function getUrlAttr($value,$data){ $finalUrl=$value; if ($data['from']==1) { $finalUrl = config('setting.img_prefix').$value; } return $finalUrl; } }
读取器触发的方式,模型调用字段属性的时候自动调用
如下面的代码也能调用getUrlAttr读取器方法:上面的getBannerById方法在 return $banner;时由于关联模型会关联到Image框架会自动的调用Image模型的每一个模型属性没有显示的调用!
$img=new Image(); $img->url;
但是其他的模型字段也有类似的需求呢?我们就需要新建一个基类集成mode然后其他的model集成这个基类然后将读取器放到基类里,
但是有个弊端当继承基类的模型可能隐式的调用有夺取器的字段时,他会自动调用读取器的,这不是我们所期望的,所以我们需要改造一下!
php namespace app\api\model; use think\Model; class BaseModel extends Model { public function prefixImgUrl($value,$data){ $finalUrl=$value; if ($data['from']==1) { $finalUrl = config('setting.img_prefix').$value; } return $finalUrl; } } php namespace app\api\model; use app\api\model\BaseModel; class Image extends BaseModel{ protected $hidden=['id','from','delete_time','update_time']; public function getUrlAttr($value,$data){ return $this->prefixImgUrl($value,$data); } }
版本控制:
可以在route.php中这样定义:
Route::get("api/:version/banner/:id","api/:version.Banner/getBanner");
然后:
http://www.tp5.lichihua.com/index.php/api/v2/Banner/1
http://www.tp5.lichihua.com/index.php/api/v1/Banner/1
session、cookie
/* SESSION设置 */ 'SESSION_AUTO_START' => true, // 是否自动开启Session 'SESSION_OPTIONS' => array(), // session 配置数组 支持type name id path expire domain 等参数 'SESSION_TYPE' => '', // session hander类型 默认无需设置 除非扩展了session hander驱动 'SESSION_PREFIX' => '', // session 前缀 //'VAR_SESSION_ID' => 'session_id', //sessionID的提交变量
/** * session管理函数 * @param string|array $name session名称 如果为数组则表示进行session设置 * @param mixed $value session值 * @return mixed */ function session($name='',$value='') { $prefix = C('SESSION_PREFIX');//配置文件的session前缀 if(is_array($name)) { // session初始化 在session_start 之前调用
// 第一个参数维数组且存在prefix键 则将此值赋值给配置的SESION_PREFIX if(isset($name['prefix'])) C('SESSION_PREFIX',$name['prefix']);
//如果配置文件重新定义的session_id 且$_COOKIE、$_POST、$_GET存在该值 则设置sessionid 如果传入的数组存在id则设置sesionid(类似秘钥) if(C('VAR_SESSION_ID') && isset($_REQUEST[C('VAR_SESSION_ID')])){ session_id($_REQUEST[C('VAR_SESSION_ID')]); }elseif(isset($name['id'])) { session_id($name['id']); } if('common' == APP_MODE){ // 其它模式可能不支持 ini_set('session.auto_start', 0); } if(isset($name['name'])) session_name($name['name']); if(isset($name['path'])) session_save_path($name['path']); if(isset($name['domain'])) ini_set('session.cookie_domain', $name['domain']); if(isset($name['expire'])) { ini_set('session.gc_maxlifetime', $name['expire']); ini_set('session.cookie_lifetime', $name['expire']); } if(isset($name['use_trans_sid'])) ini_set('session.use_trans_sid', $name['use_trans_sid']?1:0); if(isset($name['use_cookies'])) ini_set('session.use_cookies', $name['use_cookies']?1:0); if(isset($name['cache_limiter'])) session_cache_limiter($name['cache_limiter']); if(isset($name['cache_expire'])) session_cache_expire($name['cache_expire']); if(isset($name['type'])) C('SESSION_TYPE',$name['type']); if(C('SESSION_TYPE')) { // 读取session驱动 $type = C('SESSION_TYPE'); $class = strpos($type,'\\')? $type : 'Think\\Session\\Driver\\'. ucwords(strtolower($type)); $hander = new $class(); session_set_save_handler( array(&$hander,"open"), array(&$hander,"close"), array(&$hander,"read"), array(&$hander,"write"), array(&$hander,"destroy"), array(&$hander,"gc")); } // 启动session if(C('SESSION_AUTO_START')) session_start(); }elseif('' === $value){ if(''===$name){ // 获取全部的session return $prefix ? $_SESSION[$prefix] : $_SESSION; }elseif(0===strpos($name,'[')) { // session 操作 if('[pause]'==$name){ // 暂停session session_write_close(); }elseif('[start]'==$name){ // 启动session session_start(); }elseif('[destroy]'==$name){ // 销毁session $_SESSION = array(); session_unset(); session_destroy(); }elseif('[regenerate]'==$name){ // 重新生成id session_regenerate_id(); } }elseif(0===strpos($name,'?')){ // 检查session $name = substr($name,1); if(strpos($name,'.')){ // 支持数组 list($name1,$name2) = explode('.',$name); return $prefix?isset($_SESSION[$prefix][$name1][$name2]):isset($_SESSION[$name1][$name2]); }else{ return $prefix?isset($_SESSION[$prefix][$name]):isset($_SESSION[$name]); } }elseif(is_null($name)){ // 清空session if($prefix) { unset($_SESSION[$prefix]); }else{ $_SESSION = array(); } }elseif($prefix){ // 获取session if(strpos($name,'.')){ list($name1,$name2) = explode('.',$name); return isset($_SESSION[$prefix][$name1][$name2])?$_SESSION[$prefix][$name1][$name2]:null; }else{ return isset($_SESSION[$prefix][$name])?$_SESSION[$prefix][$name]:null; } }else{ if(strpos($name,'.')){ list($name1,$name2) = explode('.',$name); return isset($_SESSION[$name1][$name2])?$_SESSION[$name1][$name2]:null; }else{ return isset($_SESSION[$name])?$_SESSION[$name]:null; } } }elseif(is_null($value)){ // 删除session if(strpos($name,'.')){ list($name1,$name2) = explode('.',$name); if($prefix){ unset($_SESSION[$prefix][$name1][$name2]); }else{ unset($_SESSION[$name1][$name2]); } }else{ if($prefix){ unset($_SESSION[$prefix][$name]); }else{ unset($_SESSION[$name]); } } }else{ // 设置session if(strpos($name,'.')){ list($name1,$name2) = explode('.',$name); if($prefix){ $_SESSION[$prefix][$name1][$name2] = $value; }else{ $_SESSION[$name1][$name2] = $value; } }else{ if($prefix){ $_SESSION[$prefix][$name] = $value; }else{ $_SESSION[$name] = $value; } } } return null; }
sesion初始化及设置
//初始化 sesion(array('name'=>'replacePHPSESSID'),'expire'=>3600,'id'=>'9h1bano5p1lvt6tac5nvbboac7'); //设置session session('loginSign',1)
ThinkPHP 事务处理 (事务回滚) 、异常处理
//进行付款处理 事务实现付款 $user=M('User'); $user->startTrans(); // $new_user_money=$user_money-$order_total_money; //用户的积分增加 $user_score= $user_info[0]['user_score']+$order_total_money; $user_status=$user->where(array('user_id'=>$user_id))->save(array('user_score'=>$user_score)); // $order_status=M("Order")->where(array('order_id'=>$order_id))->save(array('order_state'=>'已付款')); //判断两个是否都执行成功 if($order_status&&$user_status){ $user->commit(); //付款成功就显示到页面上 //把信息写到用户消费表 $user_payment['user_payment_money']="-".$order_total_money; $user_payment['user_payment_why']="在线购买"; $user_payment['user_payment_time']=time(); $user_payment['user_id']=$user_id; M('UserPayment')->add($user_payment); //删除session中保存的order_id信息 session(C("USER_ORDER_ID"),null); //显示信息到前台去 $this->order_no=$order_info[0]['order_no']; $this->order_total_money=$order_info[0]['order_total_money']; }else{ //执行失败就回滚 $user->rollback(); $this->error('付款失败'); exit; }
多表的回滚:
$m=D('YourModel');//或者是M(); $m2=D('YouModel2'); $m->startTrans();//在第一个模型里启用就可以了,或者第二个也行 $result=$m->where('删除条件')->delete(); $result2=m2->where('删除条件')->delete(); if($result && $result2){ $m->commit();//成功则提交 }else{ $m->rollback();//不成功,则回滚 }
common 无法通过 域名/common/index/index 这种方式访问
我们需要在某个模块下定义一个commom方法 然后实例化并调用他 如:aplication/index/controller/index.php
//application/index/controller/Index.php php namespace app\index\controller; use app\common\controller\Index as commonIndex; class Index{ public function index(){} public function common(){ $common = new commomIndex(); return $common->index(); } } //aplication/common/controller/Index.php php namespace app\common\controller; class Index { public function index(){ } }
//application/index/controller/User.php php namespace app\index\controller; use app\common\controller\User as commonUser; class User extends commonUser{ public function demo(){ return $this->showName('zhangsan'); } } //aplication/common/controller/User.php php namespace app\common\controller; class User { public function showName($name=' '){ return "my name is {$name}"; } }
Controller:
属性:
$view : \think\View 视图类实例 赋值__construct $request : \think\Request Request 实例 赋值__construct $failException: bool 验证失败是否抛出异常 $batchValidate:bool 是否批量验证 $beforeActionList:array 前置操作方法列表
方法:
_initialize:初始化操作 beforeAction($method, $options = []):前置操作 fetch($template = '', $vars = [], $replace = [], $config = []):加载模板输出 display($content = '', $vars = [], $replace = [], $config = []):渲染内容输出 assign($name, $value = ''):模板变量赋值 engine($engine):初始化模板引擎 validateFailException($fail = true):设置验证失败后是否抛出异常 validate($data, $validate, $message = [], $batch = false, $callback = null):验证数据
model: