本節將使用使用者建立博文來學習資料模型關聯的有關知識。
一、Laravel常用知識總結
1.提示資訊漢化
如果用Laravel原生的表單提示錯誤資訊,則是英文的,如果需要中文,則需要修改resources/lang/en
的英文,這樣比較麻煩,不過,我們可以使用github開源的漢化包,然後引入該資料夾,在config/app.php
配置檔案裡邊修改語言包引入即可使用。
2.Carbon日期友好化處理
<span class="timestamp">
{{ $status->created_at->diffForHumans() }}
</span>
該方法的作用是將日期進行友好化處理,我們可以使用 tinker 來檢視該方法的具體輸出情況。
$ php artisan tinker
在 tinker 中輸出第一位使用者的建立時間如下。
>>> $created_at = AppModelsUser::first()->created_at
=> CarbonCarbon {#704
+"date": "1998-12-06 03:15:31.000000",
+"timezone_type": 3,
+"timezone": "UTC",
}
在 tinker 中呼叫 diffForHumans 方法來輸出,結果如下。
>>> $created_at->diffForHumans()
=> "17 years ago"
我們發現 diffForHumans 為我們生成的時間是英文的,如果要使用中文時間,則需要對 Carbon 進行本地化設定。Carbon 是 PHP DateTime 的一個簡單擴充套件,Laravel 將其預設整合到了框架中。
三、Eloquent:關聯
資料表之間經常會互相進行關聯。例如,一篇部落格文章可能會有多條評論,或是一張訂單可能對應一個下單客戶。Eloquent
讓管理和處理這些關聯變得很容易,同時也支援多種型別的關聯。
定義關聯
你可在 Eloquent 模型類內將 Eloquent 關聯定義為函式。因為關聯像 Eloquent 模型一樣也可以作為強大的 查詢語句構造器
(資料庫:查詢構造器),定義關聯為函式提供了強而有力的鏈式呼叫及查詢功能。例如:
$user->posts()->where(`active`, 1)->get();
不過,在深入瞭解使用關聯之前,先讓我們來學習如何定義每個型別:
1.一對一
一對一關聯是很基本的關聯。例如一個 User 模型也許會對應一個 Phone。要定義這種關聯,我們必須將 phone 方法放置於 User 模型上。phone 方法應該要返回基類 Eloquent 上的 hasOne
方法的結果:
<?php
namespace App;
use IlluminateDatabaseEloquentModel;
class User extends Model
{
/**
* 獲取與指定使用者互相關聯的電話紀錄。
*/
public function phone()
{
return $this->hasOne(`AppPhone`);
}
}
傳到 hasOne 方法裡的第一個引數是關聯模型的類名稱。定義好關聯之後,我們就可以使用 Eloquent 的動態屬性
來獲取關聯紀錄。動態屬性
讓你能夠訪問關聯函式,就像他們是在模型中定義的屬性:
$phone = User::find(1)->phone;
Eloquent 會假設對應關聯的外來鍵名稱是基於模型名稱的。在這個例子裡,它會自動假設 Phone 模型擁有 user_id 外來鍵。如果你想要重寫這個約定,則可以傳入第二個引數到 hasOne 方法裡。
return $this->hasOne(`AppPhone`, `foreign_key`);
此外,Eloquent 的預設外來鍵在上層模型的 id 欄位會有個對應值。換句話說,Eloquent 會尋找使用者的 id
欄位與 Phone
模型的 user_id
欄位的值相同的紀錄。如果你想讓關聯使用 id 以外的值,則可以傳遞第三個引數至 hasOne 方法來指定你自定義的鍵:
return $this->hasOne(`AppPhone`, `foreign_key`, `local_key`);
定義相對的關聯
所以,我們可以從 User 訪問到 Phone 模型。現在,讓我們在 Phone 模型上定義一個關聯,此關聯能夠讓我們訪問擁有此電話的 User。我們可以定義與 hasOne
關聯相對應的 belongsTo
方法:
<?php
namespace App;
use IlluminateDatabaseEloquentModel;
class Phone extends Model
{
/**
* 獲取擁有此電話的使用者。
*/
public function user()
{
return $this->belongsTo(`AppUser`);
}
}
2. 一對多
一個「一對多」關聯使用於定義單個模型擁有任意數量的其它關聯模型。例如,一篇部落格文章可能會有無限多個評論。就像其它的 Eloquent 關聯一樣,可以通過放置一個函式到 Eloquent 模型上來定義一對多關聯:
<?php
namespace App;
use IlluminateDatabaseEloquentModel;
class Post extends Model
{
/**
* 獲取部落格文章的評論。
*/
public function comments()
{
return $this->hasMany(`AppComment`);
}
}
切記,Eloquent 會自動判斷 Comment
模型上正確的外來鍵欄位。按約定來說,Eloquent 會取用自身模型的「蛇形命名」後的名稱,並在後方加上 _id
。所以,以此例來說,Eloquent 會假設 Comment 模型的外來鍵是 post_id
。
一旦關聯被定義,則可以通過 comments 屬性來訪問評論的集合。切記,因為 Eloquent 提供了「動態屬性」,因此我們可以對關聯函式進行訪問,就像他們是在模型中定義的屬性一樣:
$comments = AppPost::find(1)->comments;
foreach ($comments as $comment) {
//
}
定義相對的關聯
現在我們已經能訪問到所有文章的評論,讓我們來接著定義一個通過評論訪問上層文章的關聯。若要定義相對於 hasMany
的關聯,可在下層模型定義一個叫做 belongsTo
方法的關聯函式:
<?php
namespace App;
use IlluminateDatabaseEloquentModel;
class Comment extends Model
{
/**
* 獲取擁有此評論的文章。
*/
public function post()
{
return $this->belongsTo(`AppPost`);
}
}
3. 多對多
多對多關聯要稍微比 hasOne
及 hasMany
關聯複雜。如一個使用者可能擁有多種身份,而一種身份能同時被多個使用者擁有。舉例來說,很多使用者都擁有「管理者」的身份。要定義這種關聯,需要使用三個資料表:users
、roles
和 role_user
。role_user 表命名是以相關聯的兩個模型資料表來依照字母順序命名,幷包含了 user_id 和 role_id 欄位。
多對多關聯通過編寫一個在自身 Eloquent 類呼叫的 belongsToMany
的方法來定義。舉個例子,讓我們在 User 模型定義 roles 方法:
<?php
namespace App;
use IlluminateDatabaseEloquentModel;
class User extends Model
{
/**
* 屬於該使用者的身份。
*/
public function roles()
{
return $this->belongsToMany(`AppRole`);
}
}
定義相對的關聯
要定義相對於多對多的關聯,只需簡單的放置另一個名為 belongsToMany
的方法到你關聯的模型上。讓我們接著以使用者身份為例,在 Role 模型中定義 users 方法:
<?php
namespace App;
use IlluminateDatabaseEloquentModel;
class Role extends Model
{
/**
* 屬於該身份的使用者。
*/
public function users()
{
return $this->belongsToMany(`AppUser`);
}
}
如你所見,此定義除了簡單的參考 AppUser 模型外,與 User 的對應完全相同。因為我們重複使用了 belongsToMany
方法,當定義相對於多對多的關聯時,所有常用的自定義資料表與鍵的選項都是可用的。
關聯總結
1.一對一
$this->hasOne(`AppPhone`);
$this->belongsTo(`AppUser`);
hasOne
關聯相對應的 belongsTo
方法
2.一對多
$this->hasMany(`AppComment`);
$this->belongsTo(`AppPost`);
hasMany
關聯相對應的 belongsTo
方法
3.多對多
$this->belongsToMany(`AppRole`);
$this->belongsToMany(`AppUser`);
belongsToMany
關聯相對應的 belongsToMany
方法
4.查詢關聯
你可以查詢 posts 關聯並增加額外的條件至關聯,像這樣:
$user = AppUser::find(1);
$user->posts()->where(`active`, 1)->get();
關聯方法與動態屬性
如果你不需要增加額外的條件至 Eloquent 的關聯查詢,則可以簡單的像訪問屬性一樣來訪問關聯。例如我們剛剛的 User 及 Post 模型示例,我們可以像這樣來訪問所有使用者的文章:
$user = AppUser::find(1);
foreach ($user->posts as $post) {
//
}
預載入
當通過屬性訪問 Eloquent 關聯時,該關聯資料會被「延遲載入」。意味著該關聯資料只有在你使用屬性訪問它時才會被載入。不過,Eloquent 可以在你查詢上層模型時「預載入」關聯資料。預載入避免了 N + 1 查詢的問題。要說明 N + 1 查詢的問題,可試想一個關聯到 Author 的 Book 模型,如下所示:
<?php
namespace App;
use IlluminateDatabaseEloquentModel;
class Book extends Model
{
/**
* 獲取編寫該書的作者。
*/
public function author()
{
return $this->belongsTo(`AppAuthor`);
}
}
現在,讓我們來獲取所有書籍及其作者的資料:
$books = AppBook::all();
foreach ($books as $book) {
echo $book->author->name;
}
上方的迴圈會執行一次查詢並取回所有資料表上的書籍,接著每本書會執行一次查詢作者的操作。因此,若存在著 25 本書,則迴圈就會執行 26 次查詢:1 次是查詢所有書籍,其它 25 次則是在查詢每本書的作者。
很幸運地,我們可以使用預載入來將查詢的操作減少至 2 次。可在查詢時使用 with 方法來指定想要預載入的關聯資料:
$books = AppBook::with(`author`)->get();
foreach ($books as $book) {
echo $book->author->name;
}
對於該操作則只會執行兩次查詢:
select * from books
select * from authors where id in (1, 2, 3, 4, 5, ...)