N+1 Problem
今天看document看到N+1 problem,基於laravel是lazy loading所以有可能因此寫出效率很差的sql指令,之前在玩laravel的時候就使用
/laravel-debugbar
透過這個工具發現,奇怪我的sql怎麼在我使用foeach的時候居然一筆一筆去作query
$books = App\Book::all();
foreach ($books as $book) {
echo $book->author->name;
}
就像這個範例一樣,假設我從book select * from book; 拿到 25本書
然後我再foreach的時候要從每一本書中拿出author的name,所以就會有所謂的n+1 problem
因為是lazy loading所以在foreach時,laravel會一筆一筆去作query ----> select name from author where book.id = author.id
因為連續25筆就會連續select 25次,這個假如是使用者多一點mysql不掛給你看才奇怪,
根本就是自己寫出來的DDOS了XDD
我原本還以為Eloquent怎麼會做成這樣子,還以為ORM居然只有這點程度。
直到翻到laravel Documentation才知道可以轉換成Eager loading
$books = App\Book::with('author')->get();
foreach ($books as $book) {
echo $book->author->name;
}
透過With告知Eloquent我之後會使用到author這個model。
select * from books
select * from authors where id in (1, 2, 3, 4, 5, ...)
他就會縮減成兩個sql而已,因此增加極大的效能,小地方不得不注意,!!!!!!
$books = App\Book::with(['author', 'publisher'])->get();
針對多重關係的eager loading
$books = App\Book::with('author.contacts')->get();
與上述相 同,使用 dot來比較好書寫。少打好幾個 ' 果然有artisan的感覺!!!
$users = App\Book::with('author:id,name')->get();
只select出特定的內容 ,在這裡只select出author的name, !!!!另外特別注意 要單純只挑幾個name出來必須要包含author:id 在開頭 !!!!!
$users = App\User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%');
}])->get();
增加限制條件!!
The limit
and take
query builder methods may not be used when constraining eager loads.
當有限制條件limit和take就沒辦法一起使用囉!!
$books = App\Book::all();
if ($someCondition) {
$books->load('author', 'publisher');
}
同樣是eager loading可是你可以選擇晚一點再Loading近來
有點像是你先select * from book;
之後在等$someCondition = true時,select * from author, publisher where book.id = .......
一樣是只有執行兩次!!!!
$books->load(['author' => function ($query) {
$query->orderBy('published_date', 'asc');
}]);
在lazy eager loading使用constraints.
public function format(Book $book)
{
$book->loadMissing('author');
return [
'name' => $book->name,
'author' => $book->author->name
];
}
loadMissing,先檢查有沒有load過,沒有才加載
$comment = new App\Comment(['message' => 'A new comment.']);
$post = App\Post::find(1);
$post->comments()->save($comment);
這個使用過php artisan tinker應該就知道概念了!!!
這比較特別一點是我把新增的comment丟到特定的post裡面,非常的有OOP。
$post = App\Post::find(1);
$post->comments()->saveMany([
new App\Comment(['message' => 'A new comment.']),
new App\Comment(['message' => 'Another comment.']),
]);
同時save多個comment 0.0
$post = App\Post::find(1);
$post->comments[0]->message = 'Message';
$post->comments[0]->author->name = 'Author Name';
$post->push();
push 可以save多個改過的資料。 狂!!
In addition to the save
and saveMany
methods, you may also use the create
method, which accepts an array of attributes, creates a model, and inserts it into the database. Again, the difference between save
and create
is that save
accepts a full Eloquent model instance while create
accepts a plain PHP array
:
$post = App\Post::find(1);
$comment = $post->comments()->create([
'message' => 'A new comment.',
]);
create只接受plain PHP array 可是save可以把Object直接存進去。
且create method不允許mass assignment,什麼是mass assignment,根據SO的說法
https://stackoverflow.com/questions/22279435/what-does-mass-assignment-mean-in-laravel
$user = new User(Input::all());
我們可以在laravel中直接使用array執行create,但是這樣有機率性被強制塞入一個field叫做 user_type = admin
而造成安全性問題,所以我們必須在Model中使用fillable array來限制說只有那些屬性是可以透過此方法 or 陣列來create User,
然而$guarded = ['price'] 則代表不接受 mass assignment,與fillable剛好相反。
而沒有被fillable的屬性只能使用$user->user_type = 'admin'
$user->save()來執行了,避免透過 user傳過來的惡意屬性攻擊。
留言列表