使用 ThinkPHP5.1快速開發項目的步驟及實例
編寫這篇文章旨在做一個學習筆記。如有錯誤請批評糾正。
一、前期准備工作
1、編程工具的選擇:開發工具:Notepad + 服務器工具:phpStudy(thinkphp5.1運行環境要求PHP5.6+)
2、下載安裝thinkphp5.1
(可參考“ThinkPHP5.1完全開發手冊”下載ThinkPHP5.1:https://www.kancloud.cn/manual/thinkphp5_1/353948)
Composer安裝:在 Windows 中,你需要下載並運行:https://getcomposer.org/Composer-Setup.exe
安裝好composer后,打開win系統的命令行窗口cmd(windows用戶),並執行如下命令:
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
composer create-project topthink/think=5.1.* tp5
這里的tp5目錄名你可以任意更改,執行完畢后,會在當前目錄下的tp5子目錄安裝最新版本的ThinkPHP
二、使用 ThinkPHP5.1快速開發項目的完整步驟:
1、創建數據庫和數據表
2、配置數據庫連接信息
3、創建項目相關文件夾
4、創建控制器類文件
5、創建驗證器類文件(選做,一般都會做)
6、創建模型類文件(選做,簡單的數據庫操作可直接用DB類操作,可不用模型)
7、創建模板視圖文件
8、隱藏入口文件index.php(為了網址簡短)
9、配置路由文件(為了網址簡短)
10、運行應用調試
三、實例
php代碼主要寫在控制器類文件中,html前端代碼主要寫在模板視圖文件中,下面以一個具體實例加以說明:
1、創建數據庫和數據表
可以使用phpstudy中自帶的SQLFront連接mysql並手動建個數據庫,然后用Notepad寫sql腳本並在SQLFront運行可建數據表,例如:
mydb.sql:
DROP TABLE IF EXISTS `blog_article`;
CREATE TABLE `blog_article` (
`id` mediumint(9) NOT NULL AUTO_INCREMENT COMMENT '文章id',
`title` varchar(60) NOT NULL COMMENT '文章標題',
`author` varchar(30) NOT NULL COMMENT '文章作者',
`desc` varchar(255) NOT NULL COMMENT '文章簡介',
`keywords` varchar(255) NOT NULL COMMENT '文章關鍵詞',
`content` text NOT NULL COMMENT '文章內容',
`pic` varchar(100) NOT NULL COMMENT '縮略圖',
`downhttp` varchar(100) DEFAULT NULL COMMENT '下載網址',
`click` int(10) NOT NULL DEFAULT '0' COMMENT '點擊數',
`down` int(11) DEFAULT '0' COMMENT '下載數',
`state` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:不推薦 1:推薦',
`time` int(10) NOT NULL COMMENT '發布時間',
`cateid` mediumint(9) NOT NULL COMMENT '所屬欄目',
`tsort` int(5) DEFAULT '100' COMMENT '文章排序',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;
2、配置數據庫連接信息
打開config文件夾中的database.php進行數據庫連接信息的配置,一般要改動的就是:database(數據庫名稱),username(數據庫用戶名),password(數據庫密碼 ) ,prefix(數據表前綴)
3、創建項目相關文件夾
在application文件夾中創建模塊文件夾,在模塊文件夾下創建控制器類文件夾、模型類文件夾、模板文件夾、驗證器文件夾,例如:
在application文件夾中創建admin(模塊文件夾),在admin文件夾建立以下幾個文件夾:controller(控制器類文件夾,將放php控制器類文件)、model(模型類文件夾,將放php模型類文件)、view(模板文件夾,將放html模板文件)、validate(驗證器文件夾,將放php驗證器文件)
4、創建控制器類文件
在controller文件夾下建個控制器類文件Article.php,寫入控制器的方法:增刪改查等方法,例如:
<?php
namespace app\Admin\controller; //命名空間,即本文件所在位置,該文件位於application\index\controller文件夾中
use think\Controller; //引用控制器類
use think\Db; //引用數據庫操作類
class Article extends Controller //創建一個Controller類的子類Article(繼承父類),類名頭字母要用大寫字母,文件名叫做Article.php,名字對應的叫做Article
{
public function lst() //查,方法名叫 lst
{
$list =Db::name('article')->paginate();//查詢數據表(前綴_article)的數據並且每頁顯示10條數據
$this->assign('list',$list);// 把分頁數據賦值給模板變量list
return $this->fetch();// 渲染模板輸出顯示
}
public function add() //增
{
if(request()->isPost()){//如為post過來的數據
$data=[//要新增的數據,以數組形式
'title'=>input('title'),
'author'=>input('author'),
'desc'=>input('desc'),
'keywords'=>str_replace(',', ',', input('keywords')),
'content'=>input('content'),
'cateid'=>input('cateid'),
'downhttp'=>input('downhttp'),
'tsort'=>input('tsort'),
'time'=>time(),
];
if(input('state')=='on'){
$data['state']=1;
}
if($_FILES['pic']['tmp_name']){//上傳縮略圖圖片文件
$file = request()->file('pic');
$info = $file->validate(['ext'=>'jpg,png,gif'])->move(\think\facade\Env::get('ROOT_PATH') .'public'.DIRECTORY_SEPARATOR .'static'.DIRECTORY_SEPARATOR .'uploads',''); //上傳圖片文件,限制擴展名為jpg,png,gif,DIRECTORY_SEPARATOR表示當前windows系統或linux系統的分隔符(如windows系統為/)
if ($info) {
$data['pic']=DIRECTORY_SEPARATOR .'static'.DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR .$info->getSaveName();
} else {
$this->error($file->getError());
}
}
$validate = new \app\admin\validate\Article; //驗證器類
if(!$validate->scene('add')->check($data)){ //提交到validate文件夾中的驗證器類Article.php的add場景驗證,驗證數據是否合法才增加
$this->error($validate->getError()); die;
}
if(Db::name('Article')->insert($data)){ //執行數據庫Db類的增加方法
$this->redirect("article/lst");
}else{
show_msg('添加文章失敗!');
}
return;
}
$cateres=Db::name('cate')->select();//查詢文章欄目數據表
$this->assign('cateres',$cateres);
return $this->fetch();
}
public function edit() //改
{
$id=input('id');
$articles=Db::name('Article')->find($id);
if(request()->isPost()){
$data=[
'id'=>input('id'),
'title'=>input('title'),
'author'=>input('author'),
'desc'=>input('desc'),
'keywords'=>str_replace(',', ',', input('keywords')),
'content'=>input('content'),
'cateid'=>input('cateid'),
'downhttp'=>input('downhttp'),
'tsort'=>input('tsort'),
];
if(input('state')=='on'){
$data['state']=1;
}else{
$data['state']=0;
}
if($_FILES['pic']['tmp_name']){
$file = request()->file('pic');
$info = $file->validate(['ext'=>'jpg,png,gif'])->move(\think\facade\Env::get('ROOT_PATH') .'public'.DIRECTORY_SEPARATOR .'static'.DIRECTORY_SEPARATOR .'uploads','');
if ($info) {
$data['pic']=DIRECTORY_SEPARATOR .'static'.DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR .$info->getSaveName();
} else {
$this->error($file->getError());
}
}
$validate = new \app\admina\validate\Article;
if(!$validate->scene('edit')->check($data)){ //提交到validate文件夾中的驗證器類Article.php的edit場景驗證,驗證數據是否合法才更新
$this->error($validate->getError()); die;
}
$save=Db::name('Article')->update($data); //執行數據庫Db類的更新方法
if($save !== false){
$this->redirect("article/lst");
}else{
show_msg('修改文章失敗!');
}
return;
}
$this->assign('articles',$articles);
$cateres=Db::name('cate')->select();
$this->assign('cateres',$cateres);
return $this->fetch();
}
public function del() //刪
{
$id=input('id');
if(Db::name('Article')->delete(input('id'))){ //執行數據庫Db類的刪除方法
$this->redirect("article/lst");
}else{
show_msg('刪除文章失敗!');
}
}
}
5、創建驗證器類文件(選做,一般都會做)
在validate文件夾下建個驗證器類文件Article.php,寫入增加或更新數據時的驗證,例如:
<?php
namespace app\admin\validate;
use think\Validate; //引用驗證器類
class Article extends Validate //創建一個Validate類的子類Article(繼承父類),類名頭字母要用大寫字母
{
//規則
protected $rule = [
'title' => 'require|max:100',
'cateid' => 'require',
];
//提示信息
protected $message = [
'title.require' => '文章標題必須填寫',
'title.max' => '文章標題長度不得大於100位',
'cateid.require' => '請選擇文章所屬欄目',
];
//場景(增加和修改)
protected $scene = [
'add' => ['title','cateid'],
'edit' => ['title','cateid'],
];
}
6、創建模型類文件(選做,簡單的數據庫操作可直接用DB類操作,可不用模型)
7、創建模板視圖文件
在view文件夾下建立article文件夾(存放Article.php控制器類對應的增改查方法的前端html文件,html文件名要與方法名一致),在article文件夾下建立lst.html,add.html,edit.html模板文件,例如:
lst.html:
<!DOCTYPE html>
<html>
<head>
<title>后台管理</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/bootstrap-4.1.3/css/bootstrap.min.css">
</head>
<body>
<div class="row">
<div class="col-lg-12 col-sm-12 col-xs-12">
<table class="table table-bordered table-hover">
<thead class="table-info">
<tr>
<th class="text-center" width="4%">ID</th>
<th class="text-center">文章標題</th>
<th class="text-center">文章作者</th>
<th class="text-center">是否推薦</th>
<th class="text-center">縮略圖</th>
<th class="text-center">所屬欄目</th>
<th class="text-center">文章排序</th>
<th class="text-center">點擊數</th>
<th class="text-center">下載數</th>
<th class="text-center" width="14%">操作</th>
</tr>
</thead>
<tbody>
{volist name="list" id="vo"}
<tr>
<td align="center">{$vo.id}</td>
<td align="center">{$vo.title}</td>
<td align="center">{$vo.author}</td>
<td align="center">
{if condition="$vo['state'] eq 1 "}
已推薦
{else /}
未推薦
{/if}
</td>
<td align="center">
{if condition="$vo['pic'] neq '' "}
<img src="{$vo.pic}" height="50">
{else /}
暫無縮略圖
{/if}
</td>
<th class="text-center">{$vo.cate.catename}</th>
<td align="center">{$vo.tsort}</td>
<td align="center">{$vo.click}</td>
<td align="center">{$vo.down}</td>
<td align="center">
<a href="{:url('article/edit',array('id'=>$vo['id']))}" class="btn btn-primary">
<i class="fa fa-edit"></i> 編輯
</a>
<a href="#" onClick="warning('確實要刪除嗎', '{:url('article/del',array('id'=>$vo['id']))}')" class="btn btn-danger">
<i class="fa fa-trash-o"></i> 刪除
</a>
</td>
</tr>
{/volist}
</tbody>
</table>
</div>
</div>
<div class="row justify-content-center"> {$list|raw}</div>
</body>
</html>
add.html:
<!DOCTYPE html>
<html>
<head>
<title>后台管理</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/bootstrap-4.1.3/css/bootstrap.min.css">
</head>
<body>
<div class="row">
<div class="col-lg-12 col-sm-12 col-xs-12">
<div class="p-1 mb-2 bg-success text-white">新增文章</div>
<hr>
<form role="form" action="" enctype="multipart/form-data" method="post">
<div class="form-group row">
<label for="title" class="col-sm-2 col-form-label text-right">文章標題</label>
<div class="col-sm-6">
<input class="form-control" id="title" placeholder="" name="title" type="text" required>
</div>
<p class="col-sm-4 text-danger">* 必填</p>
</div>
<div class="form-group row">
<label for="author" class="col-sm-2 col-form-label text-right">文章作者</label>
<div class="col-sm-6">
<input class="form-control" id="author" placeholder="" name="author" type="text">
</div>
</div>
<div class="form-group row">
<label for="keywords" class="col-sm-2 col-form-label text-right">關鍵字</label>
<div class="col-sm-6">
<input class="form-control" id="keywords" placeholder="" name="keywords" type="text">
</div>
</div>
<div class="form-group row">
<label for="desc" class="col-sm-2 col-form-label text-right">文章描述</label>
<div class="col-sm-6">
<textarea name="desc" class="form-control" id="desc"></textarea>
</div>
</div>
<div class="form-group row">
<label for="pic" class="col-sm-2 col-form-label text-right">縮略圖</label>
<div class="col-sm-6">
<input id="pic" placeholder="" name="pic" type="file" accept=".jpg, .gif, .png">
</div>
</div>
<div class="form-group row">
<label for="downhttp" class="col-sm-2 col-form-label text-right">下載網址</label>
<div class="col-sm-6">
<input class="form-control" id="downhttp" placeholder="" name="downhttp" type="text">
</div>
</div>
<div class="form-group row">
<label for="cateid" class="col-sm-2 col-form-label text-right">所屬欄目</label>
<div class="col-sm-2">
<select class="form-control" name="cateid" id="cateid" required>
<option value="">請選擇欄目</option>
{volist name="cateres" id="vo"}
<option value="{$vo.id}">{$vo.catename}</option>
{/volist}
</select>
</div>
<p class="col-sm-4 text-danger">* 必填</p>
</div>
<div class="form-group row">
<div class="col-sm-2 text-right">是否推薦</div>
<div class="col-sm-6">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" name="state" id="state">
<label class="custom-control-label" for="state">推薦</label>
</div>
</div>
</div>
<div class="form-group row">
<label for="tsort" class="col-sm-2 col-form-label text-right">文章排序</label>
<div class="col-sm-6">
<input class="form-control" id="tsort" placeholder="" name="tsort" type="text">
</div>
</div>
<div class="form-group row">
<label for="content" class="col-sm-2 col-form-label text-right">文章內容</label>
<div class="col-sm-6">
<label>
<textarea name="content" id="content"></textarea>
</label>
</div>
</div>
<div class="form-group">
<div class="offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">保存信息</button>
</div>
</div>
</form>
</div>
</div>
</body>
</html>
edit.html:
<!DOCTYPE html>
<html>
<head>
<title>后台管理</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/bootstrap-4.1.3/css/bootstrap.min.css">
</head>
<body>
<div class="row">
<div class="col-lg-12 col-sm-12 col-xs-12">
<div class="p-1 mb-2 bg-success text-white">修改文章</div>
<hr>
<form role="form" action="" enctype="multipart/form-data" method="post">
<input type="hidden" name="id" value="{$articles.id}">
<div class="form-group row">
<label for="title" class="col-sm-2 col-form-label text-right">文章標題</label>
<div class="col-sm-6">
<input class="form-control" id="title" placeholder="" name="title" value="{$articles.title}" type="text" required>
</div>
<p class="col-sm-4 text-danger">* 必填</p>
</div>
<div class="form-group row">
<label for="author" class="col-sm-2 col-form-label text-right">文章作者</label>
<div class="col-sm-6">
<input class="form-control" id="author" placeholder="" name="author" value="{$articles.author}" type="text">
</div>
</div>
<div class="form-group row">
<label for="keywords" class="col-sm-2 col-form-label text-right">關鍵字</label>
<div class="col-sm-6">
<input class="form-control" id="keywords" placeholder="" name="keywords" value="{$articles.keywords}" type="text">
</div>
</div>
<div class="form-group row">
<label for="desc" class="col-sm-2 col-form-label text-right">文章描述</label>
<div class="col-sm-6">
<textarea name="desc" class="form-control" id="desc">{$articles.desc}</textarea>
</div>
</div>
<div class="form-group row">
<label for="pic" class="col-sm-2 col-form-label text-right">縮略圖</label>
<div class="col-sm-6">
<input id="pic" placeholder="" name="pic" style="display:inline;" type="file" accept=".jpg, .gif, .png">
{if condition="$articles['pic'] neq ''"}
<img src="{$articles.pic}" height="50">
{else /}
<span>暫無縮略圖</span>
{/if}
</div>
</div>
<div class="form-group row">
<label for="downhttp" class="col-sm-2 col-form-label text-right">下載網址</label>
<div class="col-sm-6">
<input class="form-control" id="downhttp" placeholder="" name="downhttp" value="{$articles.downhttp}" type="text">
</div>
</div>
<div class="form-group row">
<label for="cateid" class="col-sm-2 col-form-label text-right">所屬欄目</label>
<div class="col-sm-2">
<select class="form-control" name="cateid" id="cateid" required>
<option value="">請選擇欄目</option>
{volist name="cateres" id="vo"}
<option {if condition="$vo['id'] eq $articles['cateid']"}selected="selected"{/if} value="{$vo.id}">{$vo.catename}</option>
{/volist}
</select>
</div>
<p class="col-sm-4 text-danger">* 必填</p>
</div>
<div class="form-group row">
<div class="col-sm-2 text-right">是否推薦</div>
<div class="col-sm-6">
<div class="custom-control custom-checkbox">
<input {if condition="$articles['state'] eq 1"}checked="checked"{/if} type="checkbox" class="custom-control-input" name="state" id="state">
<label class="custom-control-label" for="state">推薦</label>
</div>
</div>
</div>
<div class="form-group row">
<label for="tsort" class="col-sm-2 col-form-label text-right">文章排序</label>
<div class="col-sm-6">
<input class="form-control" id="tsort" placeholder="" name="tsort" value="{$articles.tsort}" type="text">
</div>
</div>
<div class="form-group row">
<label for="content" class="col-sm-2 col-form-label text-right">文章內容</label>
<div class="col-sm-6">
<label>
<textarea name="content" id="content">{$articles.content}</textarea>
</label>
</div>
</div>
<div class="form-group">
<div class="offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">保存信息</button>
</div>
</div>
</form>
</div>
</div>
</body>
</html>
8、隱藏入口文件index.php(為了網址簡短)
以 Apache 為例,需要在入口文件的同級添加 .htaccess 文件(官方默認自帶了該文件):
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]//都是差別在這一句
</IfModule>
如果用的 phpstudy ,規則如下:
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [L,E=PATH_INFO:$1]
</IfModule>
如果你使用的 apache 版本使用上面的方式無法正常隱藏 index.php ,可以嘗試使用下面的方式配置 .htaccess 文件:
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?/$1 [QSA,PT,L]
</IfModule>
如果是 Nginx 環境的話,可以在 Nginx.conf 中添加:
(找到nginx的配置文件 nginx.conf, 在里面的 server{ } 里增加以下內容)
location / {//…..省略部分代碼
if (!-e $request_filename){
rewrite ^(.*)$ /index.php?s=/$1 last;
break;} }
thinkphp5 IIS7.5 隱藏index.php的方法:
web.config文件里:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="WPurls" enabled="true" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.php/{R:0}" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
根據不同的空間,請將上面的偽靜態規則,保存為.htaccesss文件或Httpd.ini或web.config文件,並放到ThinkPHP項目入口文件同級目錄下。
附:偽靜態配置
Apache Rewrite的配置:Apache下的Rewrite配置主要有兩種,一種是針對整個apache服務器的配置,此種配置的Rewrite規則是直接在httpd.conf下書寫。另一種是針對apache服務器下的某一目錄的配置,此種配置的Rewrite規則需在此目錄下建立一個.htaccess文件來書寫。
IIS 服務器:web.config 主要應用在iis7/iis7.5的服務器上,httpd.ini 主要應用在iis以及iis6 的服務器上,跟.htaccess的規則比較接近。
NGINX服務器:nginx里使用偽靜態是直接在配置文件nginx.conf 中寫規則的,在里面的 server{ } 中寫需要的規則即可。
9、配置路由文件(為了網址簡短)
打開route文件夾中的route.php,配置如下:
Route::rule('/', 'admin/article/lst');//相當於用http://127.0.0.1/ 則訪問到admin模塊下的article控制器的lst方法
Route::rule('add', 'admin/article/add');//相當於用http://127.0.0.1/add 則訪問到admin模塊下的article控制器的add方法
Route::rule('edit', 'admin/article/edit');
10. 運行應用調試
為了在開發過程中調試,可以打開config文件夾中database.php中的'debug'=>true(數據庫調試模式)和app.php中的'app_debug' =>true( 應用調試模式)、'app_trace'=>true(應用跟蹤Trace調試)
我們在瀏覽器里面輸入:http://localhost/ 就可以看到頁面的輸出了
由於我們開啟了調試模式,所以在頁面的最下面還會看到一些額外的調試信息,並且可以很清楚的看到當前頁面的請求信息和執行時間、 SQL 日志,最后還有加載的文件列表,事實上,頁面 Trace 信息的顯示完全是可以定制的,而這些內容不需要在模板里面定義。在 ThinkPHP 中,我們稱之為頁面 Trace 信息,這是為了在開發過程中調試用的,關閉調試模式后,這些信息會自動消失。另外在調試模式下面,由於開啟了日志記錄,並且關閉了所有緩存,所以執行效率會有一定影響,但是關閉調試模式后,效率會有非常顯著的提高。
到目前為止,我們已經完成了一個完整的數據操作應用了。