對于本文涉及到的數據查詢的幾個基本原則請盡量納入你的項目規范,也是官方倡導的最佳實踐。在此之前,我希望你已經看過之前的一篇博客:「 你真的了解Db類和模型的正確使用姿勢么??」。
盡量不要使用數組條件查詢
大部分混亂的查詢語法都是使用了數組查詢導致的,而5.1的數組條件查詢用法又和5.0是完全不同的,如果你習慣了5.0的數組查詢方式,建議你閱讀下這篇文章:「 你真的了解Db類和模型的正確使用姿勢么? 」。
下面可能是很多新手比較容易犯的一個查詢錯誤。
立即學習“PHP免費學習筆記(深入)”;
$where['id']?=?['in',?'1,2,3']; User::where($where)->select();
顯然,這個查詢思維深受老版本的影響。5.1版本的查詢語法相比較5.0來說,更加對象化,下面的這種才是正確的用法。
$where['id']?=?[1,2,3]; User::where($where)->select();
也許是因為PHP的數組太好用的緣故,很多人對數組查詢條件樂此不疲(或者是對象焦慮?)。但如果你正確的使用查詢構造器以及配合模型的相關特性,可以讓你的查詢邏輯變得更清晰,也更加易于維護。
而且,在一些較為復雜的查詢條件下,你無法使用數組完成查詢,例如下面的查詢用法。
User::where('id',?'>',?100) ????->whereOr('id',?'where('name',?'like',?'think%') ????->whereColumn('name',?'nickname') ????->when('80'==?$condition,?function?($query)?{ ????????$query->where('score',?'>',?80)->limit(10); ????})->select();
所以,除非你很清楚5.1的數組查詢用法,否則請盡量不要用數組條件查詢了。
安全使用字符串查詢條件
在使用字符串查詢條件的時候,如果存在外部變量,請務必使用參數綁定,并最好使用whereRaw方法,該方法可以和其它的查詢構造器方法混合使用。
User::whereRaw("id?=?:id?AND?name?=?:name",?[ ????????'id'?=>?[$id,?PDO::PARAM_INT]?,? ????????'name'?=>?$name ????])->where('status',?1) ????->order('id',?'desc') ????->select();
對于一些比較在意性能的查詢,你也可以直接使用query或者execute方法,但同樣也要注意參數的安全以及考慮不同數據庫的移植問題。
Db::query("select?*?from?think_user?where?id=??AND?status=?",?[8,?1]); Db::execute("update?think_user?set?name=:name?where?status=:status",?['name'?=>?'thinkphp',?'status'?=>?1]);
對使用了SQL函數的查詢采用Raw機制
如果你的查詢里面包含了SQL函數,那么請使用whereRaw(或者whereExp)、orderRaw或者fieldRaw方法。
User::whereExp('nickname',?"=?CONCAT(name,?'-',?id)") ????->orderRaw("field(name,'thinkphp',?'kancloud')") ????->fieldRaw('id,SUM(score)') ????->select();
合理運用閉包,但不要濫用
閉包查詢在查詢構造器中有一些特殊用途,但如非必要,也無需濫用。
閉包查詢的典型使用場景包括下面幾個。
條件查詢中通常都用閉包來表示一組條件查詢。
User::when($condition,?function?($query)?{ ????//?滿足條件后執行 ????$query->where('score',?'>',?80)->limit(10); },?function?($query)?{ ????//?不滿足條件執行 ????$query->where('score',?'>',?60); })->select();
在一些子查詢中經常會用到閉包。
User::whereIn('id',?function?($query)?{ ????$query->table('profile') ????????->where('name',?'like',?'think%') ????????->field('id'); })->select();
生成一組閉合的查詢條件
User::where('id',?'>',?100) ????->whereOr(function($query)?{ ????????$query->where('name',?'like',?'think%') ????????->whereColumn('name',?'nickname'); ????})->select();
在這個查詢用法中,閉包里面的查詢條件會在兩邊加上括號而成為一個閉合的查詢條件。
在很多的關聯預載入查詢中可以通過閉包來進行關聯數據的篩選。
User::with(['profile'?=>?function($query)?{ $query->field('user_id,email,phone'); }])->select([1,2,3]);
盡量復用你的查詢條件
所有的查詢條件應該做到一處定義多處復用,例如封裝到模型的方法里面,尤其不要直接把一堆復雜的查詢條件寫到你的控制器代碼,否則一旦業務調整,滿世界的搜索代碼改變你的查詢條件將會是一場噩夢。
你也許在官方的手冊或者一些教程中看到很多在控制器里面直接封裝查詢條件的寫法,但那僅僅是出于方便展示用法的需要,并不可取。
在一些中大型的應用架構設計中,通常會把模型分成數據層、邏輯層和服務層,控制器只會調用服務層方法。而查詢邏輯則基本上被封裝到邏輯層里面,數據層僅僅是做模型的各種定義。
而在簡單的應用里面,也可以采用PHP的Trait機制來實現代碼的復用機制。
用查詢范圍或搜索器簡化查詢
如果你使用模型查詢的話,把你的查詢條件盡量封裝到查詢范圍或者搜索器方法里面,查詢范圍和搜索器的區別主要在于查詢范圍比較適合定義一組(多個字段)查詢條件,如果要調用多個查詢范圍需要多次調用,而搜索器比較適合定義一個字段(其實并非絕對)的查詢條件,只需要調用一次withSearch方法。
使用查詢范圍和搜索器的例子。
<?php namespace appindexmodel; use thinkModel; class User extends Model { public function scopeVip($query) { $query->where('user_type',?'vip') ????????????->where('status',?1) ????????????->field('id,name'); ????} ???? ????public?function?searchAgeAttr($query,?$age) ????{ ????????$query->where('age','>',$age); ????}???? ???? ????public?function?searchScoreAttr($query,?$score) ????{ ????????$query->where('score','where('score',?'>'?,0); ????}???? }
控制器代碼
<?php namespace appindexcontroller; use thinkController; use thinkRequest; class index extends Controller { public function index(Request $request) { // 查詢VIP會員 User::vip()->select(); ????????//?查詢年齡和分數 ????????User::withSearch(['age,'score''],?$request->param())->select(); ????} }
在控制器代碼中,我們只關注業務邏輯本身,而不需要關注這個邏輯內部的查詢條件是什么。更詳細的關于搜索器和查詢范圍的內容可以參考官方手冊。
PHP中文網,有大量免費的你真的了解Db類和模型的正確使用姿勢么?,歡迎大家學習!
本文轉自:https://blog.thinkphp.cn/833794