tp6筆記
json() 提供 方法
修改 開啟報錯查看
env
中開啟 de_bug
database配置數據庫 會先使用 .env 中的配置
tp6 中使用
Db:: 需要 use 門面模式 user think\facade/\Db
app.php
config/app.php 錯誤信息
多應用模式
composer require topthink/think-multi-app
在當前控制器下創建 route 路由文件夾
配置域名訪問
http://www.tp6.com/demo.php/index/test?a=1
在public 下復制index.php 改名為 demo.php
$response = $http->name(‘demo’)->run(); 若是文件名不同 可用 name指向
config 可以在每個 目錄下 +
admin / config 當前目錄有用
api/config …
路由
多應用 需要 加 文件名 admin/text index/…
config 下 業務狀態碼
創建 status.php`
return [
‘success’ => 1,
‘error’ => 0,
];
未開啟強制路由
都可以
http://www.tp6.com/index.php/demo/index/t demo 應用 inde想控制器 t方法
http://www.tp6.com/demo.php/index/t
has 判斷某個值是否設置
容器和依賴注入
全局中間件
可以通過命令行指令快速生成中間件
php think make:middleware Check
<?php namespace app\middleware; class Check { public function handle($request, \Closure $next) { // 添加中間件執行代碼 ... return $next($request); } }
在 app/middleware.php
<?php // 全局中間件定義文件 return [ // 全局請求緩存 // \think\middleware\CheckRequestCache::class, // 多語言加載 // \think\middleware\LoadLangPack::class, // Session初始化 // \think\middleware\SessionInit::class app\middleware\Check::class, ];
應用中間件
模板引擎
composer安裝
composer require topthink/think-view
簡單的密碼+鹽處理
密碼 123
md5(‘123_md5’) 處理字符串
驗證碼
composer require topthink/think-captcha
驗證碼使用 驗證是時 徐快需要在中間件中開啟session
模型分層架構
思維
驗證器
$data = [ 'username' => $username, 'password' => $password, 'captcha' => $captcha, ]; $validate = new \app\admin\validate\AdminUser(); if(!$validate->check($data)) { return show(config("status.error"), $validate->getError()); }
<?php /** * Created by singwa * User: singwa * motto: 現在的努力是為了小時候吹過的牛逼! * Time: 07:11 */ namespace app\admin\validate; use think\Validate; class AdminUser extends Validate { protected $rule = [ 'username' => 'require', 'password' => 'require', 'captcha' => 'require|checkCapcha', ]; protected $message = [ 'username' => '用戶名必須,請重新輸入', 'password' => '密碼必須', 'captcha' => '驗證碼必須', ]; protected function checkCapcha($value, $rule, $data = []) { if(!captcha_check($value)) { return "您輸入的驗證碼不正確!"; } return true; } }
Url 注意
如何正確分層之操作datebase
注意使用 init初始化
如何處理在init里無法重定向
注意 如何 /admin/index/index 寫的時候需要+ /’
前置中間件
preg_match()
1 正則
2 str 匹配2中有無 1
return 1 0
后置中間件
session
session('adminUser', $res); session('adminUser', null);
- 1
business 業務邏輯層
在business 處理業務邏輯 -> 調用 model 的sql執行 在business進行 異常拋出 在c層try catch 補獲
model 放在 common 公共文件里
redis 實現驗證碼 存活時間60s
安裝 https://blog.csdn.net/kxukai/article/details/106692983
日志
開啟日志 runtime 可以查看執行的原生sql語句 進行分析
工廠模式
短信可以使用redis 進行限制 存手機號 + 發信息間隔時間~
如何流控 20%阿里雲短信 80%百度雲短信
案例:2018年年底 央視春晚 百度手機號登錄發送驗證碼分發不同運營商流量, 就是用這個來做的, 並發50萬 發送短信驗證碼。
簡單粗暴 實用 最簡單的方法往往最有效
$a = rand(0,99); if($a < 80) { // 阿里雲邏輯 } else { // 百度雲邏輯 }
- 1
- 2
- 3
- 4
- 5
- 6
前后端分離 redis+ token 不使用session+cookie
config(‘statis.success’)
異常處理 try catch
捕獲異常時 注意記錄日志 以供分析
tp6 helper 全局搜索自帶拋出異常 throw_if 同laravel
手動拋出異常
throw new \think\Exception("不存在該驗證碼", config('status.code.not_code'));
- 1
捕獲異常
try { $result = (new \app\common\business\User())->login($data); } catch (\Exception $e) { return show($e->getCode(), $e->getMessage()); }
// 阻止數據庫拋出異常 被 用戶看到 手動拋出~
try { $this->userObj->save($userData); $userId = $this->userObj->id; }catch (\Exception $e) { throw new \think\Exception("數據庫內部異常"); }
redis 刪除 等於null 即可
redis 寫登錄 邏輯
前后端分離 所以是 api 接口
- 用戶點擊發送驗證碼 把 sms . 手機號 存入 redis 內容有 驗證碼 存活60s //這里sms為了再redis中進行區分別的緩存
- 接收數據 轉換格式 驗證 isajax ispost
if (!$this->request->isPost() || !$this->request->isAjax() ) return show(config("status.error"), "非法請求"); $phoneNumber = input("phone_number",'','trim'); $code = input("code", 0, "intval");
- validate 驗證器 驗證參數
- 驗證 用戶輸入的 code !== redis中的該手機號對應的 code
// 用戶輸入的 code !== redis中的該手機號對應的 code $redisCode = cache(config("redis.code_pre") . $data['phone_number']); if (empty($redisCode) || $redisCode != $data['code']) { throw new \think\Exception("不存在該驗證碼", config('status.code.not_code')); }
從第五層開始要在 business 邏輯層進行處理 判斷
例子 拋出異常最好用 try catch 進行補獲 避免數據庫拋出暴露信息被用戶查看
5. 還是在common/ business 操作
根據用戶手機號 查找有該用戶登錄記錄
// 需要去判斷表 是否有 用戶記錄 phone_number // 生成token $user = $this->userObj->getUserByPhoneNumber($data['phone_number']);
這里model 層在 common/model datebase 操作公共的 可以共用
然后進行判斷 操作用戶記錄
6. 然后把生成的token作為k 用戶的id 和用戶名作為 v 存入redis 存活時間 ~
$redisData = [ "id" => $userId, "username" => $username, ]; $res = cache(config("redis.token_pre") . $token, $redisData, Time::userLoginExpiresTime($data['type']));
7.最后把前端需要的數據返回
中間件 進行 前端 檢測是否登錄 2種方法
第一種方法 需要驗證login的controller extends 該控制器 進行檢測
class AuthBase extends ApiBase { public $userId = 0; public $username = ""; public $accessToken = ""; public $isLogin = 1; public function initialize() { parent::initialize(); // TODO: Change the autogenerated stub // if ($this->isLogin == 1) { // $this->userId = 6; // 測試場景 // return true; // } $this->accessToken = $this->request->header("access-token"); if (!$this->accessToken || !$this->isLogin()) { return $this->show(config("status.not_login"), "沒有登錄"); } } /** * 判斷用戶是否登錄 * @return bool */ public function isLogin() { $userInfo = cache(config("redis.token_pre") . $this->accessToken); if (!$userInfo) { return false; } if (!empty($userInfo['id']) && !empty($userInfo['username'])) { $this->username = $userInfo['username']; $this->userId = $userInfo['id']; return true; } return false; } }
第二種方法使用中間件 進行檢測
在api 文件下 創建一個
修改 middlevare.php 中 對應信息
<?php declare (strict_types=1); namespace app\api\middleware; class Auth { /** * 處理請求 * * @param \think\Request $request * @param \Closure $next * @return Response */ public function handle($request, \Closure $next) { //前置中間件 // if (empty(session('adminUser')) && !preg_match("/login/", $request->pathinfo())) { // return redirect((string)url('login/index')); // } //ajax 返回api格式 例如處理rbac //這樣會處理所有api下的控制器 所以需要 // return $this->show(config("status.not_login"), "沒有登錄"); if (in_array($request->pathinfo(), config('login.need_verify_login'))) if (!$this->isLogin()) return ajaxReturn(0, '', '未登錄,請先登錄'); return $next($request); } public function end(\think\Response $response) { } /** * 判斷用戶是否登錄 * @return bool */ public function isLogin() { $token = request()->header("access-token"); $userInfo = cache(config("redis.token_pre") . $token); if (!$userInfo) { return false; } if (!empty($userInfo['id']) && !empty($userInfo['username'])) { return true; } return false; } }
免登錄
打開登錄界面 前 執行控制器 的 中間件 進行 驗證 獲得返回數據 進行 重定向至跳轉首頁 or 等待用戶登錄
##退出登錄
清空當前redis中 k 拼接 對應的token v 為 null
model 層代碼優化
business 邏輯層
model 層
這里可以父類繼承 調用 減少代碼量
面向過程 => 面向對象 統一返回給前端
模型 使用
with 會 兩次
執行sql語句 進行查詢 數據量大 大公司常用
withjoin 一次join 查詢
sku 建議用2 方便 直接對應sku表的id
統一規格也使用 sku
系統瀏覽量 pv
每個商品的瀏覽量
商品展示流程
展示商品有自己默認skuid
用戶點擊根據skuid 到sku查詢到 該商品的goodsid
然后根據goodsid 到goods 獲取該商品信息 和 該商品對應的 所有 sku信息
這里gids
1,11 :1 : 1 //skuid
1, 11 規格id // 顏色, 尺碼
sku
購物車
瀏覽器緩存
mysql redis
存redis 高性能
hash
表如何設計的
流程
1.加入購物車
用戶登錄狀態 點擊商品加入購物車
即加入redis 中
(1) 到mysql根據skuid查找該商品的sku數據
2.購物車展示頁面
3.購物車刪除 修改數量
4.購物車排序
封裝在基礎類庫
hgetall
刪除 hdel test 1 2
獲取redis 購物車數據 進行排序
hLen 獲取數據
返回值
integer-reply: 哈希集中字段的數量,當 key 指定的哈希集不存在時返回 0
例子
redis> HSET myhash field1 “Hello”
(integer) 1
redis> HSET myhash field2 “World”
(integer) 1
redis> HLEN myhash
(integer) 2
redis>
庫存 嚴謹判斷 購物車+ 訂單 未支付 …2
hMget 返回 key 指定的哈希集中指定字段的值。 對於哈希集中不存在的每個字段,返回 nil 值。
hGetAll 返回 key 指定的哈希集中所有的字段和值。
訂單
主表副表
訂單流程
進入 加入 購物車頁面 訂單確認頁面 和 提交訂單頁面 都需要判斷 庫存
提交訂單流程
- 刪除購物車 的該商品
- 減庫存
- insert order 主表 副表
redis 延遲隊列處理無效訂單
用戶點擊 確定訂單時 未支付
存 redis 20分鍾有效期 然后腳本每秒 和當前時間比較 小於當前時間的則 把該訂單狀態改為 已取消 恢復該訂單對應sku的庫存
存redis 訂單id time()+20min
定時任務
暫時有bug 生成 所以要手動擋
最后在當前tp6 目錄的 命令行 運行 php think order 即可開啟進程
如果定時任務 進程掛了這么解決? supervise維持進程任務自動重啟
支付抽離 單獨服務 子服務 微服務
寫出用戶所有流程
支付流程
從購物車點擊進入結算 下單頁 先減庫存
點擊立即付款 跳轉支付界面 然后看支付回調
成功 失敗 進行數據庫處理 庫存等等…
Start
前置中間件 檢測 是否登錄 || 創建一個base來繼承 在其中判斷 pathinfo 對比
判斷前端傳來的token 是否存在redis
登錄 注冊 用redis存儲60s 驗證碼 進行判斷 sms . 133333333 驗證碼 | sms 是標識 拼接手機號 作 k v是驗證碼 參3存活時間
登錄成功 redis 存儲 k => user_token . 后台生成唯一token v => 登錄的用戶信息 然后把token 給header頭
進首頁 驗證
注冊登錄
- 登錄流程
驗證登錄
購物車
訂單
支付