欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

laravel學(xué)習(xí)教程之關(guān)聯(lián)模型

 更新時間:2016年07月30日 14:55:19   投稿:daisy  
在PHP開發(fā)中,選擇合適的框架有助于加快軟件開發(fā),節(jié)約寶貴的項目時間,讓開發(fā)者專注于功能的實現(xiàn)上。今天我們來學(xué)習(xí)學(xué)習(xí)laravel中的關(guān)聯(lián)模型。

Eloquent: 關(guān)聯(lián)模型

簡介

數(shù)據(jù)庫中的表經(jīng)常性的關(guān)聯(lián)其它的表。比如,一個博客文章可以有很多的評論,或者一個訂單會關(guān)聯(lián)一個用戶。Eloquent 使管理和協(xié)作這些關(guān)系變的非常的容易,并且支持多種不同類型的關(guān)聯(lián):

    一對一

    一對多

    多對多

    遠程一對多

    多態(tài)關(guān)聯(lián)

    多態(tài)多對多關(guān)聯(lián)

定義關(guān)聯(lián)

Eloquent 關(guān)聯(lián)可以像定義方法一樣在 Eloquent 模型類中進行定義。同時,它就像 Eloquent 模型自身一樣也提供了強大的查詢生成器。這允許關(guān)聯(lián)模型可以鏈式的執(zhí)行查詢能力。比如:

$user->posts()->where('active', 1)->get();

但是,在更深入的使用關(guān)聯(lián)之前,讓我們先來學(xué)習(xí)一下如何定義各種類型的關(guān)聯(lián)。

一對一

一對一的關(guān)聯(lián)是最基礎(chǔ)的關(guān)聯(lián)。比如,一個 User 模型可能關(guān)聯(lián)一個 Phone。我們需要在 User 模型上放置一個 phone 方法來定義這種關(guān)聯(lián)。phone 方法應(yīng)該返回一個基類 Eloquent 模型上 hasOne 方法的結(jié)果:

<?php

namespace 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 方法的第一個參數(shù)應(yīng)該是關(guān)聯(lián)模型的名稱。一旦關(guān)聯(lián)被定義完成,我們可以使用 Eloquent 的動態(tài)屬性來訪問關(guān)聯(lián)模型的記錄。動態(tài)屬性允許你訪問關(guān)聯(lián)函數(shù),就像是它們是定義在模型中的屬性一樣:

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

Eloquent 假定所關(guān)聯(lián)的外鍵是基于模型的名稱的。在這個前提下,Phone 模型會自動的假定其擁有一個 user_id 外鍵。如果你希望修改這個慣例,你可以傳遞第二個參數(shù)到 hasOne 方法中:

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

另外,Eloquent 也會假定外鍵應(yīng)該在其上層模型上擁有一個匹配的 id(或者自定義的 $primaryKey)值。換句話說,Eloquent 會查詢 Phone 記錄中的 user_id 列所對應(yīng)的用戶的 id 列的記錄。如果你希望關(guān)聯(lián)使用 id 以外的值,你可以傳遞第三個參數(shù)到 hasOne 方法來指定自定義的鍵:

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

定義相對的關(guān)聯(lián)

那么,我們可以從我們的 User 中訪問 Phone 模型?,F(xiàn)在,讓我們在 Phone 模型上定義一個關(guān)聯(lián),讓我們可以從 Phone 模型中訪問其所屬的 User。我們使用 belongsTo 方法來定義 hasOne 相對的關(guān)聯(lián):

<?php

namespace 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');
 }
}

在上面的例子中,Eloquent 將會嘗試從 Phone 模型中的 user_id 字段中匹配查找 id 相同的 User。Eloquent 會依據(jù)所關(guān)聯(lián)的模型的蛇形命名和 _id 來假定默認的外鍵名。事實上,如果在 Phone 模型上的外鍵不是 user_id,那么你可以傳遞自定義的外鍵名到 belongsTo 方法的第二個參數(shù):

/**
 * Get the user that owns the phone.
 */
public function user()
{
 return $this->belongsTo('App\User', 'foreign_key');
}

如果你的上級模型并沒有使用 id 作為主鍵名,或者你希望下級模型關(guān)聯(lián)一個不同的列。你可以傳遞第三個參數(shù)到 belongsTo 方法來指定上級模型表中的自定義鍵:

/**
 * Get the user that owns the phone.
 */
