laravel Eloquent操做

基本用法

咱們先從創建一個 Eloquent 模型開始。模型一般放在 app 目錄下,可是您能夠將它們放在任何地方,只要能經過 composer.json 自動載入。全部的 Eloquent 模型都繼承於Illuminate\Database\Eloquent\Model 。php

定義一個 Eloquent 模型

class User extends Model {} 

你也能夠經過 make:model 命令自動生成 Eloquent 模型:數據庫

php artisan make:model User 

注意咱們並無告訴 Eloquent User 模型會使用哪一個數據庫表。若沒有特別指定,系統會默認自動對應名稱爲「類名稱的小寫複數形態」的數據庫表。因此,在上面的例子中, Eloquent 會假設 User 模型將把數據存在 users 數據庫表。您也能夠在類中定義table 屬性自定義要對應的數據庫表。json

class User extends Model { protected $table = 'my_users'; } 

注意: Eloquent 也會假設每一個數據庫表都有一個字段名稱爲 id 的主鍵。您能夠在類裏定義 primaryKey 屬性來重寫。一樣的,您也能夠定義 connection 屬性,指定模型鏈接到指定的數據庫鏈接。數組

定義好模型以後,您就能夠從數據庫表新增及獲取數據了。注意在默認狀況下,在數據庫表裏須要有 updated_at 和 created_at 兩個字段。若是您不想設定或自動更新這兩個字段,則將類裏的 $timestamps 屬性設爲 false便可。緩存

取出全部記錄

$users = User::all(); 

根據主鍵取出一條數據

$user = User::find(1); var_dump($user->name); 

提示: 全部查詢構造器裏的方法,查詢 Eloquent 模型時也可使用。安全

根據主鍵取出一條數據或拋出異常

有時, 您可能想要在找不到模型數據時拋出異常,經過 firstOrFail 方法。閉包

$model = User::findOrFail(1); $model = User::where('votes', '>', 100)->firstOrFail(); 

Doing this will let you catch the exception so you can log and display an error page as necessary. To catch the ModelNotFoundException, add some logic to yourapp/Exceptions/Handler.php file.app

