目錄
介紹
安裝(windows環境)
安裝Composer
安裝ThinkPHP
目錄結構
配置文件
第一個接口(Controller層)
Hello World
?自定義Controller
請求參數
獲取查詢參數(Get請求)
獲取指定請求參數?
獲取所有URL的請求參數
獲取json數據(Post)
獲取JSON數據中的屬性
一些問題
響應參數
路由
基礎路由映射
?路由寫法解析
模型(Model)
模型的定義
模型使用測試
模型的設置
設置表名
?設置主鍵
模型字段的設置
廢棄字段
訪問數據庫?
數據庫的連接
增刪改查(CURD)
新增
刪除?
更新/修改
查詢
查詢單條
獲取多條數據
模糊查詢
?多條件
?排序
聚合函數的使用
分頁查詢
原生查詢
部署
部署前準備?
部署方式一:內置服務器直接啟動??
部署方式二:使用Nginx服務器
最后
介紹
?官方介紹?
ThinkPHP
是一個免費開源的,快速、簡單的面向對象的輕量級PHP開發框架,是為了敏捷WEB應用開發和簡化企業應用開發而誕生的。
ThinkPHP
誕生十七年來一直秉承簡潔實用的設計原則,在保持出色的性能和至簡代碼的同時,更注重易用性。
遵循Apache2
開源許可協議發布,意味著你可以免費使用ThinkPHP
,甚至允許把你基于ThinkPHP開發的應用開源或商業產品發布/銷售。
本文章主要是針對已有其他后端開發框架基礎的人群,如SpringBoot(Java),Express(NodeJS)?等。
這樣,閱讀本文章你就可以快速了解ThinkPHP框架如何ORM的與數據庫做交互,提供訪問接口。?
?ThinkPHP為MVC架構,但本文章不說明視圖層(V),使用接口測試工具apifox來測試ThinkPHP創造出的接口。?
安裝(windows環境)
安裝Composer
我們安裝ThinkPHP之前,要先去安裝Composer。
為什么要安裝composer?
因為ThinkPHP的下載需要通過Composer來進行
Composer是一個PHP的依賴管理工具,可以用于管理項目中的PHP庫和包的依賴關系。
類似于NodeJS中的npm包管理器,也類似前端腳手架構建vue項目的Vite。
?安裝步驟?
1. 下載Composer安裝包
訪問官方下載地址:Composer下載https://doc.thinkphp.cn/v8_0/setup.html
2. 執行下載的.exe安裝引導程序,無腦下一步。
其中需要注意的勾選:
3. 檢查安裝結果:命令行輸入composer。
看見輸出就好了。
安裝ThinkPHP
安裝好了composer之后,我們就能使用composer"腳手架"來創建ThinkPHP基礎項目了。?
使用腳手架去構建TP(ThinkPHP)基本項目需要下載代碼、依賴包等文件,而Composer的下載服務器在國外,直接下載會很慢,因此我們可以配置一下國內的鏡像源。
1. 安裝前配置國內鏡像:
命令:composer config -g repo.packagist composer 鏡像源地址
阿里云:?https://mirrors.aliyun.com/composer/?
華為云:?https://repo.huaweicloud.com/repository/php/
例如執行命令: 配置阿里國內鏡像源
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
2. 切換到你PHP網站根目錄(www目錄)下執行安裝命令:
composer create-project topthink/think tp6
命令是創建一個ThinkPHP項目。(tp6為根目錄名稱,可自定義)
這個操作類似于前端我們使用vite腳手架構建vue項目類似 。
3. 運行測試
cd進入剛創建的tp6目錄,執行運行命令:php think run
看到輸出信息:項目運行在了“內置服務器”的8000端口。
通過訪問URL:localhost:8000 即可訪問項目首頁。
?
這樣我們的ThinkPHP基本項目的構造就完成了。?
上面提到了“內置服務器”,是基于?PHP 內置的 CLI 開發服務器。
在本地開發調試的時候我們直接使用內置服務器是可行的。
但是在部署到生產環境的時候,建議還是要部署到Apache或Nginx上。
目錄結構
上面說了Composer將像是一個腳手架幫我們去構建特定結構的ThinkPHP項目。
那么接下來我們就可以看看生成的這個項目結構長啥樣。
目錄還是不少,下面我會說明幾個較為常用的說明:
1. app目錄:是應用目錄,我們就在這個目錄進行編碼,例如接口的編寫。
2. config是配置目錄,里面存放著各類配置文件,如數據庫的配置。
3. public是公共目錄,對外訪問目錄,像上面我們通過訪問localhost:8000顯示的頁面就是這個目錄中的index.php文件。
4. route是定義路由的目錄,就是定義我們的接口URL以什么樣子暴露出去讓前端進行調用。
?更詳細的目錄和文件說明,我們可以訪問官方文檔進行查看:ThinkPHP目錄結構
配置文件
腳手架構建的這個基礎項目中的配置文件很多,目前我們可以就關注一個
就是“database.php”,顧名思義就是數據庫連接的配置。
我們展開/config目錄看看其中的各種配置文件。
總之,后續我們要配置一些參數,如數據庫的連接參數,我們就到/config下找就好。
第一個接口(Controller層)
接下來,我們進行正題吧。
讓我們一起來寫出ThinkPHP的第一個"Hello World"接口 。
Hello World
打開文件controller/Index.php
<?php
namespace app\controller;use app\BaseController;class Index extends BaseController
{// 輸出hello worldpublic function hi(){return 'hello world';}}
php think run 啟動項目,apifox訪問接口localhost:8000/index/hi
可以看到,我們通過訪問方式為:類名/方法名?
通過上面實踐,我們就能知道一種接口訪問的方式(后面會講到路由訪問)。
?自定義Controller
值得注意的是:如果我們要在controller目錄下新建新的Controller,如UserController,需要文件名和類名保持一致。
例如,我們下面創建自定義User控制器,專門用來處理用戶數據相關的請求。
BaseController?
我們通過繼承BaseController (基礎控制器)使得我們自定義的控制器功能得到擴展。
例如繼承后我們就能通過$this去獲取當前請求的一些參數。
我們可以看看BaseController中的內容:
請求參數
這里說的是如何獲取前端調用接口時傳入的請求參數。?
下面說明獲取:查詢參數,對象參數,至于"路徑參數"在下面的路由章節會提到。
獲取查詢參數(Get請求)
獲取參數的方式有很多種,下面介紹兩種:1.Request對象獲取? 2.助手函數獲取
獲取指定請求參數?
訪問:/UserController/test1?name=tom
?接口代碼:
use think\facade\Request; // 引入Requestpublic function test1(){// 方法一:使用 Request 類獲取請求參數halt(Request::param("name"));// 方法二:助手函數獲取halt(input("name"));
}
halt() 輸出顯示:
獲取所有URL的請求參數
當我們同時傳遞多個參數的情況
訪問:/UserController/test1?name=tom&age=18
接口代碼:
public function test1(){// 方法一:使用 Request 類獲取請求參數halt(Request::param());// 方法二:助手函數獲取請求參數halt(input("param."));
}
halt()輸出
獲取json數據(Post)
?獲取JSON數據?
我們通過apifox模擬發送一段json數據(post請求)
接口代碼:
use think\facade\Request;public function index(){// 方法一:使用 Request 類獲取請求參數halt(Request::param());// 方法二:助手函數獲取請求參數halt(input("param."));
}
獲取JSON數據中的屬性
例如我們想要獲取傳入json對象中的name屬性值,可以這樣:
public function index(){// 方法一:使用 Request 類獲取請求參數halt(Request::param()["name"]);// 方法二:助手函數獲取請求參數halt(input("param.name"));
}
一些問題
?
在上面獲取傳入json數據的時候其實接口可讀性是有點差的,因為我們沒有將接口該傳入的參數在方法上體現出來。
我想說的是:
// 顯示聲明name 和 age
public function index(String name, int age){// 函數體
}// 顯示接收用戶信息Json數據
public function addUser(User user){// 函數體
}
例如上面的index()方法其中就顯示的定義調用該接口需要的參數,以及addUser()方法中需要User用戶類型的一個對象參數。
我想這樣的定義方式有SpringBoot使用經驗的并不陌生。
這樣的可讀性是很好的,雖然規范性強了些。
?關于對象類型的參數說明:
在上面的addUser方法中我們定義了(User user)作為參數,表示我們想要接收一個User用戶類型的對象/json數據。
但是需要知道ThinkPHP中并不像SpringBoot那樣會自動將json對象轉為我們的PHP對象,所以,如果我們以上面的方式去定義參數,我們需要手動的先將json數據轉為對象。
所以就推薦直接使用請求對象Request::param() 或助手函數input("param.")去獲取傳入的參數好了,不必去那么嚴格的定義接口參數的定義。
如果就是想要定義對象參數的自動轉換,可以去了解“封裝中間件或者創建自定義請求處理器”,或者考慮更“大”一點的PHP框架,比如Laravel或者Symfony。
響應參數
大多數情況,我們不需要關注Response
對象本身,只需要在控制器的操作方法中返回數據即可。
為了規范和清晰起見,最佳的方式是在控制器最后明確輸出類型。
ThinkPHP中支持的輸出類型:
例如想要返回json數據給客戶端:
public function hello()
{$data = ['name' => 'thinkphp', 'status' => '1'];return json($data);
}
想要返回html格式:
response($data);
?返回字符串:
return 'Hello,ThinkPHP!';
?響應參數?
我們還可以設置響應的狀態碼和響應頭等信息,可以這樣寫:
json($data)->code(201)->header(['Cache-control' => 'no-cache,must-revalidate'
]);
使用鏈式調用設置狀態碼和響應頭。
路由
在上面的控制器或說是接口的定義中,我們并沒有像SpringBoot或Express那樣去顯示的聲明路由。
例如接口使用什么請求方式(Get還是Post),和顯示的把URL和處理函數進行綁定等。
這一節就是說明這些問題。
在SpringBoot中我們通過注解來“隱式”綁定路由,而在ThinkPHP中的路由系統獨立于Controller存在,需要顯式配置。
基礎路由映射
下面我們使用路由來映射控制器演示寫法。
待路由的控制器:
<?php
namespace app\controller;
use app\BaseController;class UserController extends BaseController{public function test3(){return "我是路由訪問的控制器方法";}
}
定義路由,打開/route/app.php文件
use think\facade\Route; // 引入RouteRoute::get('user/test3', 'UserController/test3');
測試路由訪問:
?路由寫法解析
上面的路由語法:Route::快捷方法名('路由表達式', '路由地址');
關于快捷方法名有:
?關于路由表達式:
說的就是我們自定義的請求URL的地址。
當然表達式定義上還可以說明路徑參數,可以這樣寫:
Route::get('user/test3/:name', 'UserController/test3');
控制器代碼:?
值得注意的是:在路由上定義的路徑參數名要和控制器參數名相同。?
模型(Model)
?這里的模型在SpringBoot中體現為實體類。
我們能使用模型來和數據庫表的映射關系,也就是ORM的一個重要組成。
模型的定義
1.模型目錄創建:app/model
2.創建的模型名稱要和數據表名一致(大駝峰映射)?
例如創建一個User模型:?
User模型對應user數據表:
模型使用測試
測試前到配置文件config/database.php中配置好數據庫連接信息。
我們通過這個數據表映射出的模型去做查詢測試
use app\model\User; // 導入User類/模型// 獲取用戶表數據
public function getUserData(){$users = User::select(); // 直接用User模型快捷查詢數據halt(json($users));
}
訪問控制器:?
發現可以獲取到user表的所有數據,至于User::select()的寫法先不用糾結,這是使用模型查詢數據的一種方式,下面會介紹到。
?
模型的設置
模型對應數據表是我們已經明確了的,在上面的模型測試訪問中也是成功了的。
可以查詢到數據是因為框架根據我們的類名去查詢數據表名,然后獲取數據。
設置表名
如果我們的類名和表明不一致,我們可以在模型中進行設置:
class ABC extends Model
{protected $table = 'user'; // 數據表名稱
}
?上面代碼可以看到,我們的模型名為ABC,而我們的表名為user,直接查詢必然會映射失敗導致數據無法查詢,我們可以通過$table屬性對表名進行定義。
?設置主鍵
默認會識別主鍵名稱為“id”,如果你的主鍵名稱是其他需要設置$pk屬性值。?
use think\Model;class User extends Model
{protected $pk = 'uid'; // 主鍵名稱
}
模型字段的設置
這使得我們可以在模型中設置字段名,就是顯示的設置類的成員屬性。
為什么要顯示設置字段名?
在上面的模型測試中我們沒有設置模型的字段名依然可以查詢出數據。
那么為什么我們還要顯示的聲明字段名呢?
1. 一方面是可讀性,使我們看到模型就知道數據表有哪些字段。
2. 提升性能,當我們默認使用模型映射查詢數據時,首先框架會根據數據表名先查詢出表的所有字段,再使用查詢出來的字段查詢數據,這必然就會導致多執行一次sql查詢,所以如果我們能在模型中先定義,就可以省去一次sql查詢。
使用$schema設置字段名
<?php
namespace app\model;use think\Model;class User extends Model
{// protected $table = 'user'; // 數據表名稱// 設置表字段protected $schema = ['id' => 'int','name' => 'string','status' => 'int','score' => 'float','create_time' => 'datetime','update_time' => 'datetime',];
}
通過$schema去定義字段我們還能顯示看到字段的類型。
當然,模型中的字段要和數據表的字段名對上的。
廢棄字段
當我們在操作數據表想要忽略某個/些字段時,我們可以將字段進行設置:
class User extends Model
{// 設置廢棄字段protected $disuse = [ 'status', 'type' ];
}
這樣設置之后,在查詢和寫入的時候會忽略定義的status
和type
廢棄字段。
訪問數據庫?
下面只會說明使用模型直接ORM訪問數據表,而不會說明Db:table()的原始方式去查詢數據。
如果對Db::table()的方式感興趣,可以訪問官方手冊。
數據庫的連接
訪問config/database.php文件:
可以看到在數據庫的連接信息中,是優先使用環境變量中的連接信息的,如果環境變量中沒有連接信息的參數,才會使用第二個參數。
所以,我們在本地測試中優先到.env文件中進行連接信息的配置。
這個文件名原先為.example.env ,可以改為.env。
然后進行.env配置文件配置連接信息:
關于.env文件,一般用于本地環境測試,如果后續要部署到服務器需要將.env內容注釋,使用database.php文件進行配置。
增刪改查(CURD)
新增
定義路由
<?phpuse think\facade\Route;Route::post("user/add", "UserController/addUser"); // 新增用戶
控制器代碼:
// 新增用戶
public function addUser(){// 獲取請求參數$data = Request::param();// 創建用戶模型實例$user = new User();// 設置用戶數據$user->data($data);// 保存用戶數據到數據庫if($user->save()){return "新增成功";}else{return "新增失敗";}
}
測試訪問?
save
方法支持傳入模型實例或實體對象實例。?
使用::create方法
public function addUser(){// 獲取請求參數$data = Request::param();// 執行添加,失敗會返回null,成功返回模型對象$user = User::create($data);if($user){// 成功邏輯}else{// 失敗邏輯}
}
?新增多條
使用saveAll()方法。?
$user = new User;
$list = [['name'=>'thinkphp','email'=>'thinkphp@qq.com'],['name'=>'onethink','email'=>'onethink@qq.com']
];
$user->saveAll($list);
刪除?
定義路由:
Route::delete('user/del/:id', 'UserController/deleteUser'); // 刪除用戶
控制器代碼:
// 刪除用戶
public function deleteUser($id){// 方法一、$user = User::find($id);$user->delete();// 方法二、$res = User::destroy($id);
}
?刪除多條:
// 方法1:
User::destroy([1,2,3]);
// 方法2:
User::where('id','>',10)->delete();
更新/修改
在取出數據后,更改字段內容后使用save
方法更新數據。這種方式是最佳的更新方式。?
// 方法1
$user = User::find(1);
$user->name = 'thinkphp';
$user->email = 'thinkphp@qq.com';
$user->save();// 方法2:
User::update(['name' => 'thinkphp'], ['id' => 1]);
// 如果你的第一個參數中包含主鍵數據,可以無需傳入第二個參數(更新條件)
User::update(['name' => 'thinkphp', 'id' => 1]);
查詢
下面的內容并不完整,僅演示常用的一些查詢。
可以往下過一遍看看語法,其他的方法用法都大差不差。
完整的查詢方法可以到官方手冊,或用的時候直接問題AI就行。?
查詢單條
// 根據主鍵查詢單條數據
$user = User::find(1);// 使用查詢構造器查詢滿足條件的數據
$user = User::where('name', 'thinkphp')->find();
?執行find()方法后,如果沒有找到數據返回null,否則返回當前模型的對象實例。
獲取多條數據
// 根據主鍵獲取多個數據
$list = User::select([1,2,3]);
// 對數據集進行遍歷操作
foreach($list as $key=>$user){echo $user->name;
}
帶有條件的查詢多條?
$users = User::where('id', '>',10)->select();
模糊查詢
$user = User::whereLike("name", "%mao%")->select();
?多條件
如果有多條件,我們可以多次調用where()進行拼接。
$users = User::where('id', '>',10)->where("name", "like", "mao")->select();
多個where之間的連接為AND。
如果想要where之間的關系為OR,我們可以把where換成whereOr:
$users = User::where('id', '>',10)->whereOr("name", "like", ""mao)->select();
?排序
在調用鏈上添加上order("排序字段", "排序規則(asc , desc)")
users = User::where("id" , ">", 1)->order("age", "desc")->select();
聚合函數的使用
// 獲取用戶表年齡最大值
$user = User::max("age");
其他的如min(), avg(), sum()是一樣的用法。
分頁查詢
// 獲取分頁參數,默認每頁 10 條,第 1 頁
$page = Request::param('page', 1);
$limit = Request::param('limit', 10);// 查詢用戶數據并分頁
$users = User::paginate(['list_rows' => $limit,'page' => $page
]);
原生查詢
原生查詢說的是我們可以手寫sql語句去執行查詢。
public function getUsers(){$user = Db::query("select * from user");halt($user);
}
?測試訪問??
部署
在我們本地開發完ThinkPHP項目后,如果想要讓項目能線上訪問,我們需要將項目部署到線上服務器上。這是我們要說明的問題。
我們將項目部署到服務器上,然后使用瀏覽器或apifox訪問服務器的接口進行測試。
?
部署前準備?
?1.關閉調試模式?
將APP_DEBUG設置為false
2.數據庫的配置
?把數據庫的連接配置信息填好(線上可訪問)。
?3.服務器環境?
部署的服務器需要安裝:
- PHP環境
- Composer
- Nginx
上面三個服務器的環境安裝配置過程就不演示了。
?
部署方式一:內置服務器直接啟動??
最直接的部署方式是直接將thinkphp項目上傳到服務器,然后通過composer安裝依賴后直接使用php think run將項目啟動。
這種方式 僅適合本地開發調試,不推薦在生產環境中使用。
但是如果你的項目僅用來上線演示,不供大眾使用,直接使用這種方式也無妨。
?部署步驟??
1.?上傳開發好的ThinkPHP項目到服務器
2.?到項目根目錄下安裝依賴,命令:composer install --no-dev -o?
3.?使用命令啟動項目: php think run --host=0.0.0.0 --port=9878?
- 通過--port參數臨時指定啟動的端口(自定義)。
- 如果部署在云服務器上,記得將服務器防火墻和云服務器的安全組端口給打開。
4.?訪問測試就行:服務器域名或IP:自定義端口/接口路徑
這種方式為什么不推薦生產環境使用?
性能較差
php think run
使用的是 PHP 自帶的開發服務器,單線程、無并發處理能力,無法承受多用戶訪問,容易崩潰。穩定性不足
它不是為長時間運行設計的,容易在高負載或錯誤情況下宕掉,不具備錯誤自動恢復機制。安全性較低
內建服務器沒做太多安全隔離,比如目錄穿越、惡意請求防護等都非常薄弱。日志、連接管理能力差
沒有日志輪轉、訪問日志、慢日志、連接池等功能,排查問題不方便。
部署方式二:使用Nginx服務器
1.?上傳開發好的ThinkPHP項目到服務器
2.?到上傳好的項目根目錄下安裝依賴,執行安裝命令:composer install --no-dev -o
3.??配置nginx,打開你自己的nginx目錄下的配置文件nginx.conf
# 配置示例,請根據自己的服務器參數進行填寫
server {listen 8000; # 端口,自定義server_name 你域名或服務器IP;root 你的項目路徑/public;index index.php index.html;location / {if (!-e $request_filename) {rewrite ^(.*)$ /index.php?s=$1 last;break;}}location ~ \.php$ {# 這里的配置需要按照實際情況填寫,看下面說明fastcgi_pass unix:/run/php-fpm/www.sock;fastcgi_index index.php;fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;include fastcgi_params;}
}
配置完nginx之后,記得執行命令nginx -s reload 命令重新加載一下配置文件。??
需要注意!!
?
上面配置中的:fastcgi_pass unix:/run/php-fpm/www.sock;?一行中,需要正確配置php-fpm服務目錄名稱。
怎么找到該填的值呢?
我們可以通過命令 “grep "listen =" /etc/php-fpm.d/*.conf” 進行查詢。
然后將查詢結果填到上面的nginx配置中。
例如圖片輸出:/run/php-fpm/www.sock。
?php-fpm是什么??
- 是PHP在Web服務器(如 Nginx)中運行的核心組件。
- 例如當我們用Nginx訪問一個
.php
頁面時,Nginx本身不會執行PHP,它會把請求轉交給php-fpm
去處理,處理完再把結果返回給用戶。- 簡單來說就是PHP的解釋器。
?所以,我們在nginx配置中需要正確配置fpm的路徑,以便使nginx能找到fpm。
4.?開啟php-fpm服務
// 查看php-fpm服務狀態
systemctl status php-fpm// 如果是關閉狀態就啟動他
systemctl start php-fpm
5.?開放文件權限
?給上傳的ThinkPHP項目根目錄下的public 和 runtime目錄開放權限
chmod -R 775 runtime
chmod -R 775 public
以及,需要給上面執行命令grep "listen =" /etc/php-fpm.d/*.conf后輸出(php-fpm)的目錄開放權限。
這就是剛才在nginx中配置的php解釋器,給www.sock開放權限。
這是必要的,否則會出現502報錯攻擊!!?
6. 測試訪問
測試前記得把你在nginx配置的端口在服務器防火墻和云服務器安全組打開。?
最后
完整教程請訪問官方手冊:序言 - ThinkPHP官方手冊
?