public function user()
{
 return $this->belongsTo('App\User', 'foreign_key', 'other_key');
}

一對多

一個一對多的關(guān)聯(lián)常常用來定義一個模型擁有其他任意數(shù)目的模型。比如,一個博客文章可以擁有很多條評論。就像其他的 Eloquent 關(guān)聯(lián)一樣,一對多關(guān)聯(lián)在 Eloquent 模型中通過方法來進行定義:

<?php

namespace 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');
 }
}

記住,Eloquent 會自動的根據(jù) Comment 模型來判斷合適的外鍵。依據(jù)慣例,Eloquent 會使用自身模型的蛇形命名和 _id 來作為外鍵。所以,在這個例子中,Eloquent 會假定 Comment 模型的外鍵是 post_id

一旦關(guān)聯(lián)定義完成之后,我們可以通過 comments 屬性來訪問所有關(guān)聯(lián)的評論的集合。記住,由于 Eloquent 提供了動態(tài)屬性,我們可以對關(guān)聯(lián)函數(shù)進行訪問,就像他們是在模型中定義的屬性一樣:

$comments = App\Post::find(1)->comments;

foreach ($comments as $comment) {
 //
}

當然,由于所有的關(guān)聯(lián)都提供了查詢生成器的功能,所以你可以在調(diào)用 comments 方法時繼續(xù)的添加一些限制條件,你可以通過鏈式的調(diào)用進行查詢條件的添加:

$comments = App\Post::find(1)->comments()->where('title', 'foo')->first();

就像 hasOne 方法,你可以通過添加額外的參數(shù)到 hasMany 方法中來重置外鍵和主鍵:

return $this->hasMany('App\Comment', 'foreign_key');

return $this->hasMany('App\Comment', 'foreign_key', 'local_key');

定義相對的關(guān)聯(lián)

現(xiàn)在我們可以訪問文章中所有的評論了,讓我們?yōu)樵u論定義一個關(guān)聯(lián)使其可以訪問它的上層文章模型。為了定義一個 hasMany 相對的關(guān)聯(lián),你需要在下層模型中定義一個關(guān)聯(lián)方法并調(diào)用 belongsTo 方法:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
 /**
  * Get the post that owns the comment.
  */
 public function post()
 {
  return $this->belongsTo('App\Post');
 }
}

一旦關(guān)聯(lián)被定義完成,我們就可以通過 Comment 模型的 post 動態(tài)屬性來檢索到其對應(yīng)的 Post 模型:

$comment = App\Comment::find(1);

echo $comment->post->title;

在上面的例子中,Eloquent 會嘗試從 Comment 模型中的 post_id 字段檢索與其相對應(yīng) id Post 模型。Eloquent 會使用關(guān)聯(lián)模型的蛇形命名和 _id 來作為默認的外鍵。如果 Comment 模型的外鍵不是 post_id,你可以傳遞一個自定義的鍵名到 belongsTo 方法的第二個參數(shù):

/**
 * Get the post that owns the comment.
 */
public function post()
{
 return $this->belongsTo('App\Post', 'foreign_key');
}

如果上層模型并沒有使用 id 作為主鍵,或者你想在下層模型中關(guān)聯(lián)其他的列,你可以傳遞第三個參數(shù)到 belongsTo 方法中:

/**
 * Get the post that owns the comment.
 */
public function post()
{
 return $this->belongsTo('App\Post', 'foreign_key', 'other_key');
}

多對多

多對多的關(guān)聯(lián)比 hasOne hasMany 關(guān)聯(lián)要稍微復(fù)雜一些。假如一個用戶擁有多個角色,而角色又可以被其他的用戶所共享。比如,多個用戶可以擁有管理員的角色。如果定義這種關(guān)聯(lián),我們需要定義三個數(shù)據(jù)庫表:users,roles,和 role_userrole_user 表的命名是以相關(guān)聯(lián)的兩個模型數(shù)據(jù)表來依照字母順序命名,并且表中包含了 user_id role_id 列。

多對多關(guān)聯(lián)需要編寫一個方法調(diào)用基礎(chǔ) Eloquent belongsToMany 方法。比如,讓我們在 User 模型中定義一個 roles 方法:

<?php

namespace 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');
 }
}

一旦關(guān)聯(lián)被定義,你可以通過 roles 動態(tài)屬性來訪問用戶的角色:

$user = App\User::find(1);

foreach ($user->roles as $role) {
 //
}

