Laravel學習筆記七-建立部落格

Corwien發表於2019-02-16

本節將使用使用者建立博文來學習資料模型關聯的有關知識。

一、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. 多對多

多對多關聯要稍微比 hasOnehasMany 關聯複雜。如一個使用者可能擁有多種身份,而一種身份能同時被多個使用者擁有。舉例來說,很多使用者都擁有「管理者」的身份。要定義這種關聯,需要使用三個資料表:usersrolesrole_userrole_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, ...)

相關文章