Laravel架構-EloquentORM進階部分的詳細介紹

來源:互聯網
上載者:User

關聯關係

One To One
假設User模型關聯了Phone模型,要定義這樣一個關聯,需要在User模型中定義一個phone方法,該方法返回一個hasOne方法定義的關聯

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class User extends Model{    /**     * Get the phone record associated with the user.     */    public function phone()    {        return $this->hasOne('App\Phone');    }}

hasOne方法的第一個參數為要關聯的模型,定義好之後,可以使用下列文法查詢到關聯屬性了

$phone = User::find(1)->phone;

Eloquent會假定關聯的外鍵是基於模型名稱的,因此Phone模型會自動使用user_id欄位作為外鍵,可以使用第二個參數和第三個參數覆蓋

return $this->hasOne('App\Phone', 'foreign_key');return $this->hasOne('App\Phone', 'foreign_key', 'local_key');

定義反向關係

定義上述的模型之後,就可以使用User模型擷取Phone模型了,當然也可以通過Phone模型擷取所屬的User了,這就用到了belongsTo方法了

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Phone extends Model{    /**     * Get the user that owns the phone.     */    public function user()    {        return $this->belongsTo('App\User');        // return $this->belongsTo('App\User', 'foreign_key');        // return $this->belongsTo('App\User', 'foreign_key', 'other_key');    }}

One To Many

假設有一個文章,它有很多關聯的評論資訊,這種情況下應該使用一對多的關聯,使用hasMany方法

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Post extends Model{    /**     * Get the comments for the blog post.     */    public function comments()    {        return $this->hasMany('App\Comment');    }}

查詢操作

$comments = App\Post::find(1)->comments;foreach ($comments as $comment) {    //}$comments = App\Post::find(1)->comments()->where('title', 'foo')->first();

定義反向關聯

反向關聯也是使用belongsTo方法,參考One To One部分。

$comment = App\Comment::find(1);echo $comment->post->title;

Many To Many

多對多關聯因為多了一個中間表,實現起來比hasOne和hasMany複雜一些。

考慮這樣一個情境,使用者可以屬於多個角色,一個角色也可以屬於多個使用者。這就引入了三個表: users, roles, role_user。其中role_user表為關聯表,包含兩個欄位user_id和role_id。

多對多關聯需要使用belongsToMany方法

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class User extends Model{    /**     * The roles that belong to the user.     */    public function roles()    {        // 指定關聯表        // return $this->belongsToMany('App\Role', 'role_user');        // 指定關聯表,關聯欄位        // return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');        return $this->belongsToMany('App\Role');    }}

上述定義了一個使用者屬於多個角色,一旦該關係確立,就可以查詢了

user = App\User::find(1);foreach ($user->roles as $role) {    //}$roles = App\User::find(1)->roles()->orderBy('name')->get();

反向關聯關係

反向關係與正向關係實現一樣

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Role extends Model{    /**     * The users that belong to the role.     */    public function users()    {        return $this->belongsToMany('App\User');    }}

檢索中間表的列值

對多對多關係來說,引入了一個中間表,因此需要有方法能夠查詢到中間表的列值,比如關係確立的時間等,使用pivot屬性查詢中間表

$user = App\User::find(1);foreach ($user->roles as $role) {    echo $role->pivot->created_at;}

上述代碼訪問了中間表的created_at欄位。

注意的是,預設情況下之後模型的鍵可以通過pivot對象進行訪問,如果中間表包含了額外的屬性,在指定關聯關係的時候,需要使用withPivot方法明確的指定列名

return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');

Has Many Through

這種關係比較強大,假設這樣一個情境:Country模型下包含了多個User模型,而每個User模型又包含了多個Post模型,也就是說一個國家有很多使用者,而這些使用者都有很多文章,我們希望查詢某個國家的所有文章,怎麼實現呢,這就用到了Has Many Through關係

countries    id - integer    name - stringusers    id - integer    country_id - integer    name - stringposts    id - integer    user_id - integer    title - string

可以看到,posts表中並不直接包含country_id,但是它通過users表與countries表建立了關係

使用Has Many Through關係

namespace App;use Illuminate\Database\Eloquent\Model;class Country extends Model{    /**     * Get all of the posts for the country.     */    public function posts()    {        // return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id');        return $this->hasManyThrough('App\Post', 'App\User');    }}

方法hasManyThrough的第一個參數是我們希望訪問的模型名稱,第二個參數是中間模型名稱。

HasManyThrough hasManyThrough(     string $related,     string $through,     string|null $firstKey = null,     string|null $secondKey = null,     string|null $localKey = null)

Polymorphic Relations (多態關聯)

多態關聯使得同一個模型使用一個關聯就可以屬於多個不同的模型,假設這樣一個情境,我們有一個文章表和一個評論表,使用者既可以對文章執行喜歡操作,也可以對評論執行喜歡操作,這樣的情況下該怎麼處理呢?

表結構如下

posts    id - integer    title - string    body - textcomments    id - integer    post_id - integer    body - textlikes    id - integer    likeable_id - integer    likeable_type - string

可以看到,我們使用likes表中的likeable_type欄位判斷該記錄喜歡的是文章還是評論,表結構有了,接下來就該定義模型了

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Like extends Model{    /**     * Get all of the owning likeable models.     */    public function likeable()    {        return $this->morphTo();    }}class Post extends Model{    /**     * Get all of the product's likes.     */    public function likes()    {        return $this->morphMany('App\Like', 'likeable');    }}class Comment extends Model{    /**     * Get all of the comment's likes.     */    public function likes()    {        return $this->morphMany('App\Like', 'likeable');    }}

預設情況下,likeable_type的類型是關聯的模型的完整名稱,比如這裡就是App\Post和App\Comment。

通常情況下我們可能會使用自訂的值標識關聯的表名,因此,這就需要自訂這個值了,我們需要在項目的服務提供者對象的boot方法中註冊關聯關係,比如AppServiceProvider的boot方法中

use Illuminate\Database\Eloquent\Relations\Relation;Relation::morphMap([    'posts' => App\Post::class,    'likes' => App\Like::class,]);

檢索多態關係

訪問一個文章所有的喜歡

$post = App\Post::find(1);  foreach ($post->likes as $like) {    //}

訪問一個喜歡的文章或者評論

$like = App\Like::find(1);   $likeable = $like->likeable;

上面的例子中,返回的likeable會根據該記錄的類型返回文章或者評論。

多對多的多態關聯

多對多的關聯使用方法morphToMany和morphedByMany,這裡就不多廢話了

關聯關係查詢

在Eloquent中,所有的關係都是使用函數定義的,可以在不執行關聯查詢的情況下擷取關聯的執行個體。假設我們有一個部落格系統,User模型關聯了很多Post模型:

/** * Get all of the posts for the user. */public function posts(){   return $this->hasMany('App\Post');}

你可以像下面這樣查詢關聯並且添加額外的約束

$user = App\User::find(1);$user->posts()->where('active', 1)->get();

如果不需要對關聯的屬性添加約束,可以直接作為模型的屬性訪問,例如上面的例子,我們可以使用下面的方式訪問User的Post

$user = App\User::find(1);foreach ($user->posts as $post) {    //}

動態屬性都是消極式載入的,它們只有在被訪問的時候才會去查詢資料庫,與之對應的是預先載入,預先載入可以使用關聯查詢出所有資料,減少執行sql的數量。

查詢關係存在性

使用has方法可以基於關係的存在性返回結果

// 檢索至少有一個評論的所有文章...$posts = App\Post::has('comments')->get();// Retrieve all posts that have three or more comments...$posts = Post::has('comments', '>=', 3)->get();// Retrieve all posts that have at least one comment with votes...$posts = Post::has('comments.votes')->get();

如果需要更加強大的功能,可以使用whereHas和orWhereHas方法,把where條件放到has語句中。

// 檢索所有至少存在一個匹配foo%的評論的文章$posts = Post::whereHas('comments', function ($query) {    $query->where('content', 'like', 'foo%');})->get();

預先載入

在訪問Eloquent模型的時候,預設情況下所有的關聯關係都是消極式載入的,在使用的時候才會開始載入,這就造成了需要執行大量的sql的問題,使用預先載入功能可以使用關聯查詢出所有結果

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Book extends Model{    /**     * Get the author that wrote the book.     */    public function author()    {        return $this->belongsTo('App\Author');    }}

接下來我們檢索所有的書和他們的作者

$books = App\Book::all();foreach ($books as $book) {    echo $book->author->name;}

上面的查詢將會執行一個查詢查詢出所有的書,然後在遍曆的時候再執行N個查詢查詢出作者資訊,顯然這樣做是非常低效的,幸好我們還有預先載入功能,可以將這N+1個查詢減少到2個查詢,在查詢的時候,可以使用with方法指定哪個關係需要預先載入。

$books = App\Book::with('author')->get();foreach ($books as $book) {    echo $book->author->name;}

對於該操作,會執行下列兩個sql

select * from booksselect * from authors where id in (1, 2, 3, 4, 5, ...)

預先載入多個關係

$books = App\Book::with('author', 'publisher')->get();

嵌套的預先載入

$books = App\Book::with('author.contacts')->get();

帶約束的預先載入

$users = App\User::with(['posts' => function ($query) {    $query->where('title', 'like', '%first%');}])->get();$users = App\User::with(['posts' => function ($query) {    $query->orderBy('created_at', 'desc');}])->get();

延遲預先載入#

有時候,在上級模型已經檢索出來之後,可能會需要預先載入關聯資料,可以使用load方法

$books = App\Book::all();if ($someCondition) {    $books->load('author', 'publisher');}$books->load(['author' => function ($query) {    $query->orderBy('published_date', 'asc');}]);

關聯模型插入

save方法

儲存單個關聯模型

$comment = new App\Comment(['message' => 'A new comment.']);$post = App\Post::find(1);$post->comments()->save($comment);

儲存多個關聯模型

$post = App\Post::find(1); $post->comments()->saveMany([    new App\Comment(['message' => 'A new comment.']),    new App\Comment(['message' => 'Another comment.']),]);

save方法和多對多關聯

多對多關聯可以為save的第二個參數指定關聯表中的屬性

App\User::find(1)->roles()->save($role, ['expires' => $expires]);

上述代碼會更新中間表的expires欄位。

create方法

使用create方法與save方法的不同在於它是使用數組的形式建立關聯模型的

$post = App\Post::find(1);$comment = $post->comments()->create([    'message' => 'A new comment.',]);

更新 “Belongs To” 關係

更新belongsTo關係的時候,可以使用associate方法,該方法會設定子模型的外鍵

$account = App\Account::find(10);$user->account()->associate($account);$user->save();

要移除belongsTo關係的話,使用dissociate方法

$user->account()->dissociate();$user->save();

Many to Many 關係

中間表查詢條件#

當查詢時需要對使用中間表作為查詢條件時,可以使用wherePivot, wherePivotIn,orWherePivot,orWherePivotIn添加查詢條件。

$enterprise->with(['favorites' => function($query) {    $query->wherePivot('enterprise_id', '=', 12)->select('id');}]);

Attaching / Detaching#

$user = App\User::find(1);// 為使用者添加角色$user->roles()->attach($roleId);// 為使用者添加角色,更新中間表的expires欄位$user->roles()->attach($roleId, ['expires' => $expires]);// 移除使用者的單個角色$user->roles()->detach($roleId);// 移除使用者的所有角色$user->roles()->detach();

attach和detach方法支援數組參數,同時添加和移除多個

$user = App\User::find(1);$user->roles()->detach([1, 2, 3]);$user->roles()->attach([1 => ['expires' => $expires], 2, 3]);

更新中間表(關聯表)欄位

使用updateExistingPivot方法更新中間表

$user = App\User::find(1);$user->roles()->updateExistingPivot($roleId, $attributes);

同步中間表(同步關聯關係)#

使用sync方法,可以指定兩個模型之間只存在指定的關聯關係

$user->roles()->sync([1, 2, 3]);$user->roles()->sync([1 => ['expires' => true], 2, 3]);

上述兩個方法都會讓使用者只存在1,2,3三個角色,如果使用者之前存在其他角色,則會被刪除。

更新父模型的時間戳記#

假設情境如下,我們為一個文章增加了一個新的評論,我們希望這個時候文章的更新時間會相應的改變,這種行為在Eloquent中是非常容易實現的。

在子模型中使用$touches屬性實現該功能

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Comment extends Model{    /**     * All of the relationships to be touched.     *     * @var array     */    protected $touches = ['post'];    /**     * Get the post that the comment belongs to.     */    public function post()    {        return $this->belongsTo('App\Post');    }}

現在,更新評論的時候,文章的updated_at欄位也會被更新

$comment = App\Comment::find(1);$comment->text = 'Edit to this comment!';$comment->save();

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.