當然,就像其他類型的關(guān)聯(lián),你可以調(diào)用 roles 方法并且鏈式調(diào)用查詢條件:

$roles = App\User::find(1)->roles()->orderBy('name')->get();

就如先前所提到的,Eloquent 會合并兩個關(guān)聯(lián)模型并依照字母順序進行命名。當然你也可以隨意的重寫這個約定,你可以傳遞第二個參數(shù)到 belongsToMany 方法:

return $this->belongsToMany('App\Role', 'role_user');

除了自定義合并數(shù)據(jù)表的名稱之外,你也可以通過往 belongsToMany 方法傳傳遞額外參數(shù)來自定義數(shù)據(jù)表里的鍵的字段名稱。第三個參數(shù)是你定義在關(guān)聯(lián)中模型外鍵的名稱。第四個參數(shù)則是你要合并的模型外鍵的名稱:

return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');

定義相對關(guān)聯(lián)

你只需要在相對應(yīng)的關(guān)聯(lián)模型里放置其他的方法來調(diào)用 belongsToMany 方法就可以定義相對關(guān)聯(lián)。繼續(xù)我們上面的用戶角色示例,讓我們在 Role 模型中定義一個 users 方法:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Role extends Model
{
 /**
  * The users that belongs to the role.
  */
 public function users()
 {
  return $this->belongsToMany('App\User');
 }
}

就如你所看到的,這個關(guān)聯(lián)的定義與用戶的關(guān)聯(lián)定義完全相同。因為我們重復(fù)的使用了 belongsToMany 方法,當定義相對于多對多的關(guān)聯(lián)時,所有常用的自定義數(shù)據(jù)表和鍵的選項都是可用的。

檢索中間表字段

正如你已經(jīng)了解到的。定義多對多的關(guān)聯(lián)需要引入一個中間表。Eloquent 提供了幾種非常有幫助的方式來與這個表進行交互。比如,讓我們假定我們的 User 對象關(guān)聯(lián)到了很多 Role 對象。在訪問這些關(guān)聯(lián)對象時,我們可以通過在模型上使用 pivot 屬性來訪問中間表:

$user = App\User::find(1);

foreach ($user->roles as $role) {
 echo $role->pivot->created_at;
}

注意我們?nèi)〕龅拿總€ Role 對象,都會被自動的分配 pivot 屬性。這個屬性包含了一個代表中間表的模型,并且可以像其他 Eloquent 模型一樣被使用。

默認的,只有模型的鍵會被 pivot 對象提供,如果你的中間表包含了額外的屬性,你必須在定義關(guān)聯(lián)時指定它們:

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

如果你想要中間表自動維護 created_at updated_at 時間戳,你可以在定義關(guān)聯(lián)時使用 withTimestamps 方法:

return $this->belongsToMany('App\Role')->withTimestamps();

通過中間表字段過濾關(guān)系

你可以通過在定義關(guān)聯(lián)時使用 wherePrivot wherePivotIn 方法來在返回的結(jié)果中進行過濾:

return $this->belongsToMany('App\Role')->wherePivot('approved', 1);

return $this->belongsToMany('App\Role')->wherePivotIn('approved', [1, 2]);

遠程一對多

遠程一對多關(guān)聯(lián)提供了簡短便捷的方法通過中間關(guān)聯(lián)件來訪問遠端的關(guān)聯(lián)。比如,一個 Country 模型應(yīng)該通過 User 模型可以擁有很多的 Post 模型。在這個例子中,你可以非常容易的就檢索出一個國家中的所有的文章。讓我們來看一下定義這些關(guān)聯(lián)所需要的表:

countries
 id - integer
 name - string

users
 id - integer
 country_id - integer
 name - string

posts
 id - integer
 user_id - integer
 title - string

遠端的 posts 并沒有包含 country_id 列,hasManyThrough 關(guān)聯(lián)可以通過 $country->posts 來訪問一個國家的文章。為了執(zhí)行這個查詢,Eloquent 會通過中間表 users country_id 來檢索 posts 表中用戶 ID 相匹配的記錄。

現(xiàn)在我們已經(jīng)明確了關(guān)聯(lián)表的結(jié)構(gòu),那么讓我們來在 Country 模型上定義關(guān)聯(lián):

<?php

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');
 }
}

傳遞到 hasManyThrough 方法的第一個參數(shù)是我們最終想要訪問到的模型,而第二個參數(shù)則是中間層的模型名稱。