use Illuminate\Database\Eloquent\ModelNotFoundException; class Handler extends ExceptionHandler { public function render($request, Exception $e) { if ($e instanceof ModelNotFoundException) {  // Custom logic for model not found... } return parent::render($request, $e); } } 

Eloquent 模型結合查詢語法

$users = User::where('votes', '>', 100)->take(10)->get(); foreach ($users as $user) { var_dump($user->name); } 

Eloquent 聚合查詢

固然,您也可使用查詢構造器的聚合查詢方法。composer

$count = User::where('votes', '>', 100)->count(); 

若是沒辦法使用流暢接口產生出查詢語句,也可使用 whereRaw 方法:ide

$users = User::whereRaw('age > ? and votes = 100', [25])->get(); 

拆分查詢

若是您要處理很是多(數千條)Eloquent 查詢結果,使用 chunk 方法可讓您順利工做而不會消耗大量內存:

User::chunk(200, function($users) { foreach ($users as $user) {  // } }); 

傳到方法裏的第一個參數表示每次「拆分」要取出的數據數量。第二個參數的閉合函數會在每次取出數據時被調用。

指定查詢時鏈接數據庫

您也能夠指定在執行 Eloquent 查詢時要使用哪一個數據庫鏈接。只要使用 on 方法:

$user = User::on('connection-name')->find(1); 

若是您在使用 讀取 / 寫入鏈接, 您能夠經過以下命令來強制查詢使用 寫入 鏈接:

$user = User::onWriteConnection()->find(1); 

 

批量賦值

在創建一個新的模型時,您把屬性以數組的方式傳入模型的構造方法,這些屬性值會經由批量賦值存成模型數據。這一點很是方便,然而,若盲目地將用戶輸入存到模型時,可能會形成嚴重的安全隱患。若是盲目的存入用戶輸入,用戶能夠隨意的修改任何以及全部模型的屬性。基於這個理由,全部的 Eloquent 模型默認會阻止批量賦值 。

咱們以在模型裏設定 fillable 或 guarded 屬性做爲開始。

定義模型 Fillable 屬性

fillable 屬性指定了哪些字段支持批量賦值 。能夠設定在類的屬性裏或是實例化後設定。

class User extends Model { protected $fillable = ['first_name', 'last_name', 'email']; } 

在上面的例子裏,只有三個屬性容許批量賦值。

定義模型 Guarded 屬性

guarded 與 fillable 相反,是做爲「黑名單」而不是「白名單」:

class User extends Model { protected $guarded = ['id', 'password']; } 

注意: 使用 guarded 時, Input::get() 或任何用戶能夠控制的未過濾數據,永遠不該該傳入 save 或 update 方法,由於沒有在「黑名單」內的字段可能被更新。

阻擋全部屬性被批量賦值

上面的例子中, id 和 password 屬性不會被批量賦值,而全部其餘的屬性則容許批量賦值。您也可使用 guard 屬性阻止全部屬性被批量賦值:

protected $guarded = ['*']; 

 

新增,更新,刪除

要從模型新增一條數據到數據庫,只要創建一個模型實例並調用 save 方法便可。

儲存新的模型數據

$user = new User; $user->name = 'John'; $user->save(); 

注意: 一般 Eloquent 模型主鍵值會自動遞增。可是您若想自定義主鍵,將incrementing 屬性設成 false 。

也可使用 create 方法存入新的模型數據,新增完後會返回新增的模型實例。可是在新增前,須要先在模型類裏設定好 fillable 或 guarded 屬性,由於 Eloquent 默認會防止批量賦值。

在新模型數據被儲存或新增後,若模型有自動遞增主鍵,能夠從對象取得 id 屬性值:

$insertedId = $user->id; 

在模型裏設定 Guarded 屬性

class User extends Model { protected $guarded = ['id', 'account_id']; } 

使用模型的 Create 方法

// 在數據庫中創建一個新的用戶... $user = User::create(['name' => 'John']);  // 以屬性找用戶,若沒有則新增並取得新的實例... $user = User::firstOrCreate(['name' => 'John']);  // 以屬性找用戶,若沒有則創建新的實例... $user = User::firstOrNew(['name' => 'John']); 

更新取出的模型

要更新模型,能夠取出它,更改屬性值,而後使用 save 方法:

$user = User::find(1); $user->email = 'john@foo.com'; $user->save(); 

儲存模型和關聯數據

有時您可能不僅想要儲存模型自己,也想要儲存關聯的數據。您可使用 push 方法達到目的:

$user->push(); 

您能夠結合查詢語句,批次更新模型:

$affectedRows = User::where('votes', '>', 100)->update(['status' => 2]); 

**注意: ** 若使用 Eloquent 查詢構造器批次更新模型,則不會觸發模型事件。

刪除模型

要刪除模型,只要使用實例調用 delete 方法:

$user = User::find(1); $user->delete(); 

按主鍵值刪除模型

User::destroy(1); User::destroy([1, 2, 3]); User::destroy(1, 2, 3); 

固然,您也能夠結合查詢語句批次刪除模型:

$affectedRows = User::where('votes', '>', 100)->delete(); 

只更新模型的時間戳

若是您只想要更新模型的時間戳,您可使用 touch 方法:

$user->touch(); 

 

軟刪除

經過軟刪除方式刪除了一個模型後,模型中的數據並非真的從數據庫被移除。而是會設定 deleted_at時間戳。要讓模型使用軟刪除功能,只要在模型類里加入SoftDeletingTrait 便可:

use Illuminate\Database\Eloquent\SoftDeletes; class User extends Model { use SoftDeletes; protected $dates = ['deleted_at']; } 

要加入 deleted_at 字段到數據庫表,能夠在遷移文件裏使用 softDeletes 方法:

$table->softDeletes(); 

如今當您使用模型調用 delete 方法時, deleted_at字段會被更新成如今的時間戳。在查詢使用軟刪除功能的模型時,被「刪除」的模型數據不會出如今查詢結果裏。

強制查詢軟刪除數據

要強制讓已被軟刪除的模型數據出如今查詢結果裏,在查詢時使用 withTrashed 方法:

$users = User::withTrashed()->where('account_id', 1)->get(); 

withTrashed 也能夠用在關聯查詢:

$user->posts()->withTrashed()->get(); 

若是您只想查詢被軟刪除的模型數據,可使用 onlyTrashed 方法:

$users = User::onlyTrashed()->where('account_id', 1)->get(); 

要把被軟刪除的模型數據恢復,使用 restore 方法:

$user->restore(); 

您也能夠結合查詢語句使用 restore :

User::withTrashed()->where('account_id', 1)->restore(); 

如同 withTrashed , restore 方法也能夠用在關聯對象:

$user->posts()->restore(); 

若是想要真的從模型數據庫刪除,使用 forceDelete 方法:

$user->forceDelete(); 

forceDelete 方法也能夠用在關聯對象:

$user->posts()->forceDelete(); 

要確認模型是否被軟刪除了,可使用 trashed 方法:

if ($user->trashed()) {  // } 

 

時間戳

默認 Eloquent 會自動維護數據庫表的 created_at 和 updated_at 字段。只要把這兩個「時間戳」字段加到數據庫表, Eloquent 就會處理剩下的工做。若是不想讓 Eloquent 自動維護這些字段,把下面的屬性加到模型類裏:

關閉自動更新時間戳

class User extends Model { protected $table = 'users'; public $timestamps = false; } 

自定義時間戳格式

若是想要自定義時間戳格式,能夠在模型類裏重寫 getDateFormat 方法:

class User extends Model { protected function getDateFormat() { return 'U'; } } 

 

範圍查詢

定義範圍查詢

範圍查詢可讓您輕鬆的重複利用模型的查詢邏輯。要設定範圍查詢,只要定義有scope 前綴的模型方法:

class User extends Model { public function scopePopular($query) { return $query->where('votes', '>', 100); } public function scopeWomen($query) { return $query->whereGender('W'); } } 

使用範圍查詢

$users = User::popular()->women()->orderBy('created_at')->get(); 

動態範圍查詢

有時您可能想要定義可接受參數的範圍查詢方法。只要把參數加到方法裏:

class User extends Model { public function scopeOfType($query, $type) { return $query->whereType($type); } } 

而後把參數值傳到範圍查詢方法調用裏:

$users = User::ofType('member')->get(); 

 

Global Scopes

有時您可能但願定義一個 scope 能夠用於模型的全部查詢中。本質上,這也是 Eloquent 的"軟刪除"功能的實現原理。Global scopes 是經過 PHP traits 的組合以及實現 Illuminate\Database\Eloquent\ScopeInterface 接口來定義的。

首先,咱們須要定義一個 trait。 這裏咱們用 Laravel 的 SoftDeletes 舉例:

trait SoftDeletes { /** * Boot the soft deleting trait for a model. * * @return void */ public static function bootSoftDeletes() { static::addGlobalScope(new SoftDeletingScope); } } 

若是一個 Eloquent 模型引入了一個 trait ,而這個 trait 中帶有符合 bootNameOfTrait 慣例命名的方法 ,那麼這個方法會在 Eloquent 模型啓動的時候調用, 您能夠在此時註冊 global scope ,或者作一些其餘您想要的操做。定義的 scope 必須實現ScopeInterface 接口,這個接口提供了兩個方法:apply 和 remove

apply 方法接受一個 Illuminate\Database\Eloquent\Builder 查詢構造器對象以及它所應用的 Model,用來添加這個 scope 所需的額外的 where 子句。而remove 方法一樣接受一個 Builder 對象以及 Model ,用來反向的執行 apply 操做。也就是說,remove 方法應該移除已經添加的 where 子句 (或者其餘查詢子句)。所以,咱們的SoftDeletingScope 的方法應該以下:

/** * Apply the scope to a given Eloquent query builder. * * @param \Illuminate\Database\Eloquent\Builder $builder * @param \Illuminate\Database\Eloquent\Model $model * @return void */ public function apply(Builder $builder, Model $model) { $builder->whereNull($model->getQualifiedDeletedAtColumn()); $this->extend($builder); } /** * Remove the scope from the given Eloquent query builder. * * @param \Illuminate\Database\Eloquent\Builder $builder * @param \Illuminate\Database\Eloquent\Model $model * @return void */ public function remove(Builder $builder, Model $model) { $column = $model->getQualifiedDeletedAtColumn(); $query = $builder->getQuery(); foreach ((array) $query->wheres as $key => $where) {  // If the where clause is a soft delete date constraint, we will remove it from  // the query and reset the keys on the wheres. This allows this developer to  // include deleted model in a relationship result set that is lazy loaded. if ($this->isSoftDeleteConstraint($where, $column)) { unset($query->wheres[$key]); $query->wheres = array_values($query->wheres); } } } 

 

關聯

固然,您的數據庫表極可能跟另外一張表相關聯。例如,一篇 blog 文章可能有不少評論,或是一張訂單跟下單客戶相關聯。 Eloquent 讓管理和處理這些關聯變得很容易。 Laravel 有不少種關聯類型:

  • 一對一
  • 一對多
  • 多對多
  • 遠層一對多關聯
  • 多態關聯
  • 多態的多對多關聯

 

一對一

定義一對一關聯

一對一關聯是很基本的關聯。例如一個 User 模型會對應到一個 Phone 。 在 Eloquent 裏能夠像下面這樣定義關聯:

class User extends Model { public function phone() { return $this->hasOne('App\Phone'); } } 

傳到 hasOne 方法裏的第一個參數是關聯模型的類名稱。定義好關聯以後,就可使用 Eloquent 的動態屬性取得關聯對象:

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

SQL 會執行以下語句:

select * from users where id = 1 select * from phones where user_id = 1 

注意, Eloquent 假設對應的關聯模型數據庫表裏,外鍵名稱是基於模型名稱。在這個例子裏,默認 Phone 模型數據庫表會以 user_id 做爲外鍵。若是想要更改這個默認,能夠傳入第二個參數到 hasOne 方法裏。更進一步,您能夠傳入第三個參數,指定關聯的外鍵要對應到自己的哪一個字段:

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

定義相對的關聯

要在 Phone 模型裏定義相對的關聯,可使用 belongsTo 方法:

class Phone extends Model { public function user() { return $this->belongsTo('App\User'); } } 

在上面的例子裏, Eloquent 默認會使用 phones 數據庫表的 user_id 字段查詢關聯。若是想要本身指定外鍵字段,能夠在 belongsTo 方法裏傳入第二個參數:

class Phone extends Model { public function user() { return $this->belongsTo('App\User', 'local_key'); } } 

除此以外,也能夠傳入第三個參數指定要參照上層數據庫表的哪一個字段:

class Phone extends Model { public function user() { return $this->belongsTo('App\User', 'local_key', 'parent_key'); } } 

 

一對多

一對多關聯的例子如,一篇 Blog 文章可能「有不少」評論。能夠像這樣定義關聯:

class Post extends Model { public function comments() { return $this->hasMany('App\Comment'); } } 

如今能夠經由動態屬性取得文章的評論:

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

若是須要增長更多條件限制,能夠在調用 comments 方法後面經過鏈式查詢條件方法:

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

一樣的,您能夠傳入第二個參數到 hasMany 方法更改默認的外鍵名稱。以及,如同hasOne 關聯,能夠指定自己的對應字段:

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

定義相對的關聯

要在 Comment 模型定義相對應的關聯,可以使用 belongsTo 方法:

class Comment extends Model { public function post() { return $this->belongsTo('App\Post'); } } 

 

多對多

多對多關聯更爲複雜。這種關聯的例子如,一個用戶( user )可能用有不少身份( role ),而一種身份可能不少用戶都有。例如不少用戶都是「管理者」。多對多關聯須要用到三個數據庫表: users , roles ,和 role_user 。 role_user 樞紐表命名是以相關聯的兩個模型數據庫表,依照字母順序命名,樞紐表裏面應該要有 user_id 和role_id 字段。

可使用 belongsToMany 方法定義多對多關係:

class User extends Model { public function roles() { return $this->belongsToMany('App\Role'); } } 

如今咱們能夠從 User 模型取得 roles:

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

若是不想使用默認的樞紐數據庫表命名方式,能夠傳遞數據庫表名稱做爲belongsToMany 方法的第二個參數:

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

也能夠更改默認的關聯字段名稱:

return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'foo_id'); 

固然,也能夠在 Role 模型定義相對的關聯:

class Role extends Model { public function users() { return $this->belongsToMany('App\User'); } } 

 

Has Many Through 遠層一對多關聯

「遠層一對多關聯」提供了方便簡短的方法,能夠經由多層間的關聯取得遠層的關聯。例如,一個 Country 模型可能經過 Users 關聯到不少 Posts 模型。 數據庫表間的關係可能看起來以下:

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 方法讓咱們可使用 $country->posts 取得 country 的 posts。咱們能夠定義如下關聯:

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

若是想要手動指定關聯的字段名稱,能夠傳入第三和第四個參數到方法裏:

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

 

多態關聯

多態關聯能夠用一個簡單的關聯方法,就讓一個模型同時關聯多個模型。例如,您可能想讓 photo 模型同時和一個 staff 或 order 模型關聯。能夠定義關聯以下:

class Photo extends Model { public function imageable() { return $this->morphTo(); } } class Staff extends Model { public function photos() { return $this->morphMany('App\Photo', 'imageable'); } } class Order extends Model { public function photos() { return $this->morphMany('App\Photo', 'imageable'); } } 

取得多態關聯對象

如今咱們能夠從 staff 或 order 模型取得多態關聯對象:

$staff = Staff::find(1); foreach ($staff->photos as $photo) {  // } 

取得多態關聯對象的擁有者

然而,多態關聯真正神奇的地方,在於要從 Photo 模型取得 staff 或 order 對象時:

$photo = Photo::find(1); $imageable = $photo->imageable; 

Photo 模型裏的 imageable 關聯會返回 Staff 或 Order 實例,取決於這是哪種模型擁有的照片。

多態關聯的數據庫表結構

爲了理解多態關聯的運做機制,來看看它們的數據庫表結構:

staff
    id - integer name - string orders id - integer price - integer photos id - integer path - string imageable_id - integer imageable_type - string 

要注意的重點是 photos 數據庫表的 imageable_id 和 imageable_type。在上面的例子裏, ID 字段會包含 staff 或 order 的 ID,而 type 是擁有者的模型類名稱。這就是讓 ORM 在取得 imageable 關聯對象時,決定要哪種模型對象的機制。

 

多態的多對多關聯

Polymorphic Many To Many Relation Table Structure 多態的多對多關聯數據庫表結構

除了通常的多態關聯,也可使用多對多的多態關聯。例如,Blog 的 Post 和 Video模型能夠共用多態的 Tag 關聯模型。首先,來看看數據庫表結構:

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 

如今,咱們準備好設定模型關聯了。 Post 和 Video 模型均可以經由 tags 方法創建morphToMany 關聯:

class Post extends Model { public function tags() { return $this->morphToMany('App\Tag', 'taggable'); } } 

在 Tag 模型裏針對每一種關聯創建一個方法:

class Tag extends Model { public function posts() { return $this->morphedByMany('App\Post', 'taggable'); } public function videos() { return $this->morphedByMany('App\Video', 'taggable'); } } 

 

關聯查詢

根據關聯條件查詢

在取得模型數據時,您可能想要以關聯模型做爲查詢限制。例如,您可能想要取得全部「至少有一篇評論」的Blog 文章。可使用 has 方法達成目的:

$posts = Post::has('comments')->get(); 

也能夠指定運算符和數量:

$posts = Post::has('comments', '>=', 3)->get(); 

也可使用"點號"的形式來獲取嵌套的 has 聲明:

$posts = Post::has('comments.votes')->get(); 

若是想要更進階的用法,可使用 whereHas 和 orWhereHas 方法,在 has 查詢裏設置 "where" 條件 :

$posts = Post::whereHas('comments', function($q) { $q->where('content', 'like', 'foo%'); })->get(); 

 

動態屬性

Eloquent 能夠經由動態屬性取得關聯對象。 Eloquent 會自動進行關聯查詢,並且會很聰明的知道應該要使用 get(用在一對多關聯)或是 first (用在一對一關聯)方法。能夠經由和「關聯方法名稱相同」的動態屬性取得對象。例如,以下面的模型對象 $phone

class Phone extends Model { public function user() { return $this->belongsTo('App\User'); } } $phone = Phone::find(1); 

您能夠不用像下面這樣打印用戶的 email :

echo $phone->user()->first()->email; 

而能夠簡寫以下:

echo $phone->user->email; 

注意: 若取得的是許多關聯對象,會返回Illuminate\Database\Eloquent\Collection 對象。

 

預載入

預載入是用來減小 N + 1 查詢問題。例如,一個 Book 模型數據會關聯到一個 Author。關聯會像下面這樣定義:

class Book extends Model { public function author() { return $this->belongsTo('App\Author'); } } 

如今考慮下面的代碼:

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

上面的循環會執行一次查詢取回全部數據庫表上的書籍,然而每本書籍都會執行一次查詢取得做者。因此若咱們有 25 本書,就會進行 26次查詢。

很幸運地,咱們可使用預載入大量減小查詢次數。使用 with 方法指定想要預載入的關聯對象:

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

如今,上面的循環總共只會執行兩次查詢:

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

使用預載入能夠大大提升程序的性能。

固然,也能夠同時載入多種關聯:

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

甚至能夠預載入巢狀關聯:

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

上面的例子中, author 關聯會被預載入, author 的 contacts 關聯也會被預載入。

預載入條件限制

有時您可能想要預載入關聯,同時也想要指定載入時的查詢限制。下面有一個例子:

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

上面的例子裏,咱們預載入了 user 的 posts 關聯,並限制條件爲 post 的 title 字段需包含 "first" 。

固然,預載入的閉合函數裏不必定只能加上條件限制,也能夠加上排序:

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

延遲預載入

也能夠直接從模型的 collection 預載入關聯對象。這對於須要根據狀況決定是否載入關聯對象時,或是跟緩存一塊兒使用時頗有用。

$books = Book::all(); $books->load('author', 'publisher'); 

你能夠傳入一個閉包來對查詢構建器進行條件限制:

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

 

新增關聯模型

附加一個關聯模型

您經常會須要加入新的關聯模型。例如新增一個 comment 到 post 。除了手動設定模型的 post_id 外鍵,也能夠從上層的 Post 模型新增關聯的 comment :

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

上面的例子裏,新增的 comment 模型中 post_id 字段會被自動設定。

若是想要同時新增不少關聯模型:

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

從屬關聯模型 ( Belongs To )

要更新 belongsTo 關聯時,可使用 associate 方法。這個方法會設定子模型的外鍵:

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

新增多對多關聯模型 ( Many To Many )

您也能夠新增多對多的關聯模型。讓咱們繼續使用 User 和 Role 模型做爲例子。咱們可使用 attach 方法簡單地把 roles 附加給一個 user:

附加多對多模型

$user = User::find(1); $user->roles()->attach(1); 

也能夠傳入要存在樞紐表中的屬性數組:

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

固然,有 attach 方法就會有相反的 detach 方法:

$user->roles()->detach(1); 

attach 和 detach 均可以接受ID數組做爲參數:

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

使用 Sync 方法同時附加一個以上多對多關聯

您也可使用 sync 方法附加關聯模型。 sync 方法會把根據 ID 數組把關聯存到樞紐表。附加完關聯後,樞紐表裏的模型只會關聯到 ID 數組裏的 id :

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

Sync 時在樞紐表加入額外數據

也能夠在把每一個 ID 加入樞紐表時,加入其餘字段的數據:

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

有時您可能想要使用一個命令,在創建新模型數據的同時附加關聯。可使用 save方法達成目的:

$role = new Role(['name' => 'Editor']); User::find(1)->roles()->save($role); 

上面的例子裏,新的 Role 模型對象會在儲存的同時關聯到 user 模型。也能夠傳入屬性數組把數據加到關聯數據庫表:

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

 

更新上層時間戳

當模型 belongsTo 另外一個模型時,比方說一個 Comment 屬於一個 Post ,若是能在子模型被更新時,更新上層的時間戳,這將會頗有用。例如,當 Comment 模型更新時,您可能想要可以同時自動更新 Post 的 updated_at 時間戳。 Eloquent 讓事情變得很簡單。只要在子關聯的類裏,把關聯方法名稱加入 touches 屬性便可:

class Comment extends Model { protected $touches = ['post']; public function post() { return $this->belongsTo('App\Post'); } } 

如今,當您更新 Comment 時,對應的 Post 會自動更新 updated_at 字段:

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

 

使用樞紐表

如您所知,要操做多對多關聯須要一箇中間的數據庫表。 Eloquent 提供了一些有用的方法能夠和這張表互動。例如,假設 User 對象關聯到不少 Role 對象。取出這些關聯對象時,咱們能夠在關聯模型上取得 pivot 數據庫表的數據:

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

注意咱們取出的每一個 Role 模型對象會自動給一個 pivot 屬性。這屬性包含了樞紐表的模型數據,能夠像通常的 Eloquent 模型同樣使用。

默認 pivot 對象只會有關聯鍵的屬性。若是您想讓 pivot 能夠包含其餘樞紐表的字段,能夠在定義關聯方法時指定那些字段:

return $this->belongsToMany('App\Role')->withPivot('foo', 'bar'); 

如今能夠在 Role 模型的 pivot 對象上取得 foo 和 bar 屬性了。

若是您想要能夠自動維護樞紐表的 created_at 和 updated_at 時間戳,在定義關聯方法時加上 withTimestamps 方法:

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

刪除樞紐表的關聯數據

要刪除模型在樞紐表的全部關聯數據,可使用 detach 方法:

User::find(1)->roles()->detach(); 

注意,如上的操做不會移除 roles 數據庫表裏面的數據,只會移除樞紐表裏的關聯數據。

更新樞紐表的數據

有時您只想更新樞紐表的數據,而沒有要移除關聯。若是您想更新樞紐表,能夠像下面的例子使用 updateExistingPivot 方法:

User::find(1)->roles()->updateExistingPivot($roleId, $attributes); 

自定義樞紐模型

Laravel 容許您自定義樞紐模型。要自定義模型,首先要創建一個繼承 Eloquent 的「基本」模型類。在其餘的 Eloquent 模型繼承這個自定義的基本類,而不是默認的 Eloquent 。在基本模型類裏,加入下面的方法返回自定義的樞紐模型實例:

public function newPivot(Model $parent, array $attributes, $table, $exists) { return new YourCustomPivot($parent, $attributes, $table, $exists); } 

 

集合

全部 Eloquent 查詢返回的數據,若是結果多於一條,不論是經由 get 方法或是relationship,都會轉換成集合對象返回。這個對象實現了 IteratorAggregate PHP 接口,因此能夠像數組通常進行遍歷。而集合對象自己還擁有不少有用的方法能夠操做模型數據。

確認集合中裏是否包含特定鍵值

例如,咱們可使用 contains 方法,確認結果數據中,是否包含主鍵爲特定值的對象。

$roles = User::find(1)->roles; if ($roles->contains(2)) {  // } 

集合也能夠轉換成數組或 JSON:

$roles = User::find(1)->roles->toArray(); $roles = User::find(1)->roles->toJson(); 

若是集合被轉換成字符串類型,會返回 JSON 格式:

$roles = (string) User::find(1)->roles; 

集合遍歷

Eloquent 集合裏包含了一些有用的方法能夠進行循環或是進行過濾:

$roles = $user->roles->each(function($role) {  // }); 

集合過濾

過濾集合時,回調函數的使用方式和 array_filter 裏同樣。

$users = $users->filter(function($user) { return $user->isAdmin(); }); 

注意: 若是要在過濾集合以後轉成 JSON,轉換以前先調用 values 方法重設數組的鍵值。

遍歷傳入集合裏的每一個對象到回調函數

$roles = User::find(1)->roles; $roles->each(function($role) {  // }); 

依照屬性值排序

$roles = $roles->sortBy(function($role) { return $role->created_at; }); $roles = $roles->sortByDesc(function($role) { return $role->created_at; }); 

依照屬性值排序

$roles = $roles->sortBy('created_at'); $roles = $roles->sortByDesc('created_at'); 

返回自定義的集合對象

有時您可能想要返回自定義的集合對象,讓您能夠在集合類里加入想要的方法。能夠在 Eloquent 模型類裏重寫 newCollection 方法:

class User extends Model { public function newCollection(array $models = []) { return new CustomCollection($models); } } 

 

獲取器和修改器

定義獲取器

Eloquent 提供了一種便利的方法,能夠在獲取或設定屬性時進行轉換。要定義獲取器,只要在模型里加入相似 getFooAttribute 的方法。注意方法名稱應該使用駝峯式大小寫命名,而對應的 database 字段名稱是下劃線分隔小寫命名:

class User extends Model { public function getFirstNameAttribute($value) { return ucfirst($value); } } 

上面的例子中, first_name 字段設定了一個獲取器。注意傳入方法的參數是本來的字段數據。

定義修改器

修改器的定義方式也是相似的:

class User extends Model { public function setFirstNameAttribute($value) { $this->attributes['first_name'] = strtolower($value); } } 

 

日期轉換器

默認 Eloquent 會把 created_at 和 updated_at 字段屬性轉換成 Carbon 實例,它提供了不少有用的方法,並繼承了 PHP 原生的 DateTime 類。

您能夠經過重寫模型的 getDates 方法,自定義哪一個字段能夠被自動轉換,或甚至徹底關閉這個轉換:

public function getDates() { return ['created_at']; } 

當字段是表示日期的時候,能夠將值設爲 UNIX timestamp 、日期字符串( Y-m-d )、 日期時間( date-time )字符串,固然還有 DateTime 或 Carbon 實例。

要徹底關閉日期轉換功能,只要從 getDates 方法返回空數組便可:

public function getDates() { return []; } 

 

屬性類型轉換

若是您想要某些屬性始終轉換成另外一個數據類型, 您能夠在模型中增長 casts 屬性。不然,您須要爲每一個屬性定義修改器,這樣會增長更多的時間開銷。這裏有一個使用casts 屬性的例子:

/** * 須要被轉換成基本類型的屬性值。 * * @var array */ protected $casts = [ 'is_admin' => 'boolean', ]; 

如今當你獲取 is_admin 屬性時始終會是布爾類型,甚至在數據庫中存儲的這個值是一個整型也會被轉換。其餘支持的類型轉換值有: integerrealfloatdouble,stringbooleanobject 和 array 。

若是您存儲的值是一個序列化的 JSON 時,那麼 array 類型轉換將會很是有用。好比,您的數據表裏有一個 TEXT 類型的字段存儲着序列化後的 JSON 數據, 經過增長array 類型轉換, 當獲取這個屬性的時候會自動反序列化成 PHP 的數組:

/** * 須要被轉換成基本類型的屬性值。 * * @var array */ protected $casts = [ 'options' => 'array', ]; 

如今,當你使用 Eloquent 模型時:

$user = User::find(1);  // $options 是一個數組... $options = $user->options;  // options 會自動序列化成 JSON... $user->options = ['foo' => 'bar']; 

 

模型事件

Eloquent 模型有不少事件能夠觸發,讓您能夠在模型操做的生命週期的不一樣時間點,使用下列方法綁定事件: creatingcreatedupdatingupdatedsavingsaved,deletingdeletedrestoringrestored

當一個對象初次被儲存到數據庫, creating 和 created 事件會被觸發。若是不是新對象而調用了 save 方法, updating / updated 事件會被觸發。而二者的 saving /saved 事件都會被觸發。

使用事件取消數據庫操做

若是 creating 、 updating 、 saving 、 deleting 事件返回 false 的話,就會取消數據庫操做

User::creating(function($user) { if ( ! $user->isValid()) return false; }); 

註冊事件監聽者的方式

您能夠在 EventServiceProvider 中註冊您的模型事件綁定。好比:

/** * Register any other events for your application. * * @param \Illuminate\Contracts\Events\Dispatcher $events * @return void */ public function boot(DispatcherContract $events) { parent::boot($events); User::creating(function($user) {  // }); } 

 

模型觀察者

要整合模型的事件處理,能夠註冊一個模型觀察者。觀察者類裏要設定對應模型事件的方法。例如,觀察者類裏可能有 creating、 updating 、 saving 方法,還有其餘對應模型事件名稱的方法:

例如,一個模型觀察者類可能看起來以下:

class UserObserver { public function saving($model) {  // } public function saved($model) {  // } } 

可使用 observe 方法註冊一個觀察者實例:

User::observe(new UserObserver); 

 

模型 URL 生成

當你把一個模型實例傳遞給 route 或者 action 方法時,模型的主鍵會被插入到生成的 URI 中。好比:

Route::get('user/{user}', 'UserController@show'); action('UserController@show', [$user]); 

在這個例子中 $user->id 屬性會被插入到生成的 URL 的 {user} 這個佔位符中。不過,若是你想使用其餘的屬性而不是 ID 的話,你能夠覆蓋模型的 getRouteKey 方法:

public function getRouteKey() { return $this->slug; } 

 

轉換成數組 / JSON

將模型數據轉成數組

當構建 JSON API 時,您可能經常須要把模型和關聯對象轉換成數組或JSON。因此Eloquent裏已經包含了這些方法。要把模型和已載入的關聯對象轉成數組,可使用toArray 方法:

$user = User::with('roles')->first(); return $user->toArray(); 

注意也能夠把整個的模型集合轉換成數組:

return User::all()->toArray(); 

將模型轉換成 JSON

要把模型轉換成 JSON,可使用 toJson 方法:

return User::find(1)->toJson(); 

從路由中返回模型

注意當模型或集合被轉換成字符串類型時會自動轉換成 JSON 格式,這意味着您能夠直接從路由返回 Eloquent 對象!

Route::get('users', function() { return User::all(); }); 

轉換成數組或 JSON 時隱藏屬性

有時您可能想要限制能出如今數組或 JSON 格式的屬性數據,好比密碼字段。只要在模型裏增長 hidden 屬性便可

class User extends Model { protected $hidden = ['password']; } 

注意: 要隱藏關聯數據,要使用關聯的方法名稱,而不是動態獲取的屬性名稱。

此外,可使用 visible 屬性定義白名單:

protected $visible = ['first_name', 'last_name']; 

有時候您可能想要增長不存在數據庫字段的屬性數據。這時候只要定義一個獲取器便可:

public function getIsAdminAttribute() { return $this->attributes['admin'] == 'yes'; } 

定義好獲取器以後,再把對應的屬性名稱加到模型裏的 appends 屬性:

protected $appends = ['is_admin']; 

把屬性加到 appends 數組以後,在模型數據轉換成數組或 JSON 格式時就會有對應的值。在 appends 數組中定義的值一樣遵循模型中 visible 和 hidden 的設定。

相關文章
相關標籤/搜索