Laravel 因可編寫出乾淨,可用可調試的代碼而爲廣大的 PHP 開發者所熟知。它一樣也支持許許多多的功能,有時卻未能在文檔中體現,或者因爲某種緣由它們出現過又被移除了。php
我已經在生產環境中使用 Laravel 2 年了,從中我學到如何把代碼變得更好,從我首次使用它以來我都充分發掘它的優點。接下來我將向你展現一些可能對你在用 Laravel 寫代碼時頗有幫助的奧義之招。laravel
Laravel 有一種很是棒的方式來使用 查詢構造器 編寫查詢。就像這樣:git
$orders = Order::where('status', 'delivered')->where('paid', true)->get();
複製代碼
很不錯。這讓我專一於編寫更友好的代碼而不是 SQL 語句。但若是用 本地範圍 ,咱們可讓這行代碼變得更好些。github
當查詢數據時, 本地範圍 容許咱們建立本身的 查詢構造器 鏈式方法。舉個例子,取代 ->where()
,咱們能夠用更簡潔的 ->delivered()
和 ->paid()
。數據庫
首先在 Order
模型,咱們加入一些方法:跨域
class Order extends Model
{
...
public function scopeDelivered($query) {
return $query->where('status', 'delivered');
}
public function scopePaid($query) {
return $query->where('paid', true);
}
}
複製代碼
當聲明本地範圍時,你應該使用 scope[Something]
來命名。這樣 Laravel 便會知道這是一個本地範圍而且能夠在查詢構造器中使用。請確保你在方法中傳入了第一個參數 $query
,也就是由 Laravel 自動注入的查詢構造器實例。數組
$orders = Order::delivered()->paid()->get();
複製代碼
對於可接受額外參數的查詢,你可使用動態範圍。每一個範圍都容許你傳入額外的參數。緩存
class Order extends Model
{
...
public function scopeStatus($query, string $status) {
return $query->where('status', $status);
}
}
$orders = Order::status('delivered')->paid()->get();
複製代碼
在本文的後面,你會知道爲何數據庫字段應該使用 蛇形命名
,但這裏有第一個緣由:Laravel 默認用 where[Something]
來替換 scope[Something]
。因此做爲 scopeStatus
範圍的代替,你能夠這樣作:bash
Order::whereStatus('delivered')->paid()->get();
複製代碼
對於 where[Something]
,Laravel 會搜索 蛇形命名
版本的數據庫字段。若是你的數據庫中有個 status
字段,你能夠用上面那個例子。若是有個 shipping_status
字段,你能夠用:app
Order::whereShippingStatus('delivered')->paid()->get();
複製代碼
由你決定!
Laravel 提供了一種優秀的方式來驗證表單提交的數據。若是你須要它,不論是 POST 仍是 GET 請求,它均可以驗證。
在控制器中,你能夠這樣作:
public function store(Request $request)
{
$validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// 若是這篇博客的內容無效……
}
複製代碼
可是當控制器中已經有不少代碼時,再把驗證表單數據的代碼加進去就會顯得很凌亂。你想盡量地減小控制器的代碼 —— 至少這是我在控制器中寫不少邏輯時想到的第一件事。
Laravel 提供了一種很萌的方式來驗證表單請求,那就是建立並使用專門的 請求類 而不是用原始的 Request
。你只須要建立你的請求類:
php artisan make:request StoreBlogPost
複製代碼
在 app/Http/Requests/
目錄中能夠找到你剛建立的請求類:
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',
];
}
}
複製代碼
如今,你應該用新建立的 App\Http\Requests\StoreBlogPostRequest
來代替原先的 Illuminate\Http\Request
類:
use App\Http\Requests\StoreBlogPostRequest;
public function store(StoreBlogPostRequest $request)
{
// 若是這篇博客的內容無效……
}
複製代碼
請求類中的 authorize()
方法應返回一個布爾值。若是返回了 false
,它會拋出一個 403
異常,請確保你在 app/Exceptions/Handler.php
的 render()
方法中捕獲了這個異常:
public function render($request, Exception $exception)
{
if ($exception instanceof \Illuminate\Auth\Access\AuthorizationException) {
//
}
return parent::render($request, $exception);
}
複製代碼
請求類中還有一個 messages()
方法,當驗證失敗時,它會返回一個包含了錯誤信息的數組:
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',
];
}
public function messages()
{
return [
'title.required' => 'The title is required.',
'title.unique' => 'The post title already exists.',
...
];
}
}
複製代碼
@if ($errors->any())
@foreach ($errors->all() as $error)
{{ $error }}
@endforeach
@endif
複製代碼
若是你想獲得某個字段的驗證信息,你能夠這樣作(當這個字段驗證經過時 $errors->has()
會返回一個 false
):
<input type="text" name="title" />
@if ($errors->has('title'))
<label class="error">{{ $errors->first('title') }}</label>
@endif
複製代碼
構建查詢時,可使用已有的魔術範圍:
created_at
倒序查詢:User::latest()->get();
複製代碼
User::latest('last_login_at')->get();
複製代碼
ORDER BY RAND()
)User::inRandomOrder()->get();
複製代碼
你是否曾經爲了獲取更多的信息而在查詢語句中使用大量的 join 操做?即便在使用查詢構造器的狀況下,編寫這樣的 SQL 語句也是困難的,可是數據模型已經使用 關聯關係 來實現一樣的功能。因爲文檔提供了太多的信息,所以剛開始時你可能對關聯關係並不熟悉,可是這些內容能夠幫助你更好的理解事物的運行原理,同時讓你的程序運行得更加順暢。
經過 這裏 查詢關聯關係的文檔。
Laravel 的任務 是後臺運行程序必用的功能強大的工具。
任務系統可以幫助你實現,在執行上述這些任務時,減小你的用戶的應用加載時間。這些任務能夠被放進命名的隊列,它們可以被安排優先級,Laravel 幾乎在全部可能的地方都實現了隊列:不管在後臺執行一些 PHP 任務,或者發送消息,或者廣播事件,隊列都在這些場景中出現。
你能夠在 這裏 查詢隊列的文檔。
在使用隊列時,我喜歡使用 Laravel Horizon ,由於它很容易安裝,它可以經過 Supervisor 工具或者配置文件實現後臺運行,同時我可以告訴 Horizon 我但願每一個隊列產生多少個進程。
Laravel 從一開始就教給你變量和方法應使用像 $camelCase
camelCase()
這樣的小駝峯命名而數據庫字段應使用像 snake_case
這樣的蛇形命名。爲何呢?由於這有助於咱們構造更好的 訪問器。
訪問器是能夠直接在模型中構造的自定義字段。若是咱們的數據庫包含了 first_name
、last_name
、age
這幾個字段,咱們能夠增長一個叫作 name
的自定義字段來把 first_name
和 last_name
拼接起來。別擔憂,這個 name
不會被寫入到數據庫。它只是某個模型的自定義屬性。全部的訪問器,和 範圍 同樣,都有自定義命名語法:getSomethingAttribute
:
class User extends Model
{
...
public function getNameAttribute(): string
{
return $this->first_name.' '.$this->last_name;
}
}
複製代碼
當使用 $user->name
,訪問器會返回拼接好的字符串。
默認狀況下,用 dd($user)
是看不到 name
屬性的,可是經過 $appends
變量咱們可使它一直可用:
class User extends Model
{
protected $appends = [
'name',
];
...
public function getNameAttribute(): string
{
return $this->first_name.' '.$this->last_name;
}
}
複製代碼
如今每次 dd($user)
,咱們均可以看到 name
了。(不過仍然,這個屬性不是從數據庫取得的,而是每次使用時將 first_name
和 last_name
拼接獲得的)。
要注意下,若是你數據庫裏已經有 name
這個字段了,那狀況就會有點不同:$appends
數組裏的 name
元素就不須要了,而後訪問器須要傳入一個參數,這個參數就是數據庫中的 name
(也就是說咱們用不着再使用 $this
了)。
舉個例子,咱們也許想用 ucfirst()
來使名字的首字母轉爲大寫:
class User extends Model
{
protected $appends = [
//
];
...
public function getFirstNameAttribute($firstName): string
{
return ucfirst($firstName);
}
public function getLastNameAttribute($lastName): string
{
return ucfirst($lastName);
}
}
複製代碼
如今當咱們用 $user->first_name
,它會返回一個首字母大寫的字符串。
因爲這個特性,數據庫字段最好是用 snake_case
這種蛇形命名。
我喜歡把與模型相關的靜態數據存放在模型文件中。讓咱們一塊兒來看一下。
不要像下面這樣:
BettingOdds.php
class BettingOdds extends Model
{
...
}
複製代碼
config/bettingOdds.php
return [
'sports' => [
'soccer' => 'sport:1',
'tennis' => 'sport:2',
'basketball' => 'sport:3',
...
],
];
複製代碼
使用下面的方式訪問:
config('bettingOdds.sports.soccer');
複製代碼
我更喜歡這樣作:
BettingOdds.php
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;
}
複製代碼
如今,咱們可使用一種高級的方法(譯者注:集合的方式)處理數組中的數據。咱們能夠過濾、轉換、遍歷和修改數組中數據:
$fruits = collect($fruits);
$fruits = $fruits->reject(function ($fruit) {
return $fruit === 'apple';
})->toArray();
['pear', 'banana', 'strawberry']
複製代碼
想要了解細節, 請查看 集合的文檔.
當使用 查詢構造器時,->get()
方法返回一個 Collection
實例。但要注意別搞混了 Collection
和 Query
builder:
orderBy()
, where()
,等等。->get()
方法以後,數據被獲取到,內存空間被消耗。它返回一個 Collection
實例。某些查詢構造器不可用或者說可用可是方法名不一樣,關於這些請查閱 全部集合的方法。若是你能在 Query Builder
層次過濾數據,就去作吧!不要依賴於等到結果 Collection
實例返回時再過濾---你將會消耗更多的內存空間。 使用 Limit 限制結果條數,在 DB 層使用索引來加快查詢。
以下是一些我在用的擴展包:
如下是我(原文做者)編寫的一些擴展包:
若是你有更多關於 Laravel 的問題,若是你須要運維方面的幫助,或者只是想說聲 謝謝
,你能夠在 Twitter @rennokki 上找到我!
轉自 PHP / Laravel 開發者社區 laravel-china.org/topics/2216…