當使用關(guān)聯(lián)查詢時,通常 Eloquent 會遵循外鍵約定。如果你希望對關(guān)聯(lián)的鍵進行自定義,你可以傳遞第三和第四個參數(shù)到 hasManyThrough 方法。第三個參數(shù)是中間層模型的外鍵名稱,第四個參數(shù)是最終想要獲取的模型中的所對應(yīng)的中間層的外鍵, 而第五個參數(shù)則是當前模型的主鍵:

class Country extends Model
{
 public function posts()
 {
  return $this->hasManyThrough(
   'App\Post', 'App\User',
   'country_id', 'user_id', 'id'
  );
 }
}

多態(tài)關(guān)聯(lián)

表結(jié)構(gòu)

多態(tài)關(guān)聯(lián)允許一個模型在單個關(guān)聯(lián)中從屬一個或多個其它模型。比如,想象一下應(yīng)用中的用戶可以喜歡文章及其評論。如果使用多態(tài)關(guān)聯(lián),那么你就可以使用一個單獨的 likes 表來關(guān)聯(lián)這兩個場景。首先,讓我們確定定義這種關(guān)聯(lián)所需要的表結(jié)構(gòu):

posts
 id - integer
 title - string
 body - text

comments
 id - integer
 post_id - integer
 body - text

likes
 id - integer
 likeable_id - integer
 likeable_type - string

你需要注意到的兩個在 likes 表中重要的字段 likeable_id likeable_type。likeable_id 字段會包含文章或者評論的 ID 值,而 likeable_type 字段會包含其所屬的模型的類名。likeable_type 就是當訪問 likeable 關(guān)聯(lián)時 ORM 用來判斷所屬的模型是哪個類型。

模型結(jié)構(gòu)

接著,讓我們檢查一下這個關(guān)聯(lián)所需要的模型定義:

<?php

namespace 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 post'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');
 }
}

獲取多態(tài)關(guān)聯(lián)

一旦數(shù)據(jù)庫表和模型都定義完成,你就可以在你的模型中訪問這些關(guān)聯(lián)。比如,你可以使用 likes 動態(tài)屬性來訪問文章中所有關(guān)聯(lián)的 likes 模型:

$post = App\Post::find(1);

foreach ($post->likes as $like) {
 //
}

你也可以通過在模型上調(diào)用提供 morphTo 的方法來獲取多態(tài)模型其關(guān)系所有者。在上面的例子中,指的就是 Like 模型中的 likeable 方法。所以,我們可以像使用動態(tài)屬性一樣使用方法來進行訪問:

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

$likeable = $like->likeable;

Like 模型的 likeable 關(guān)聯(lián)將會返回一個 Post 或者 Comment 實例,這取決于其所屬者的類型。

自定義多態(tài)類型

默認的,Laravel 會使用包完全限定類名來存儲所關(guān)聯(lián)模型的類型。比如,上面的例子中 Like 可以屬于 Post 或者 Comment。默認的 likeable_type 應(yīng)該是 App\Post 或者 App\Comment。事實上,你可能希望從你的應(yīng)用程序的內(nèi)部結(jié)構(gòu)分離數(shù)據(jù)庫。在這個例子中,你可以定義一個關(guān)聯(lián)的多態(tài)映射來指導(dǎo) Eloquent 使用模型關(guān)聯(lián)的表名稱來替代類名:

use Illuminate\Database\Eloquent\Relations\Relation;

Relation::morphMap([
 App\Post::class,
 App\Comment::class,
]);

或者,你可以指定一個自定的字符串與每個模型進行關(guān)聯(lián):

use Illuminate\Database\Eloquent\Relations\Relation;

Relation::morphMap([
 'posts' => App\Post::class,
 'likes' => App\Like::class,
]);

你可以在你的 AppServiceProvider 或者一個分離的服務(wù)提供者的 boot 方法中注冊你的 morphMap。

多態(tài)多對多關(guān)聯(lián)

表結(jié)構(gòu)

除了傳統(tǒng)的多態(tài)關(guān)聯(lián),你也可以定義多對多的多態(tài)關(guān)聯(lián)。比如,一個博客的 Post Video 模型應(yīng)該可以共享一個多態(tài)關(guān)聯(lián)的 Tag 模型。使用多對多的多態(tài)關(guān)聯(lián)可以允許你的博客文章和視頻能夠共享獨特標簽的單個列表。首先,讓我們來看一下表結(jié)構(gòu):

