不使用框架的问题
在实际工作中,如果不使用框架会遇到的问题。
程序项目生命时间非常短(维护性,生命力弱)
分共协作开发项目,彼此代码风格不一致。
开发程序,喜欢挖坑。
开发者离职,需要有人维护该离职着的代码风格.
牵一发而动全身
框架的最大的特点使得程序的业务逻辑与数据模型分开。
相关信息
ThinkPhp, 面向过程,面向对象,轻量级。
重量级:功能多,OOP面向对象,维护性好,生命力顽强。
轻量级:功能实用,面向过程和面向对象混合开发。
创建应用
创建入口文件,并引入核心框架入口文件 (index.php/app.php)
// TP框架核心框架核心程序引入
require('./ThinkPHP/ThinkPHP.php');
访问文件地址,配置虚拟目录
执行流程
运行入口文件
// 定义系统目录
define('APP_PATH', './');
// 修改为调试模式
define('APP_DEBUG', true);
TP的入口文件
ThinkPHP.php
载入
Common/runtime.php
声明常量信息
执行:`load_runtime_file()`,作用:加载运行时所需要的文件 并负责自动目录生成
执行入口:`Think::Start();`
执行:
Lib/Core/Think.class.php
static public function start() {}; // 应用程序初始化
Think::buildApp(); // 预编译项目
App::run(); // 运行应用
执行:
Lib/Core/App.class.php
static public function run() {}; // 运行应用实例 入口文件使用的快捷方法
App::init(); // 应用程序初始化
Dispatcher::dispatch(); // URL调度
// 分析路由(控制器`MODULE_NAME` 方法`ACTION_NAME`) // e.g: index.php?c=控制器&a=方法
App::exec(); // 执行应用程序
通过反射ReflectionMethod使得控制器对象调用对应的方法.
控制器和简单模板创建
如何分离控制器
根据业务特点,把控制器分离(User, Goods)
路由解析
通过GET方式告知应用,请求控制器和操作方法.
GET最基本方式
http://www.tp.com/shop/go/index.php?m=User&a=register
路径方式
http://www.tp.com/shop/go/index.php/User/register
伪静态方式
http://www.tp.com/shop/go/User/register
兼容模式 (兼容基本方式和路径方式)
http://www.tp.com/shop/go/index.php?s=User/login
在Tpl
目录下创建对应的控制器标识作为文件夹(Goods
),对应控制器的动作作为访问文件(showlist.html
)。
控制器中调用display()
;
<?php
/**
* 商品控制器
*/
class GoodsAction extends Action {
// 展示商品
public function showList() {
$this->display();
}
// 查看商品的详细信息
public function detail() {
$this->display();
}
}
?>
模板引入框架中
把模板引入ThinkPH框架中,出现的样式路径,图片路径,JavaScript路径不正确。
CSS文件和图片文件位置原则:可以单独被访问.
配置资源的public
文件,放置css,images,js等文件。
需要配置基本常量.
// 路径常量
define('SITE_URL', 'http://tp.com/'); // 网站地址
define('CSS_URL', SITE_URL . 'shop/go/public/css/'); // 前台页面CSS路径
define('IMAGE_URL', SITE_URL . 'shop/go/public/images'); // 前台页面图片路径常量
模板中如何使用常量
利用thinkphp默认的模板引擎调用常量。{$Think.const.常量名}
空操作和空模块
空操作
空请求:http://xxxx/index.php?m=user&a=pink
; pink
没有对应的操作动作,是一个空请求。
空操作,一个类实例化对象,对象调用类中不存在的方法
。在OOP中有魔术方法,__call()
,自动调用该魔术方法。
空操作处理:
对应的控制器中定义方法
_empty()
在应用
Common/common.php
中添加一个函数名:__hack_action()
。
空模块
ThinkPHP中把MVC中的控制器称之为:模块(module).
空模块:http://xxxx/index.php?m=color&a=pink
; color
不存在的控制器
空模块处理方式:
对应的控制器中定义模块:
EmptyAction.class.php
在应用
Common/common.php
中添加一个函数名:__hack_module
项目分组设置
ThinkPHP模块的分组:
'APP_GROUP_LIST' => 'home,admin', // 项目分组设定,多个组之间用逗号分隔,例如'Home,Admin'
项目分组对路由的影响:http://www.xxxx.com/index.php/分组名称/控制器/方法
分组的范围:
Action控制器分组
Tpl模板分组
配置文件
静态资源分组
frameset搭建后台页面
使用:frameset
标签进行搭建.
获得常量信息:get_defined_constants(true)
; // true参数表示分类
修改路由链接(使用绝对地址)
<frameset border=0 framespacing=0 rows="60, *" frameborder=0>
<frame name=head src="{$Think.const.__URL__}/head" frameborder=0 noresize scrolling=no>
<frameset cols="170, *">
<frame name=left src="{$Think.const.__URL__}/left" frameborder=0 noresize />
<frame name=right src="{$Think.const.__URL__}/right" frameborder=0 noresize scrolling=yes />
</frameset>
</frameset>
后台商品列表
后台商品列表-修改-增加
<?php
/**
* 后台商品控制器
*/
class GoodAction extends Action {
// 商品列表
public function showList() {
$this->display();
}
// 添加商品
public function add() {
$this->display();
}
// 修改商品
public function upd() {
$this->display();
}
}
?>
跨模块调用
利用自动加载
$user = new UserAction();
$user->number();
系统提供:A函数调用
// A函数用于实例化Action 格式:[项目://][分组/]模块
// 调用当前项目
$user = A('home/User');
$user->number();
// 调用不同项目中的控制器
$user = A('book://Index');
echo $user->info();
系统提供:远程调用模块的操作方法 URL
// 远程调用模块的操作方法 URL 参数格式 [项目://][分组/]模块/操作方法
echo R('home/User/number');
简单model模型创建
数据库连接
在配置文件中配置数据库基本信息
// 数据库
'DB_TYPE' => 'mysql', // 数据库类型
'DB_HOST' => 'localhost', // 服务器地址
'DB_NAME' => 'shop', // 数据库名
'DB_USER' => 'root', // 用户名
'DB_PWD' => '', // 密码
'DB_PORT' => '', // 端口
'DB_PREFIX' => 'sw_', // 数据库表前缀
'DB_FIELDTYPE_CHECK' => false, // 是否进行字段类型检查
'DB_FIELDS_CACHE' => true, // 启用字段缓存
数据库中每张数据表都对应一个数据model模型类。
简单model模型创建与使用
创建文件:GoodsModel.class.php
<?php
/**
* 商品model模型类
*/
class GoodsModel extends Model {
}
?>
字段缓存:
// 出于性能考虑,要把数据表字段放入缓存中,下次访问就避免执行SQL语句重复执行。
// 前提,是生产模式,字段缓存有效.
'DB_FIELDS_CACHE' => true, // 启用字段缓存
基类Model部分属性:
<?php
/**
* 第三方表
*/
class QqModel extends Model {
protected $trueTableName = 'tencen_qq'; // 第三方表名;
}
?>
实例化的三种方法
普通的实例化方法
$goods_model = new GoodsModel();
快捷方式
// D(); D函数用于实例化Model 格式 项目://分组/模块
$goods_model = D('Goods');
实例化没有模型文件的Model
$model = new Model(); // 实例化基类
// 指定实例化Model
$model = M('CateGory'); // 调用M(); 函数 // M函数用于实例化一个没有模型文件的Model
查询数据select方法
查询基本使用
// 查询数据
$goods_model = new GoodsModel();
// 查询全部数据
$info = $goods_model->select(); // select(记录主键值); 方法查询数据 // 返回二维数据
// 查询一条记录
$info = $goods_model->select(7);
// 查询多条记录
$info = $goods_model->select('17, 20, 23'); // SELECT * FROM `sw_goods` WHERE ( `goods_id` IN ('17',' 20',' 23') )
查询相关操作方法
find() 返回一条记录
// 返回一维数组, 每次只返回一条数据
$info = $goods_model->find(7); // SELECT * FROM `sw_goods` WHERE ( `goods_id` = 7 ) LIMIT 1
field() 固定字段
// 查询固定字段 // 指定查询字段 支持字段排除
$info = $goods_model->field('goods_name, goods_price, goods_number, goods_cretae_time')->select();
limit() 查询条
// 查询条数
// $info = $goods_model->limit(长度)
// $info = $goods_model->limit(偏移量, 长度)
$info = $goods_model->limit(5, 5)->select();
order() 排序
// 排序
// $info = $goods_model->order(条件 倒序/正序);
$info = $goods_model->order('goods_price desc')->select();
// 链式调用
$info = $goods_model->order('goods_price desc')->limit(5)->select();
order()
是Model
不存在的方法,会执行魔术方法__call()
自动调用
where() 设置条件
// 设置条件
$info = $goods_model->where('goods_price > 5000')->select();
table() 设置表名
// 设置表名
$info = $goods_model->table('sw_goods')->select();
group() 分组
// 分组
$info = $goods_model->group('goods_category_id')->select();
模型相关方法分析
Model.class.php 类本身就存在该方法。例如:where(), filed(), limit(), select()
__call()
自动调用方法集成了一些方法,可以链式调用 例如:table(), order(), group()
getByXXX() 查询数据
返回一维数组信息.
根据指定字段查询数据信息
$info = $goods_model->getByGoods_price('5999'); // 自动调用`__call()`魔术方法
having() 设置查询条件
和where一样效果,比where 执行晚. 可以对结果结果集进行操作.
having 可以和聚合函数一起使用
$info = $goods_model->having('goods_name like "A%"')->select();
model 聚合函数
// 聚合函数
$info = $goods_model->where('goods_id>50')->select();
$num = $goods_model->where('goods_id>50')->count(); // SELECT COUNT(*) AS tp_count FROM `sw_goods` WHERE ( goods_id>50 ) LIMIT 1
echo $num;
$total_price = $goods_model->where('goods_id>50')->sum('goods_price'); // SELECT SUM(goods_price) AS tp_sum FROM `sw_goods` WHERE ( goods_id>50 ) LIMIT 1
echo $total_price;
sum(字段)
,count(*/字段)
,max(字段)
,min(字段)
,avg(字段)
。
原生SQL语句
提供2个方法:
$model->query()
查询语句, 返回二维数据$model->execute()
增加,修改,删除, 返回受影响记录数目
// 执行原生SQL
// select g.goods_name, g.goods_price, c.cat_name from sw_goods as g left join sw_category as c on g.goods_category_id = c.cat_id;
$sql = "select g.goods_name, g.goods_price, c.cat_name from sw_goods as g left join sw_category as c on g.goods_category_id = c.cat_id";
$info = $goods_model->query($sql);
smaty模板使用
在Extend/Vendor
放入smart模板
ThinkPHP配置信息有两部分:convertion.php
和Lib/Behavior/*
配置
在配置文件中配置模板类型:
'TMPL_ENGINE_TYPE' => 'Smarty', // 修改模板引擎
显示日志信息
配置信息中配置日志信息显示:
前提:必须调用$this->display()
才会显示日志信息。就必须显示模板
'SHOW_PAGE_TRACE' => true, // 显示页面Trace信息
操作数据
数据添加
两种方式实现数据添加: 数组方式,AR方式
数组方式
// 模型对象
$goods_model = new GoodsModel();
// 实现数据添加
// 数组下标与数据库字段名一致. // 获取数据
$data = array(
'goods_name' => 'htc100',
'goods_price' => '3999',
'goods_numer' => 45,
'goods_weight' => 103
);
$goods_model->add($data); // 返回自动生成的ID值
AR 方式
Active Record 活跃记录
AR记录的规则:
数据库中的每个
数据表
对应一个类
数据表中的每条
记录
都是一个类的一个对象
记录信息的每个
字段
都是对象的属性
// AR方式实现数据添加
// 对象调用不存在的属性需要调用魔术方法`__set()`
$goods_model->goods_name = 'iphone7puls';
$goods_model->goods_price = '5700';
$goods_model->goods_number = 41;
$goods_model->goods_weight = 100;
$rst = $goods_model->add(); // 返回影响记录的条数
收集表单数据
Array
$data = $_POST;
$cnt = $goods_model->add($data);
AR方式
foreach( $_POST as $k => $v ) {
$goods_model->$k = $v;
}
$goods_model->add();
ThinkPHP
create
方式
$data = $goods_model->create();
$cnt = $goods_model->add($data);
数据修改
save()方法保存数据
// 修改商品
public function upd() {
$goods_model = new GoodsModel();
// 修改数据, 需要设置主键ID和where条件
$data = array(
'goods_id' => 55,
'goods_name' => '红米',
'goods_price' => 4000
);
$rst = $goods_model->save($data); // 返回受影响记录的数目
$data = array(
'goods_name' => '香米',
'goods_price' => 4000
);
$rst = $goods_model->where('goods_id=57')->save($data);
echo $rst;
$this->display();
}
AR方式
// 主键方式
$goods_model->goods_id = 58;
$goods_model->goods_name = 'APPLE';
$goods_model->goods_price = 4000;
$goods_model->save();
// where 条件方式
$goods_model->goods_name = 'huawei';
$goods_model->goods_price = 4000;
$snt = $goods_model->where('goods_id=56')->save();
修改数据注意:设置where条件
,或者主键条件
。
删除数据
delete(主键)
$goods_model->delete(57);
路由获取形式
// 获取参数形式
// http://www.tp.com/index.php?m=控制器&a=操作&goods_id=100&goods_price=2300
// http://www.tp.com/index.php/控制器/操作/参数1/值1/参数2/值2
// function upd( 参数1, 参数2 ) {
// $_GET['goods_id'];
// }
// URL地址参数要与方法参数一致
错误信息处理
在控制器中调用方法:
$this->success('修改成功', __URL__ .'/showList');
系统会寻找dispatch_jump.tpl
文件
在Lib/Core/View.class.php
中修改parseTemplate
方法
/**
* 自动定位模板文件
* @access protected
* @param string $template 模板文件规则
* @return string
*/
public function parseTemplate($template='') {
/ 判断是否存在模板文件 移动到后面判断.
if(is_file($template)) {
return $template;
}
}
表单验证
前提:收集表单数据必须通过create()
方法来收集. 定义的验证规则通过create()
方法触发.
ThinkPHP自动验证:TP自动验证
在UserModel.class.php
中重写$_validate
:
<?php
class UserModel extends Model {
// 是否批处理验证
protected $patchValidate = true;
// 自动验证规则定义
// array(验证字段1,验证规则,错误提示,[验证条件,附加规则,验证时间])
protected $_validate = array(
// 用户名
array('username', 'require', '用户名必须填写'),
// 密码
array('password', 'require', '密码必须填写'),
// 确认密码
array('password2', 'require', '重复密码必须填写'),
array('password2', 'password', '确认密码必须一样', 0, 'confirm'),
// 邮箱
array('user_email', 'email', '邮箱格式必须正确', 2),
// qq号码 // 全部是数字 // 第一个为非零
array('user_qq', 'require', 'qq必须填写'),
array('user_qq', '/^[1-9]\d{4,13}$/', 'qq格式必须正确'),
// 手机号码
array('user_tel', 'require', '手机号必须填写'),
array('user_tel', '/^1[34578]\d{9}$/', '手机号格式必须正确'),
// 学历 // value值必须在范围之内 2,3,4,5
array('user_xueli', array(2, 3, 4, 5), '学历必须选择一项', 0, 'in'),
// 爱好 // 必须选择一个项目以上
array('user_hobby', 'check_hobby', '爱好必须选择', 1, 'callback')
);
/**
* 验证爱好
* @param {Array} $hobby user_hobby 值
* // e.g 1 [篮球] 2 [足球] 3 [排球] 4 [棒球]
* @return {Boolean} 验证是否通过
*/
public function check_hobby( $hobby ) {
// $_POST['user_hobby']
if ( count($hobby) < 2 ) {
return false;
} else {
return true;
}
}
}
?>
Smarty引入流程
-
控制器IndexAction.class.php
function index()$this->display(); (父类Action的display)
-
父类ThinkPHP/Lib/Core/Action.class.php
$this->view->display();
-
ThinkPHP/Lib/Core/View.class.php
function display()// 解析并获取模板内容 $content = $this->fetch($templateFile,$content,$prefix);
// 解析和获取模板内容 用于输出
function fetch()tag('view_parse',$params);
-
ThinkPHP/Conf/tags.php
'view_parse' => array('ParseTemplate', // 模板解析 支持PHP、内置模板引擎和第三方模板引擎 (Bahavior行为)
),
-
parseTempliteBahavior.class.php
function run()$class = 'TemplateSmarty'
// ThinkPHP/Extend/Dirver/Template/TemplateSmarty.class.php if(class_exists($class)) { // 通过自动加载机制引入对应类文件. $tpl = new $class; $tpl->fetch($_content,$_data['var']); }else { // 类没有定义 throw_exception(L('_NOT_SUPPERT_').': ' . $class); }
-
ThinkPHP/Extend/Dirver/Template/TemplateSmarty.class.php
public function fetch($templateFile,$var)
// 寻找Smarty实体. // ThinkPHP/Extend/Vendor/Smarty/Smarty.class.php vendor('Smarty.Smarty#class'); // 获取真正的Smarty $tpl = new Smarty(); C(); // 会读取配置文件信息
smarty布局与继承
利用extends
和include
完成
{extends file="public/layout.html"}
{block name="main"}
// 子模板代码
{/block}
{include file="public/ucenterleft.tpl"}
问题:当代码公共出去之后,对应不同标签需要显示不同的样式?
通过路由解析 (如何在模板中拿到路由中的操作方法
$smarty.const.ACTION_NAME
)通过路由参数
display() 显示模板的四种方法
ThinkPHP框架调用模板:$this->display()
ThinkPHP会自动把模板名称拼装好,与操作名一致。
调用当前模块下的模板:$this->display(模板名字)
模板名字没有后缀
调用其它模块下的模板:$this->display(模块/模板名)
相对路径找到模板文件:(相对于入口文件index.html
)$this->dispaly(相对路径)
引入机制
import
引入机制
例如:import('a.b.c'); // a/b/c.class.php
可以引入那些位置的类文件?
-
本身项目的类文件 [对应的类文件都需要创建在
Lib
目录下]import('@.dir.dir.file'); e.g:import('@.Model.QqModel.class.php'); // Lib/Model/QqModel.class.php
-
ThinkPHP核心类文件
import('think.dir.dir.file'); e.g:import('think.car.dirver'); // ThinkPHP/Lib/car/dirver.class.php
-
扩展的类文件(ThinkPHP/Extend), 第三库文件引入
import('ORG.dir.dir.file'); e.g: import('ORG.color.pink'); // ThinkPHP/Extend/Library/ORG/color/pink.class.php
-
引入一个特殊文件名的类文件。
#
号使用。// 找到文件为:// Lib/apple/bananer.good.flash.class.php import('@.apple.bananer#good#flash'); // 文件名为:`bananer.good.flash.class.php`
验证码
Image
类没有在自动加载类中载入,需要手动载入。PHP底层的include
和require
会影响原始的框架中的引入,可以使用框架中提供的import
引入。
import('ORG/Util/Image');
echo Image::buildImageVerify();
显示验证码
<img src="{$smarty.const.__URL__}/verifyImg" alt="" />
验证验证码
if ( !empty($_POST['captcha']) && md5($_POST['captcha']) == $_SESSION['verify'] ) {}
用户登陆
登陆信息在模型中验证
<?php
/**
* 后台管理员模型
*/
class ManagerModel extends Model {
/**
* 用户名和密码验证
* @param {String} $name 用户名
* @param {String} $pwd 密码
* @return {Mixin} 匹配到的用户名和密码 或者 false
*/
public function checkNamePwd($name, $pwd) {
// 用户名和密码是否存在
$name_info = $this->getByMg_name($name);
// 用户名和密码是否正确
if ( $name_info != null ) {
// 密码
if ( $name_info['mg_pwd'] != $pwd ) {
return false;
} else {
return $name_info;
}
} else {
return false;
}
}
}
?>
thinkphp中的session
session的操作:
session(name, value) // 设置session
session(name, null) // 删除指定session
session(name) // 获取session信息
session(null) // 清空全部session
验证验证码 --> 校验用户名和密码 --> 判断用户名 .
<?php
/**
* 后台管理员控制器
*/
class ManagerAction extends Action {
// 登陆系统
public function login() {
if ( !empty($_POST) ) {
// 验证码
if ( md5($_POST['captcha']) == $_SESSION['verify'] ) {
// 用户名和密码 ,有效性
// 数据模型中自定义方法校验用户名和密码
$manager_model = D('Manager');
// 校验密码
$user_info = $manager_model->checkNamePwd($_POST['mg_name'], $_POST['mg_pwd']);
// 判断密码
if ( $user_info != false ) {
// 持久化用户信息(id和名字)
session('mg_name', $user_info['mg_name']);
session('mg_id', $user_info['mg_id']);
// 页面重定向
$this->redirect('Index/index');
} else {
echo '用户名或密码错误';
}
} else {
echo '验证码不正确';
}
}
$this->display();
}
// 退出系统
public function logout() {
// 删除session信息
session('mg_name', null); // 删除用户名
session('mg_id', null); // 删除id
$this->redirect('Manager/login');
}
// 生成验证码
public function verifyImg() {
import('ORG/Util/Image');
echo Image::buildImageVerify();
}
}
?>
数据分页
自定义的分页类
<?php
class Page {
private $total; //数据表中总记录数
private $listRows; //每页显示行数
private $limit;
private $uri;
private $pageNum; //页数
private $config = array('header'=>"个记录", "prev"=>"上一页", "next"=>"下一页", "first"=>"首 页", "last"=>"尾 页");
private $listNum = 8;
/*
* $total
* $listRows
*/
public function __construct($total, $listRows=10, $pa=""){
$this->total=$total;
$this->listRows=$listRows;
$this->uri=$this->getUri($pa);
$this->page=!empty($_GET["page"]) ? $_GET["page"] : 1;
$this->pageNum=ceil($this->total/$this->listRows);
$this->limit=$this->setLimit();
}
private function setLimit(){
return "Limit ".($this->page-1)*$this->listRows.", {$this->listRows}";
}
private function getUri($pa){
$url=$_SERVER["REQUEST_URI"].(strpos($_SERVER["REQUEST_URI"], '?')?'':"?").$pa;
$parse=parse_url($url);
if(isset($parse["query"])){
parse_str($parse['query'],$params);
unset($params["page"]);
$url=$parse['path'].'?'.http_build_query($params);
}
return $url;
}
public function __get($args){
if($args=="limit")
return $this->limit;
else
return null;
}
private function start(){
if($this->total==0)
return 0;
else
return ($this->page-1)*$this->listRows+1;
}
private function end(){
return min($this->page*$this->listRows,$this->total);
}
private function first(){
$html = "";
if($this->page==1)
$html.='';
else
$html.=" <a href='{$this->uri}&page=1'>{$this->config["first"]}</a> ";
return $html;
}
private function prev(){
$html = "";
if($this->page==1)
$html.='';
else
$html.=" <a href='{$this->uri}&page=".($this->page-1)."'>{$this->config["prev"]}</a> ";
return $html;
}
private function pageList(){
$linkPage="";
$inum=floor($this->listNum/2);
for($i=$inum; $i>=1; $i--){
$page=$this->page-$i;
if($page<1)
continue;
$linkPage.=" <a href='{$this->uri}&page={$page}'>{$page}</a> ";
}
$linkPage.=" {$this->page} ";
for($i=1; $i<=$inum; $i++){
$page=$this->page+$i;
if($page<=$this->pageNum)
$linkPage.=" <a href='{$this->uri}&page={$page}'>{$page}</a> ";
else
break;
}
return $linkPage;
}
private function next(){
$html = "";
if($this->page==$this->pageNum)
$html.='';
else
$html.=" <a href='{$this->uri}&page=".($this->page+1)."'>{$this->config["next"]}</a> ";
return $html;
}
private function last(){
$html = "";
if($this->page==$this->pageNum)
$html.='';
else
$html.=" <a href='{$this->uri}&page=".($this->pageNum)."'>{$this->config["last"]}</a> ";
return $html;
}
private function goPage(){
return ' <input type="text" onkeydown="javascript:if(event.keyCode==13){var page=(this.value>'.$this->pageNum.')?'.$this->pageNum.':this.value;location=\''.$this->uri.'&page=\'+page+\'\'}" value="'.$this->page.'" style="width:25px"><input type="button" value="GO" onclick="javascript:var page=(this.previousSibling.value>'.$this->pageNum.')?'.$this->pageNum.':this.previousSibling.value;location=\''.$this->uri.'&page=\'+page+\'\'"> ';
}
public function fpage($display=array(0,1,2,3,4,5,6,7,8)) {
$html[0]=" 共有<b>{$this->total}</b>{$this->config["header"]} ";
$html[1]=" 每页显示<b>".($this->end()-$this->start()+1)."</b>条,本页<b>{$this->start()}-{$this->end()}</b>条 ";
$html[2]=" <b>{$this->page}/{$this->pageNum}</b>页 ";
$html[3]=$this->first();
$html[4]=$this->prev();
$html[5]=$this->pageList();
$html[6]=$this->next();
$html[7]=$this->last();
$html[8]=$this->goPage();
$fpage='';
foreach($display as $index){
$fpage.=$html[$index];
}
return $fpage;
}
}
使用:
public function showList() {
$goods_model = new GoodsModel();
// 引入分页类
import('@.Components.Page');
// 计算当前记录总数目
$total = $goods_model->count();
// 每页5条
$per = 5;
// 实例化分页对象
$page = new Page($total, $per);
// 获得页面列表
$page_list = $page->fpage();
// $page_list = $page->fpage(array(3, 4, 5, 6, 7, 8));
// SQL语句,获得每页的信息
$sql = "select * from sw_goods " . $page->limit;
$info = $goods_model->query($sql);
// 设置数据
$this->assign('info', $info);
$this->assign('page_list', $page_list);
// 显示
$this->display();
}
缓存
把数据库中的信息获取出来,放到一个缓存介质里边,在相当长的一段时间之内,重复的数据在缓存中读取。
缓存介质:内存,file文件,数据库(超多张表,联表查询)。
// 设置缓存
public function sSet() {
// 缓存周期,默认永久。 增加缓存有效期
S('username', 'admin', 1800); // 1800 半个小时 // 过期自动删除
S('goods_info', array('apple', 'WeChat')); // 数组
}
// 获取缓存
public function gGet() {
echo S('username');
}
// 删除缓存
public function dDel() {
S('username', null);
}
缓存使用规则:
public function getInfo() {
// 1. 首先要去缓存中获取商品信息
$g = S('info');
// 2. 缓存有商品信息,直接返回. // 否则去数据库查询数据返回,并放入缓存中,设置缓存周期.
if ( !empty($g) ) {
return $g;
} else {
$g = 'apple'. time();
// 数据放入缓存
S('info', $g, 10); // 设置时间
// 返回数据
return $g;
}
}
多语言设置
配置行为
在配置文件config.php
中配置多语言参数
// 配置多语言参数
'LANG_SWITCH_ON' => true, // 默认关闭语言包功能
'LANG_AUTO_DETECT' => true, // 自动侦测语言 开启多语言功能后有效
'LANG_LIST' => 'zh-cn, zh-tw, en-us', // 允许切换的语言列表 用逗号分隔
'VAR_LANGUAGE' => 'hl', // 默认语言切换变量
设置
tags.php
执行行为
<?php
return array(
'app_begin' => array(
'ReadHtmlCache', 'CheckLang' // 读取静态缓存 , 检测语言
),
);
?>
定义语言文件
定义中文简体:../Lang/zh-us/admin/manager.php
// 模块名称/控制器名称
定义中文繁体:../Lang/zh-tw/admin/manager.php
// 模块名称/控制器名称
ThinkPHP中有在视图可以读取语言信息的变量$Think.language.username
Smarty中没有读取,需要通过控制器中的L()
函数读取,然后赋值设置。
// 读取语言变量信息
// L(name); 读取指定语言信息
// L(); 把全部语言信息以数组信息返回
$lang = L();
$this->assign('lang', $lang);
自动完成
收集表单信息,把数据存入数据库中,可以使用自动完成机制
,对即将入库的信息进行二次加密.
自动完成类似表单验证:表单验证在create()
方法内部触发。 而自动完成和自动映射也都是通过create()
触发。
手册:自动完成
自动完成,自动过滤,自动验证,自动映射,缓存,等操作都放入模型中操作(对于操作数据)。
// 自动完成 // 自动完成定义
protected $_auto = array(
// array('填充字段', '填充内容', ['填充条件', '附加规则'])
array('password', 'md5', 3, 'function'),
array('user_time', 'time', 1, 'function')
);
自动映射
// 自动映射, 把一个 form表单中的name和 数据库中的字段对应起来
protected $_map = array(
'email' => 'user_emial',
'qq' => 'user_qq'
);
面向切面编程
程序开发,执行不同的环节,不同的功能利用不同的文件进行处理。
把大块的功能切割为小块的功能执行。
整体TP框架执行,内部不是一个文件从头到尾,不同的功能由不同的文件分别执行。
作用:
系统执行由许多不同程序的执行最终看到统一的效果。
有利于程序开发,维护。许多小功能文件分别开发。
可以把系统功能开发非常完善,有利于框架的整体协调工作。
系统升级也可以分为具体小功能模块升级。
tag("app_begin");
function tag()
配置tags变量信息
B()
function B()
实例化行为Behavior,调用run()方法
tags.php
快捷函数
U(分组/模块/操作)根据参数获得具体url地址
A(项目://分组/模块)实例化模块
R(项目://分组/模块/操作)实例化模块并调用相关方法
C(名称)读取配置变量信息
L(名称)读取指定语言变量信息
D(); D函数用于实例化Model 格式 项目://分组/模块
RBAC
RBAC (rol base access) 基于角色的权限控制
用户权限分配:权限比较直观,一项项分配。
缺点:不利于权限管理。(分配繁琐,管理混乱)
打包权限,分类管理。
利用角色对权限进行打包。
3张数据表
权限数据表
角色数据表
系统用户数据表
--用户表
CREATE TABLE IF NOT EXISTS `sw_manager` (
`mg_id` int NOT NULL AUTO_INCREMENT,
`mg_name` varchar(20) NOT NULL comment '名称',
`mg_pwd` varchar(32) NOT NULL comment '密码',
`mg_time` int unsigned NOT NULL comment '时间',
`mg_role_id` tinyint(1) unsigned not null default 0 comment '角色id',
PRIMARY KEY (`mg_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--权限表
CREATE TABLE IF NOT EXISTS `sw_auth` (
`auth_id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
`auth_name` varchar(20) NOT NULL comment '名称',
`auth_pid` smallint(6) unsigned NOT NULL comment '父id',
`auth_c` varchar(32) not null default '' comment '模块',
`auth_a` varchar(32) not null default '' comment '操作方法',
`auth_path` varchar(32) NOT NULL comment '全路径',
`auth_level` tinyint not null default 0 comment '权限级别012',
PRIMARY KEY (`auth_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--角色表
CREATE TABLE IF NOT EXISTS `sw_role` (
`role_id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
`role_name` varchar(20) NOT NULL comment '角色名称',
`role_auth_ids` varchar(128) not null default '' comment '权限ids,1,2,5',
`role_auth_ac` text comment '模块-操作',
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
查询用户具体拥有的权限
// 用户 -- 角色 -- 权限
// $_SESSION['mg_id'];
// manager role auth
$model = M();
$sql = "select r.role_auth_ids from sw_manager as m join sw_role as r on m.mg_role_id = r.role_id where m.mg_id = " .$_SESSION['mg_id'];
$info = $model->query($sql);
$auto_ids = $info[0]['role_auth_ids'];
// 查询权限
// 查询父权限
$sql = "select * from sw_auth where auth_id in ($auto_ids) and auth_level=0";
$p_auth_info = $model->query($sql);
// 查询子权限
$sql = "select * from sw_auth where auth_id in ($auto_ids) and auth_level=1";
$s_auth_info = $model->query($sql);
$this->assign('p_auth', $p_auth_info);
$this->assign('s_auth', $s_auth_info);
在普通的控制器新增父类,继承当前的父类,而非框架中的Action
父类。
<?php
/**
* 普通控制器的父类
*/
class AdminAction extends Action {
// 控制用户访问的模块和操作方法
public function __construct() {
parent::__construct(); // 执行父类的构造函数
// 获取当前请求的模块和方法 , 然后与角色拥有的模块和方法权限对比
$model = MODULE_NAME;
$method = ACTION_NAME;
$AC = $model . '-' . $method; // Goods-add;
$m = new Model();
// 获取权限
$sql = "select r.role_auth_ac from sw_manager as m left join sw_role as r on m.mg_role_id=r.role_id where m.mg_id = " . $_SESSION['mg_id'];
$SqlAC = $m->query($sql);
$SqlAC = $SqlAC[0]['role_auth_ac'];
if (stripos($SqlAC, $AC) == false) {
$this->error('没有权限访问', U('Index/right'));
exit('没有权限访问');
}
}
}
?>
给角色分配权限
数据模拟权限控制
利用模拟数据进行权限控制
为角色进行具体权限分配(控制,view视图模板,Model接收处理数据
distributeAuth
)
<?php
/**
* 角色管理
*/
class RoleModel extends Model {
public function distributeAuth( $auth, $role_id ) {
$ids = implode(',', $auth);
// 根据$ids查询全部的模块和操作方法
$sql = "select auth_c, auth_a from sw_auth where auth_id in ($ids)";
$info = $this->query($sql);
$ac = "";
foreach($info as $v) {
if ( !empty($v['auth_c']) && !empty($v['auth_a']) ){
$ac .= $v['auth_c'] . '-'. $v['auth_a'] . ',';
}
}
$ac = rtrim($ac, ',');
// 拼凑SQL语句
$sql = "update sw_role set role_auth_ids='$ids', role_auth_ac='$ac' where role_id = " . $role_id;
return $this->execute($sql);
}
}
?>
管理权限
添加权限
控制器:
<?php
/**
* 权限控制器
*/
class AuthAction extends Action {
/**
* 展示
*/
public function showlist() {
// 获得全部权限
$info = D('Auth')->order('auth_path')->select();
$this->assign('info', $info);
$this->display();
}
/**
* 添加权限
*/
public function add() {
if ( !empty($_POST) ) {
$auth = new AuthModel();
$rst = $auth->saveAuth($_POST);
if ( $rst ) {
$this->success('添加权限成功', U('Auth/showlist'));
}
} else {
// 获取全部权限
$info = D('Auth')->select();
$this->assign('info', $info);
foreach( $info as $k => $v ) {
if ($v['auth_level'] == 1) {
$info[$k]['auth_name'] = '-/'. $v['auth_name'];
} else if ( $v['auth_level'] == 2 ) {
$info[$k]['auth_name'] = '-/-/' . $v['auth_name'];
}
}
// 组装 array([]);
$authinfo = array();
foreach($info as $v) {
$authinfo[$v['auth_id']] = $v['auth_name'];
}
$this->assign('authinfo', $authinfo);
$this->display();
}
}
}
?>
模型:
<?php
/**
* 权限模型
*/
class AuthModel extends Model {
/**
* 增加权限
* @param {Array} $info 权限信息
* @return
*/
public function saveAuth($info) {
$auth_c = $info['auth_c'];
$auth_a = $info['auth_a'];
$auth_name = $info['auth_name'];
$auth_pid = $info['auth_pid'];
$auth_id = $this->add($info); // 返回id 值
// auth_path // 全路径
// 如果权限是顶级权限 auth_path === auth_id本身记录id值;
// 如果权限不是顶级权限 auth_path === 父级的auth_id - 本身id
if ( $auth_pid == 0 ) {
$auth_path = $auth_id;
} else {
// 获得父级的auth_path
$p_info = $this->find($auth_pid);
$p_auth_path = $p_info['auth_path'];
$auth_path = $p_auth_path . '-' . $auth_id;
}
// 等级 auth_level
// 根据auth_level处理 10-11-35 查找'-'的个数就是auth_level的值
$auth_level = count(explode('-', $auth_path))-1;
// update
$data = array(
'auth_id' => $auth_id,
'auth_path' => $auth_path,
'auth_level' => $auth_level
);
return $this->save($data);
}
}
?>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。