商業,創業,美食,葡萄酒,閱讀,網路科技。
這是我的 FB粉專 以及 IG,我比較常使用 Threads,歡迎大家追蹤互動~
https://laravel.com/docs/5.3/eloquent
https://laravel.com/docs/5.3/eloquent-relationships
Mass Assignment
$flight = AppFlight::create(['name' => 'Flight 10']);
mass assignment 目標的 ORM class「一定」要指定 $fillable 或是 $guarded。以上面例子來說,一定要指定 $fillable = [‘name’] 或是 $guarded = [‘other_attribute’],不能不指定 (即使 $guarded = [] 也行)。官網特別提到,建議用 $guarded 而不是 $fillable (當然還是可以用 $fillable)。原因是開發者該關注的是那些不能隨意被更改的欄位。最後提一下,以上 mass assignment 規則「不適用」一般儲存/更新,如下面例子,即使 $guarded = [‘name’] 一樣能儲存:
$flight = new Flight;
$flight->name = $request->name;
$flight->save();
或是
$flight = Flight::find(5);
$flight->name = $request->name;
$flight->save();
Soft Delete
這是一樣很實用的功能。之前跟朋友聊到,larry 自己也碰到過,有的客戶很喜歡把「已完成」的訂單刪掉 (即使已經做了 archive 頁面)。有經驗的朋友就知道,不要真把已完成的訂單刪啦,要是之後有任何客人/用戶有問題怎麼辦,要去 trace DB 嗎?
Laravel 把 soft delete 的功能包裝的滿好的,在你的 migration file 加上:
$table->softDeletes();
ORM class 加上
use SoftDeletes; // 寫在 class 裡, 它是 trait
protected $dates = ['deleted_at'];
$table-> sodtDeletes() 會在你的 table 加上 deleted_at 欄位 (type timestamp), 當使用者刪除這個 instance 的時候, deleted_at 會自動填上時間。當你下次 query 這筆資料,就會 query 不到 (然而這筆資料還在 DB)。如果你要 query soft deleted 的資料:
$flights = Flight::withTrashed()->where('your_query_attr', 'your_query_value')->get();
或是
$flights = Flight::onlyTrashed()->where('your_query_attr', 'your_query_value')->get();
用 restore method 可以回復 soft deleted item (用上方 withTrashed / onlyTrashed method 拿到 instance):
$flight->restore();
如果你要真正刪除:
$flight->forceDelete();
Local Scopes
local scope 也是實現商業邏輯很好的實作。在 ORM class 加上
public function scopePopular($query) {
return $query->where('votes', '>', 100);
}
caller side (拿掉 “scope”, “popular” 的 “p” 小寫)
$flights = Flight::popular()->get();
local scope function 也可以吃參數,就不在這邊重複了。
Events
很簡單,在 AppServiceProvider::boot 加上 (updated 是舉例,可以是 created、saved、等)
Flight::updated(function ($flight) {
// your callback code
});
比較漂亮的做法是用 observer class (observer pattern), 在 App/Observers 加上 FlightObserver.php
class FlightObserver {
public function updated(Flight $flight) {
// your callback code
}
}
AppServiceProvider::boot 只要加上一行
Flight::observe(FlightObserver::class);
這樣就可以了, 不過要注意像 updated,如果 update 的資料跟原資料相同,updated event「不會」觸發。
Relationships: one to one
例如 User 與 Flight 是一對一,在 class User 裡加上
public function flight() {
return $this->hasOne('AppFlight');
}
上面假設 flights table 裡已經有 user_id 欄位,如果你不想更動 flights table,可以使用
public function flight() {
return $this->hasOne('AppFlight', 'your_foreign_key');
}
指定你要的 foreign key。生成時:
$request->user()->flight()->create([
'name' => 'China Airlines',
]);
上例是 massive assignment,注意你的 class Flight 有無設定 $guarded。有 user instance 要取出 flight 時:
$flight = $user->flight; // 注意 flight 沒有括號, laravel 稱此為 "Dynamic Property"
反向,如果有 flight instance 要取出對應的 user, 在 class Flight 加上
public function user() {
return $this->belongsTo('AppUser'); // 一樣第二個 argument 可以是 your_foreign_key
}
caller side
$user = $flight->user; // 注意 user 一樣沒有括號
Relationships: one to many
public function flights() {
return $this->hasMany('AppFlight');
}
caller side
$flights = $user->flights;
反向一樣是 belongsTo。
Eager Loading
在使用 dynamic property 時有可能會:
$books = AppBook::all();
foreach ($books as $book) {
echo $book->author->name;
}
author 是 dynamic property,所以每 loop 依次就等於下一次 sql query,會有效能上的問題 (dynamic property 不真是 property,它是一個 sql query)。使用 Eager Loading 可以改寫成
$books = AppBook::with('author')->get(); // sql query 在這邊
foreach ($books as $book) {
echo $book->author->name; // author 就是真 property 了
}
Inserting & Updating Related Models
在一對多的情況 (其實也包含一對一的情況),使用 save method 可以新增一筆 child data:
$comment = new AppComment(['message' => 'A new comment.']);
$post = AppPost::find(1);
$post->comments()->save($comment);
注意這邊的 save method 與單純新增一筆資料有一點不一樣,單純新增一筆 comment
$comment = new AppComment(['message' => 'A new comment.']);
$comment-> save();
單純新增一筆 comment 時 $comment 物件為 save method 的「caller」,relation save 時 $comment 物件為 save method 的「callee」。另外,也可以用 massive assignment create
$post = AppPost::find(1);
$comment = $post->comments()->create([
'message' => 'A new comment.',
]);
在 Belongs-To 關係中如果要更新 child model 的 foreign key:
$account = AppAccount::find(10);
$user->account()->associate($account);
$user->save();
注意這例子中 user「belongs to」account,更新的是 user 的 foreign key,不是非常直覺。如果要將 child model 的 foreign key 設為 null:
$user->account()->dissociate();
$user->save();
注意 child model 的 foreign key 要是 nullable,不然會 error。
Touching Parent Timestamps
如果你要在更新 child model 時同時更新 parent model 的 updated_at 時間,在 child model 中加上一行
protected $touches = ['post'];
這樣在更新 comment 時就會更新 post 的 updated_at 時間。
商業,創業,美食,葡萄酒,閱讀,網路科技。