posts
 id - integer
 name - string

videos
 id - integer
 name - string

tags
 id - integer
 name - string

taggables
 tag_id - integer
 taggable_id - integer
 taggable_type - string

模型結(jié)構(gòu)

接著,我們來定義模型中的關(guān)聯(lián)。Post Video 模型將都會包含調(diào)用基礎(chǔ) Eloquent 類的 morphToMany 方法的 tags 方法:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
 /**
  * Get all of the tags for the post.
  */
 public function tags()
 {
  return $this->morphToMany('App\Tag', 'taggable');
 }
}

定義相對的關(guān)聯(lián)

接著,在 Tag 模型中,你應(yīng)該為所有關(guān)聯(lián)模型定義相應(yīng)的方法。所以,在這個例子中,我們將定義 posts 方法和 videos 方法:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
 /**
  * Get all of the posts that are assigned this tag.
  */
 public function posts()
 {
  return $this->morphedByMany('App\Post', 'taggable');
 }

 /**
  * Get all of the videos that are assigned this tag.
  */
 public function videos()
 {
  return $this->morphedByMany('App\Video', 'taggable');
 }
}

獲取關(guān)聯(lián)

當定義完成數(shù)據(jù)表和模型之后,你就可以通過模型來訪問其關(guān)聯(lián)。比如,你可以簡單的使用 tags 動態(tài)屬性來訪問文章的所有標簽?zāi)P停?/p>

$post = App\Post::find(1);

foreach ($post->tags as $tag) {
 //
}

你也可以通過訪問模型中提供執(zhí)行 morphedByMany 方法的方法來獲取關(guān)聯(lián)模型的所屬模型。在上面的例子中,就是 Tag 模型上的 posts 或者 videos 方法。所以,你可以像動態(tài)屬性一樣訪問這些方法:

$tab = App\Tag::find(1);

foreach ($tag->videos as $video) {
 //
}

關(guān)聯(lián)查詢

由于所有的 Eloquent 關(guān)聯(lián)類型都是通過方法定義的,所以你可以調(diào)用這些方法來獲取所關(guān)聯(lián)的模型的實例而無需實際的執(zhí)行關(guān)聯(lián)查詢。另外,所有的 Eloquent 關(guān)聯(lián)也都提供了查詢生成器服務(wù),這允許你可以繼續(xù)的鏈式執(zhí)行查詢操作。

比如,想象一下博客系統(tǒng)中 User 模型擁有很多 Post 關(guān)聯(lián)的模型:

<?ph

namespace App;

use Illuminate\Database\Eloquent\Model;

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

你可以查詢 posts 關(guān)聯(lián)的同時添加一些額外的查詢約束:

$user = App\User::find(1);

$user->posts()->where('active', 1)->get();

你應(yīng)該注意到了,你可以在關(guān)聯(lián)中使用任何的查詢生成器的方法。

關(guān)聯(lián)方法 Vs. 動態(tài)屬性

如果你不需要在進行 Eloquent 關(guān)聯(lián)查詢時添加額外的約束,你可以簡單的像它的屬性一樣進行訪問。比如,我們繼續(xù)使用 User Post 示例模型。我們可以像這樣來訪問用戶的所有文章:

$user = App\User::find(1);

foreach ($user->posts as $post) {
 //
}

動態(tài)屬性是惰性加載的,這意味著在你實際訪問他們之前,其關(guān)聯(lián)數(shù)據(jù)是不會加載的。正因為如此,開發(fā)的時候通常使用預(yù)加載來進行加載一些即將用到的關(guān)聯(lián)模型。預(yù)加載要求必須加載一個模型的關(guān)系,這有效的減少了查詢的次數(shù)。

查詢關(guān)聯(lián)是否存在

當訪問一個模型的記錄時,你可能會希望基于關(guān)聯(lián)的記錄是否存在來對結(jié)果進行限制。比如,想象一下你希望獲取最少有一條評論的博客文章。你可以傳遞關(guān)聯(lián)的名稱到 has 方法來做這些:

// Retrieve all posts that have at least one comment...
$posts = App\Post::has('comments')->get();

你也可以指定操作符,和數(shù)量來進一步定制查詢:

// Retrieve all posts that have three or more comments...
$posts = Post::has('comments', '>=', 3)->get();

你也可以使用 . 語法來構(gòu)造嵌套的 has 語句。比如,你可以獲取所有包含至少一條評論和投票的文章:

