Laravel已是衆所周知的「優雅」、「簡潔」、「實用」、「敏捷」、「特性豐富」等的代名詞,尤爲是對PHP開發者來講。固然了,也正是由於它特性過於「豐富」,致使不少特性並無列在文檔裏,或者曾經列出來過,可是出於某些緣由,後來又移除了。不少時候,咱們給新手學習建議的時候,都不會建議他直接去看文檔,由於文檔體系的展開,每每並非新手能hold住的。咱們每每會推薦他們從一些過來人的、真正優雅規範的教程開始上手,上手之後,再在實際當中自行查閱文檔。php
本文裏,將列出一些截至laravel 5.7的最佳實踐與使用建議,不論新手與否,相信均可以幫你將laravel用得更好,將laravel的優雅進行到底,同時更充分地利用起laravel的特性來。前端
$orders = Order::where('status', 'delivered')->where('paid', true)->get();
複製代碼
咱們常常會寫些相似的邏輯,可是當條件查詢啥的多了之後,也會變得繁瑣,並且期間的不少條件限定與查詢,就不能複用,每次都得重複寫,這個時候local scopes就登場了。local scopes你能夠理解成本地的查詢域,或者更直白一些,就是一個經常使用的查詢條件片斷。咱們能夠在model裏這樣:laravel
class Order extends Model
{
...
public function scopeDelivered($query) {
return $query->where('status', 'delivered');
}
public function scopePaid($query) {
return $query->where('paid', true);
}
}
複製代碼
能夠看到這些查詢域,都是以scope開頭的,後面接上你想要的名字,而後在調用的時候,只須要調用scope後面的實際名字便可:數據庫
$orders = Order::delivered()->paid()->get();
複製代碼
因此這個查詢域,就是laravel query builder的一個個片斷,或者說數據庫條件查詢的片斷,而後它們能夠拼接起來,讓咱們終端的代碼更加簡潔直觀。bash
固然,咱們能夠更進一步,給這個查詢域傳上參數,造成動態的條件查詢,好比下面這樣:app
class Order extends Model
{
...
public function scopeStatus($query, string $status) {
return $query->where('status', $status);
}
}
$orders = Order::status('delivered')->paid()->get();
複製代碼
在不少不那麼規範的教程裏,你常常會看到以下的代碼:框架
public function store(Request $request)
{
$validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// The blog post is valid...
}
複製代碼
不規範的緣由,就是他們把全部能想到的東西,都丟在controller裏就完事了,但這樣觸犯的忌諱就太多了,違背的原則也就太多了,更重要的,隨着項目的進行,很容易讓代碼變成大花臉,變得愈來愈無法維護和擴展,更不用說別人甚至本身回頭閱讀了。那麼上面的這個例子,就是讓controller去管了它本不該該負責的數據驗證邏輯,而這塊,laravel有更優雅的方式來解放controller,也便是咱們的自定義form request:less
php artisan make:request StoreBlogPost
複製代碼
執行了之後,在app/Http/Requests/
文件夾下,就會多出下面這麼個單獨的類:dom
class StoreBlogPostRequest extends FormRequest
{
public function authorize()
{
return $this->user()->can('create.posts');
}
public function rules()
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}
}
複製代碼
這樣了之後,在controller裏,就不須要傳Request
了,而是傳上咱們剛纔定義的這個StoreBlogPostRequest
:異步
use App\Http\Requests\StoreBlogPostRequest;
public function store(StoreBlogPostRequest $request)
{
// 到這兒的時候,post的相關數據就已經驗證好了
}
複製代碼
固然在這個自定義的request類裏,咱們能夠作的文章不止這一點,好比咱們還能夠具體定義每個字段驗證失敗時的錯誤信息,這個能夠經過定義messages()
方法來實現,咱們也能夠將request類裏的錯誤信息,在blade當中去相應地返回。這些呢,在咱們的《Laravel5.7優雅實戰入門:第二版》課程裏都已經詳細說了,這裏就實在懶得囉嗦了。
created_at
一欄,進行降序排列(‘desc’)User::latest()->get();
複製代碼
User::latest('last_login_at')->get();
複製代碼
User::inRandomOrder()->get();
複製代碼
true
時,才執行相應查詢// 假設用戶在news頁面,想經過下面的url進行最新消息的排序
// mydomain.com/news?sort=new
User::when($request->query('sort'), function ($query, $sort) {
if ($sort == 'new') {
return $query->latest();
}
return $query;
})->get();
複製代碼
與這個when()
方法相對應的,還有一個unless()
, 它是when()
的反面,也即只有爲false的時候,才執行某個查詢
時至今日,你是否還在寫一些大段的、難寫難讀又難複用的SQL查詢語句呢?好比爲了獲取更多的信息,使用了大量的join
。即便你利用laravel的Query Builder,這些語句也很是難寫,每次寫一個是否得花半天呢?並且若是你不是那麼懂,原本一個很快的查詢,可能讓你寫糟糕了,反而很是很是慢。
可是若是你懂得laravel裏的Model關係,懂得ORM這種更現代的查詢方式,那麼每每幾乎沒有什麼難的查詢,寫的時候根本無需動腦,並且不少邏輯均可以複用,而且避免了因不懂背後原理而致使性能反而降低的問題。
這一方面,咱們pilishen.com已經說得足夠多了,好比能夠看看咱們以前的一篇相關帖子《laravel框架中的Model操做數據庫 , 相比DB類有什麼明顯的優越性嗎?》
固然這些,在咱們的《Laravel5.7優雅實戰入門:第二版》課程裏也都詳盡演示了。
原來咱們課程里老說,「高性能離不開異步,異步離不開隊列」。這方面,laravel的隊列job,在處理耗時的、能夠放在後臺運行的任務上,是一個強大的、必需要掌握的工具。
想發送郵件?隊列job。 想發送廣播消息?隊列job。 想進行較多的圖片操做?隊列job。 。。。
相似的耗時操做,隊列可讓你的前臺用戶無需等待,背後處理便可。這期間,你甚至能夠設置隊列的頻道或名字,設置優先級,設置超時時間,設置重試次數,等等等等。
假設你只有first_name
和 last_name
兩個字段,而後你想着每次都能輕鬆地取得user的全名name,那麼能夠:
class User extends Model
{
...
public function getNameAttribute(): string
{
return $this->first_name.' '.$this->last_name;
}
}
複製代碼
這樣當你獲取$user->name
時,就能自動拼接起全名來。默認的這個$user->name
屬性,並不會自動附加到你的user實例上,也即這個時候你dd($user)
,並不會顯示出name
屬性來,那麼怎麼樣讓這個name
屬性自動附加到$user
實例上呢?能夠以下:
class User extends Model
{
protected $appends = [
'name',
];
...
public function getNameAttribute(): string
{
return $this->first_name.' '.$this->last_name;
}
}
複製代碼
這個時候,每次咱們dd($user)
,或者在blade視圖以及js前端調用中,就天然會帶上這個拼接出來的屬性了,固然了,這個屬性,並無實際存在數據庫中。
假設有個BettingOdds.php
:
class BettingOdds extends Model
{
...
}
複製代碼
可能有的人會將一些靜態內容放到好比說config/bettingOdds.php
中:
return [
'sports' => [
'soccer' => 'sport:1',
'tennis' => 'sport:2',
'basketball' => 'sport:3',
...
],
];
複製代碼
而後呢,獲取的時候這樣config(’bettingOdds.sports.soccer’);
,可是這樣並很差,存在一些隱患,不如直接放到model當中:
class BettingOdds extends Model
{
protected static $sports = [
'soccer' => 'sport:1',
'tennis' => 'sport:2',
'basketball' => 'sport:3',
...
];
}
複製代碼
而後這樣來獲取BettingOdds::$sports['soccer'];
並且這樣,在進一步的數據調用中,更方便,好比:
class BettingOdds extends Model
{
protected static $sports = [
'soccer' => 'sport:1',
'tennis' => 'sport:2',
'basketball' => 'sport:3',
...
];
public function scopeSport($query, string $sport)
{
if (! isset(self::$sports[$sport])) {
return $query;
}
return $query->where('sport_id', self::$sports[$sport]);
}
}
複製代碼
那麼這個時候,咱們就能夠像前面說到的,BettingOdds::sport('soccer')->get();
$fruits = ['apple', 'pear', 'banana', 'strawberry'];
foreach ($fruits as $fruit) {
echo 'I have '. $fruit;
}
複製代碼
之前,咱們都習慣直接用array來操做相關數據,可是在laravel裏,更優雅的方式是多使用collection及相關方法
$fruits = collect($fruits);
$fruits = $fruits->reject(function ($fruit) {
return $fruit === 'apple';
})->toArray();
['pear', 'banana', 'strawberry']
複製代碼
這樣咱們就能夠方便地利用上一些filter、map、transform、reject等等方法,既簡單,又優雅
咱們知道laravel的Query Builders,當在它後面最終調用->get()
方法時,返回的是一個集合Collection實例。可是這裏,不要將單純的Collection和Query builder混淆起來:
->get()
或first()
這些方法前,咱們實際上並無獲取任何數據,好比當咱們用orderBy()
、where()
這些查詢方法時->get()
或first()
方法時,數據才真的去獲取,相應的內存才真的佔用,而後返回一個集合實例因此,若是能在Query Builder的環節,就進行數據的過濾或條件限定,那就要在這一環節作。而不要說先獲取了數據,返回了Collection實例之後,再用Collection相關的方法去過濾或操做,這樣的話耗費的內存資源就容易多得多。固然了,這期間也要限定好查詢,利用好數據庫索引,關於數據庫索引,若是你還不夠精通,記得看咱們的國際IT專場:《每一個程序猿必須且必定要懂的「數據庫索引」》