ThinkPHP 3.2.3 簡單背景子產品開發(一)常用配置
一、項目分組
下載下傳解壓 ThinkPHP 3.2.3,在預設的應用 Application(./Application) 中,包含一個預設的子產品 Home(./Application/Home)。
需要在該預設應用中建立一個用于背景管理的 Admin 子產品,可以通過在應用入口檔案(./index.php)中綁定 Admin 子產品來 自動生成 Admin 子產品:
define(‘BIND_MODULE‘,‘Admin‘);
此時通路 http://serverName/index.php 便會自動在 ./Application 下建立 Admin 目錄(要記得把上面的定義 删掉,否則通過入口檔案通路網站首頁就會預設通路 Admin 子產品)。
不需要修改入口檔案。
此時通路 http://serverName/index.php/Admin 就可以通路背景的 Index 控制器的 index 方法了。
目錄結構如下:

二、應用配置和子產品配置
應用配置(公共配置檔案)位于 ./Application/Common/Conf/config.php ,在 ThinkPHP 調用所有的子產品之前加載。
在這裡沒有做特别的配置,隻開啟了 Trace 追蹤資訊:
<?php
return array(
//‘配置項‘=>‘配置值‘
//顯示頁面 Trace 資訊
‘SHOW_PAGE_TRACE‘ => true,
);
Admin 子產品的子產品配置位于 ./Application/Admin/Common/Conf/config.php
該項目開啟的配置包括:
① 資料庫
//資料庫配置資訊
‘DB_TYPE‘ => ‘mysql‘, // 資料庫類型
‘DB_HOST‘ => ‘127.0.0.1‘, // 伺服器位址
‘DB_NAME‘ => ‘tptest‘, // 資料庫名
‘DB_USER‘ => ‘root‘, // 使用者名
‘DB_PWD‘ => ‘‘, // 密碼
‘DB_PORT‘ => 3306, // 端口
‘DB_PARAMS‘ => array(), // 資料庫連接配接參數
‘DB_PREFIX‘ => ‘crm_‘, // 資料庫表字首
‘DB_CHARSET‘=> ‘utf8‘, // 字元集
‘DB_DEBUG‘ => TRUE, // 資料庫調試模式 開啟後可以記錄SQL日志
② 配置背景公共檔案
由于該項目同時包含 Home、Admin 子產品,是以公共檔案必須分開,是以可以把 Admin 子產品的公共檔案放在 ./Application/Public/Admin 下,在 Admin 的子產品配置檔案中配置:
//背景公共檔案路徑
‘TMPL_PARSE_STRING‘ => array(
‘__PUBLIC__‘ => __ROOT__.‘/Public/Admin‘
),
③ 定義異常頁面
//異常頁面
‘TMPL_EXCEPTION_FILE‘ => ‘./Public/Admin/error.html‘,
異常頁面 error.html 中錯誤資訊可以用 $e[‘message‘] 表示
如果需要有倒計時跳轉的功能,可以參考 ./ThinkPHP/Tpl/dispatch_jump.tpl 檔案
error.html:
<!DOCTYPE html>
<html>
<head>
<title>error</title>
<meta charset="UTF-8">
</head>
<body>
<?php echo $e[‘message‘];?><br />
<b id="wait">5</b>秒後跳轉回首頁 或 點選<a id="href" href="/Admin/Index/index" target="_blank" rel="external nofollow" >傳回首頁</a>
<script>
(function(){
var wait = document.getElementById(‘wait‘),href = document.getElementById(‘href‘).href;
var interval = setInterval(function(){
var time = --wait.innerHTML;
if(time <= 0) {
location.href = href;
clearInterval(interval);
};
}, 1000);
})();
</script>
</body>
</html>
例如通路了不存在的方法,頁面會輸出:
無法加載控制器:Login2
3秒後跳轉回首頁 或 點選傳回首頁
然後跳轉回背景首頁。
注:該頁面中不能使用 ThinkPHP 的标簽,隻能使用原生的 PHP 語句。
三、管理者登陸
用于登陸的檔案是 Admin 子產品下的 LoginController.class.php
<?php
namespace Admin\Controller;
use Think\Controller;
class LoginController extends Controller{
public function index() {
$this->display();
}
//驗證碼
public function verify() {
$conf = array(
//‘useZh‘=>true,//使用中文
‘fontSize‘=>20,
‘length‘=>1,
‘imageW‘=>100,//驗證碼寬度
);
$Verify = new \Think\Verify($conf);
$Verify->entry();
}
public function login() {
if(!IS_POST) {
e(‘非法登陸‘);
}
//檢驗驗證碼
if(!check_verify(I(‘post.code‘))) {
$this->error(‘驗證碼錯誤‘);
}
$username = I(‘post.username‘, ‘‘);
$user = M(‘user‘)->where(array(‘username‘=>$username))->find();
if(!$user || md5(I(‘post.password‘)) != $user[‘password‘]) {
$this->error(‘使用者名或密碼錯誤‘);
}
if($user[‘lock‘]) $this->error(‘使用者被鎖定‘);
//更新使用者表
$data = array(
‘id‘=>$user[‘id‘],
‘logintime‘=>time(),
‘loginip‘=> get_client_ip()
);
M(‘user‘)->save($data);
//儲存session
session("user.uid", $user[‘id‘]);
session("user.username", $user[‘username‘]);
session("user.logintime", date(‘Y-m-d H:i:s‘, $user[‘logintime‘]));
session("user.loginip", $user[‘loginip‘]);
//成功跳轉
$this->redirect(‘Admin/Index/index‘);
}
}
幾點說明:
① IS_POST
位于 ./ThinkPHP/Library/Think/App.class.php(ThinkPHP 應用程式類 執行應用程式管理) 的 static public function init() 方法(應用程式初始化)内
定義目前請求的系統常量,源碼:
define(‘REQUEST_METHOD‘, $_SERVER[‘REQUEST_METHOD‘]);
define(‘IS_POST‘, REQUEST_METHOD ==‘POST‘ ? true : false);
② e 方法
位于 ThinkPHP/Common/functions.php (Think 系統函數庫)
/**
* 抛出異常處理
* @param string $msg 異常消息
* @param integer $code 異常代碼 預設為0
* @throws Think\Exception
* @return void
*/
function E($msg, $code=0) {
throw new Think\Exception($msg, $code);
}
③ 模闆路徑
預設情況下,Admin 子產品 Login 控制器的 index 方法對應的模闆檔案應該是 ./Application/Admin/View/Login/index.html
如果不希望目錄層級太多的話,可以将該方法對應的模闆檔案設定為 ./Application/Admin/View/Login_index.html,減少了一層目錄,在 Admin 子產品的子產品配置 config.php 中添加:
//模版路徑
‘TMPL_FILE_DEPR‘ => ‘_‘,
登陸以後進入背景首頁 http://serverName/Admin/Index/index
Admin 子產品的 Index 控制器 ./Application/Admin/Controller/IndexController.class.php:
<?php
namespace Admin\Controller;
class IndexController extends CommonController {
public function index(){
$this->display();
}
//推出登陸
public function loginout() {
session_unset();
session_destroy();
$this->redirect(‘Admin/Login/index‘);
}
}
注:要進入背景首頁,必須經過登陸,是以在加載該控制器之前應該先檢查使用者是否登陸,檢查的方法可以寫在 Admin 子產品的 Common 控制器中,然後 Index 控制器繼承 Common 控制。Common 控制器位于 ./Application/Admin/Controller/CommonController.class.php:
<?php
namespace Admin\Controller;
use Think\Controller;
class CommonController extends Controller{
public function _initialize() {
if(!isset($_SESSION[‘user‘][‘uid‘]) || !isset($_SESSION[‘user‘][‘username‘])) {
$this->redirect(‘Admin/Login/index‘);
}
}
}
注:在 ./ThinkPHP/Library/Think/Controller.class.php (ThinkPHP 控制器基類)定義了
/**
* 架構函數 取得模闆對象執行個體
* @access public
*/
public function __construct() {
Hook::listen(‘action_begin‘,$this->config);
//執行個體化視圖類
$this->view = Think::instance(‘Think\View‘);
//控制器初始化
if(method_exists($this,‘_initialize‘))
$this->_initialize();
}
登陸子產品的視圖檔案位于 ./Application/Admin/View/Login_index.html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="__PUBLIC__/Css/login.css" />
<meta charset="utf-8">
<script src="__PUBLIC__/Js/jquery-1.7.2.min.js"></script>
<script src="__PUBLIC__/Js/login.js"></script>
</head>
<body>
<div id="top">
</div>
<div class="login">
<form action="{:U(‘Admin/Login/login‘)}" method="post" id="login">
<div class="title">
登入背景
</div>
<table border="1" width="100%">
<tr>
<th>管理者帳号:</th>
<td>
<input type="username" name="username" class="len250"/>
</td>
</tr>
<tr>
<th>密碼:</th>
<td>
<input type="password" class="len250" name="password"/>
</td>
</tr>
<tr>
<th>驗證碼:</th>
<td>
<input type="code" class="len250" name="code"/> <img src="{:U(‘Admin/Login/verify‘,‘‘,‘‘)}" id="code"/> <a href="javascript:void(change_code(this));">看不清</a>
</td>
</tr>
<tr>
<td colspan="2" style="padding-left:160px;"> <input type="submit" class="submit" value="登入"/></td>
</tr>
</table>
</form>
</div>
</body>
</html>
注:
① U 方法
在控制器使用 U 方法的格式是 U(子產品/控制器/方法, array(‘參數1‘=>‘參數1的值‘,‘參數2‘=>‘參數2的值‘, ‘僞靜态字尾‘))
在模闆中使用 U 方法的格式是 {:U(子產品/控制器/方法, array(‘參數1‘=>‘參數1的值‘,‘參數2‘=>‘參數2的值‘, ‘僞靜态字尾‘))}
② 僞靜态字尾
可以在子產品配置檔案 config.php 中配置僞靜态字尾,預設為 .html,即 URL 可能是 http://serverName/Admin/Index/index.html。可以設定為空,即 URL 可能為 http://serverName/Admin/Index/index
//僞靜态字尾
‘URL_HTML_SUFFIX‘=>‘‘,
Admin 子產品的檔案目錄:
crm_user 表:
CREATE TABLE `crm_user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` char(20) NOT NULL DEFAULT ‘‘,
`password` char(32) NOT NULL DEFAULT ‘‘,
`logintime` int(10) unsigned NOT NULL,
`loginip` varchar(30) NOT NULL,
`lock` tinyint(1) unsigned NOT NULL DEFAULT ‘0‘,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
四、自定義 Session 存儲
① 把 Session 存儲在 MySQL 資料庫中
ThinkPHP 3.2.3 自帶了 Db 類型的 Session 驅動,Db 類位于 ./ThinkPHP/Library/Think/Session/Driver/Db.class.php
架構已經準備好了 session 表:
CREATE TABLE think_session (
session_id varchar(255) NOT NULL,
session_expire int(11) NOT NULL,
session_data blob,
UNIQUE KEY `session_id` (`session_id`)
);
修改子產品配置 ./Application/Admin/Common/Conf/config.php,增加以下配置即可:
‘SESSION_TYPE‘=>‘Db‘,
② 把 Session 存儲在 Redeis 中
ThinkPHP 3.2.3 自帶了 Memcache 驅動,可以參考該驅動來開發 Redis 驅動,建立 Redis.class.php,放在 ./ThinkPHP/Library/Think/Session/Driver 下:
<?php
// +---------------------------------------------------------------------
// | Date:2016/01/09
// +----------------------------------------------------------------------
namespace Think\Session\Driver;
class Redis {
//儲存Redis連接配接對象
protected $redis;
//Session有效時間
private $expire;
/**
* 打開Session
* @access public
* @param string $savePath
* @param mixed $sessName
*/
public function open($savePath, $sessName) {
$this->expire = C(‘SESSION_EXPIRE‘) ? C(‘SESSION_EXPIRE‘) : ini_get(‘session.gc_maxlifetime‘);
$this->redis = new Redis();
return $this->redis->connect(C(‘REDIS_HOST‘), C(‘REDIS_PORT‘));
}
/**
* 關閉Session
* @access public
*/
public function close() {
return $this->redis->close();
}
/**
* 讀取Session
* @access public
* @param string $sessID
*/
public function read($sessID) {
$sessID = C(‘SESSION_PREFIX‘).$sessID;
$sessData = $this->redis->get($sessID);
return $sessID ? $sessID : ‘‘;
}
/**
* 寫入Session
* @access public
* @param string $sessID
* @param String $sessData
*/
public function write($sessID, $sessData) {
$sessID = C(‘SESSION_PREFIX‘).$sessID;
return $this->redis->set($sessID, $sessData, $this->expire);
}
/**
* 删除Session
* @access public
* @param string $sessID
*/
public function destroy($sessID) {
$sessID = C(‘SESSION_PREFIX‘).$sessID;
return $this->redis->delete($sessID);
}
/**
* Session 垃圾回收
* @access public
* @param string $sessMaxLifeTime
*/
public function gc($sessMaxLifeTime) {
return true;
}
}
然後修改子產品配置檔案 config.php:
‘SESSION_TYPE‘=>‘Redis‘,
//Redeis伺服器位址
‘REDIS_HOST‘=>‘127.0.0.1‘,
//Redis端口号
‘REDIS_PORT‘=>‘6379‘,
//Session字首
‘SESSION_PREFIX‘=>‘session_‘,
//Session有效時間
‘SESSION_EXPIRE‘=>3600
五、文章管理
文章管理的控制器 ArticleManageController.class.php:
<?php
/*
* 文章管理類
* date:2016/01/10
*/
namespace Admin\Controller;
use Think\Page;
class ArticleManageController extends CommonController{
public function index() {
$count = M(‘article‘)->count();
$page = new Page($count, 3);
$limit = $page->firstRow.‘,‘.$page->listRows;
$show = $page->show();
$list = M(‘article‘)->limit($limit)->select();
$this->assign(‘list‘, $list);
$this->assign(‘show‘, $show);
$this->display();
}
}
注:ThinkPHP 3.2.3 的分頁類位于 ./ThinkPHP/Library/Think/Page.class.php,是以除了使用 use 關鍵字引入命名空間為 Think 的 Page類,然後直接執行個體化 Page類外,還可以在需要執行個體化 Page 類的時候 new \Think\Page(); Think 前面的 \ 代表根命名空間。
在使用分頁類的時候隻需要把總條數和每頁配置設定的條數作為參數傳給 Page 類以擷取 limit 參數。
文章管理的視圖檔案位于 ./Application/Admin/View/ArticleManage_index.html:
<!DOCTYPE html>
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<css file=‘__PUBLIC__/Css/public.css‘/>
</head>
<body>
<table class=‘table‘>
<tr>
<th>序号</th>
<th>标題</th>
<th>釋出人</th>
<th>内容概述</th>
<th>釋出時間</th>
<th>操作</th>
</tr>
<volist name=‘list‘ id=‘vo‘>
<tr>
<td>{$vo.id}</td>
<td>{$vo.title}</td>
<td>{$vo.creat_id}</td>
<td>{$vo.content}</td>
<td>{$vo.addtime|date="Y-m-d H:i", ###}</td>
<td>
<a href="#">删除</a>
</td>
</tr>
</volist>
<tr>
<td colspan="6" align="center">{$show}</td>
</tr>
</table>
</body>
</html>
注:模闆中使用了點文法解析數組,點文法在模闆中除了解析數組外,還可以解析對象。可以在子產品配置中設定點文法隻解析數組,可以使模闆解析速度更快:
//點文法預設解析
‘TMPL_VAR_IDENTIFY‘ => ‘array‘,