// Retrieve all posts that hava at least one comment with votes...
$posts = Post::has('comments.votes')->get();

如果你需要更高的控制,你可以使用 whereHas orWhereHas 方法來在 has 查詢中插入 where 子句。這些方法允許你為關(guān)聯(lián)進行自定義的約束查詢。比如檢查評論的內(nèi)容:

// Retrieve all posts with at least one comment containing words like foo%
$posts = Post::whereHas('comments', function ($query) {
 $query->where('content', 'like', 'foo%'); 
})->get();

統(tǒng)計關(guān)聯(lián)結(jié)果

如果你希望統(tǒng)計關(guān)聯(lián)的結(jié)果而不實際的加載它們,你可以使用 withCount 方法,這將在你的結(jié)果模型中添加 {relation}_count 列。比如:

$posts = App\Post::withCount('comments')->get();

foreach ($posts as $post) {
 echo $post->comments_count;
}

你也可以同時檢索多個關(guān)聯(lián)的統(tǒng)計,以及添加查詢約束:

$posts = Post::withCount(['votes', 'comments' => function ($query) {
 $query->where('content', 'like', 'foo%');
}])->get();

echo $posts[0]->votes_count;
echo $posts[0]->comments_count;

預(yù)加載

當通過屬性訪問 Eloquent 關(guān)聯(lián)時,該關(guān)聯(lián)的數(shù)據(jù)會被延遲加載。這意味著該關(guān)聯(lián)數(shù)據(jù)只有在你真實的訪問屬性時才會進行加載。事實上,Eloquent 可以在上層模型中一次性預(yù)加載的。預(yù)加載有效避免了 N + 1 的查找問題。要說明 N + 1 查找問題,我們可以來看一個 Author 關(guān)聯(lián) Book 的示例:

<?php

namespace 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');
 }
}

現(xiàn)在,讓我們檢索所有的書籍和他們的作者:

$books = App\Book::all();

foreach ($books as $book) {
 echo $book->author->name;
}

這個循環(huán)會執(zhí)行一次查找回所有的書籍,接著每本書會運行一次查找作者的操作。所以,如果我們擁有 25 本書,那么循環(huán)將會進行 26 次查詢:1 次查詢所有的書籍,25 次查詢相關(guān)書籍的作者。

非常幸運的,我們可以使用預(yù)加載來將查詢有效的控制在 2 次。當查詢時,使用 with 方法來指定關(guān)聯(lián)的預(yù)加載:

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

foreach ($books as $book) {
 echo $book->author->name;
}

對于這個操作,只會執(zhí)行兩個查詢:

select * from books

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

預(yù)加載多個關(guān)聯(lián)

有時候你可能需要在一個操作中預(yù)加載多個關(guān)聯(lián),你只需要傳遞額外的參數(shù)到 with 方法中就可以:

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

嵌套的預(yù)加載

你可以使用 . 語法來加載嵌套的關(guān)聯(lián)。比如,讓我們在一個 Eloquent 語句中一次加載所有書籍的作者以及作者的死人通訊簿:

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

預(yù)加載約束

有時候你可能希望預(yù)加載一些關(guān)聯(lián),但是也需要對預(yù)加載查詢指定額外的約束,這里有個示例:

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

在這個例子中,Eloquent 會值預(yù)加載文章的 title 列包含 first 單詞的記錄。當然,你也可以調(diào)用其他查詢生成器可用的方法:

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

延遲預(yù)加載

有時候你可能需要在上層模型被獲取后才預(yù)加載其關(guān)聯(lián)。當你需要來動態(tài)決定是否加載關(guān)聯(lián)模型時尤其有用:

$books = App\Book::all();

if ($someCondition) {
 $books->load('author', 'publisher');
}

如果你需要對預(yù)加載做一些查詢約束,你可以傳遞 Closure load 方法:

$books->load(['author' => function ($query) {
 $query->orderBy('published_date', 'asc');
}]);

插入關(guān)系模型

Save 方法

Eloquent 提供了方便的方法來為模型添加一個關(guān)聯(lián)。比如,也許你需要為 Post 模型新增一個 Comment。除了手動的設(shè)置 Comment post_id 屬性,你也可以直接在關(guān)聯(lián)模型中調(diào)用 save 方法來插入 Comment:

$comment = new App\Comment(['message' => 'A new comment.']);

$post = App\Post::find(1);

$post->comments()->save($comment);

注意上面我們并沒有使用關(guān)聯(lián)模型的動態(tài)屬性的方式來訪問 comments,而是使用 comments 方法的形式來獲取關(guān)聯(lián)模型的實例。save 方法會自動的添加相應(yīng)的 post_id 值到新的 Comment 模型上。

如果你需要一次添加多個關(guān)聯(lián)模型,你需要使用 saveMany 方法:

$post = App\Post::find(1);

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

Save & 多對多關(guān)聯(lián)

當與多對多關(guān)聯(lián)互動時,save 方法接收一個中間層表屬性的額外參數(shù)數(shù)組作為第二個參數(shù):

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

Create 方法

除了 save saveMany 方法之外,你也可以使用 create 方法,它可以接收屬性組成的數(shù)組,創(chuàng)建一個模型并且將其存儲到數(shù)據(jù)庫。這一次,savecreate 方法的區(qū)別是 save 接收一個完整的 Eloquent 模型實例,而 create 接收的是一個原生的 PHP array:

$post = App\Post::find(1);

$comment = $post->comments()->create([
 'message' => 'A new comment.',
]);

在使用 create 方法之前,你應(yīng)該確保已經(jīng)閱讀了屬性的 批量賦值文檔。

更新從屬關(guān)聯(lián)模型

當更新一個 belongsTo 關(guān)聯(lián)時,你應(yīng)該使用 associate 方法。這個方法會在下層模型中設(shè)置外鍵:

$account = App\Account::find(10);

$user->account()->associate($account);

$user->save();

當刪除 belongsTo 關(guān)聯(lián)時,你應(yīng)該使用 dissociate 方法,該方法會重置下層模型所關(guān)聯(lián)的外鍵:

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

$user->save();

多對多關(guān)聯(lián)

附加 / 抽離

當使用多對多關(guān)聯(lián)時,Eloquent 提供了一些額外的幫助方法來更方便的管理關(guān)聯(lián)模型。比如,讓我們想象一下用戶可以有很多角色并且角色可以有很多用戶。你可以使用 attach 方法來附加一個角色到用戶并且在中間表中加入這條記錄:

$user = App\User::find(1);

$user->roles()->attach($roleId);


當附加關(guān)聯(lián)到模型時,你也可以傳遞一個含有額外數(shù)據(jù)的數(shù)組來將其添加到中間表中:

$user->roles()->attach($roleId, ['expires' => $expires]);

當然,有時候你可能需要從用戶中刪除一個角色。你可以使用 detach 方法來刪除多對多關(guān)聯(lián)的記錄。datech 方法將從中間表中刪除相應(yīng)的記錄。但是,除了中間表,其它兩個模型的記錄都還會被保留:

// Detach a single role from the user...
$user->roles()->detach($roleId);

// Detach all roles from the user...
$user->roles()->detach();

為了更加的便捷,attachdetach 也可以接收 IDs 所組成的數(shù)組作為輸入:

$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 方法來構(gòu)建多對多的關(guān)聯(lián)。sync 方法接收放置中間表 IDs 所組成的數(shù)組。任意 IDs 如果沒有在所給定的數(shù)組中,那么其將會從中間表中進行刪除。所以,在操作完成之后,只有存在于給定數(shù)組里的 IDs 才會存在于中間表中:

$user->roles()->sync([1, 2, 3]);

你也可以同時傳遞額外的中間表的鍵值對:

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

聯(lián)動上層模型時間戳

當一個模型 belongsTo 或者 belongsToMany 另外一個模型時,比如 Comment 從屬于 Post,這對下層模型更新時同時要求更新上層模型的時間戳?xí)r很有幫助。比如,當 Comment 模型更新了,你想要自動的更新其所屬的 Post 模型的 updated_at 時間戳。Eloquent 使之變的非常容易。你只需要在下層模型中添加一個 touches 屬性來包含關(guān)聯(lián)的名稱就可以了:

<?php

namespace 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');
 }
}

現(xiàn)在,當你更新 Comment 時,其所屬的 Post 將會同時更新 updated_at 列:

$comment = App\Comment::find(1);

$comment->text = 'Edit to this comment!';

$comment->save();

以上就是laravel學(xué)習(xí)教程之關(guān)聯(lián)模型的全部內(nèi)容,希望對大家學(xué)習(xí)php有所幫助。

相關(guān)文章

最新評論