【整理】Laravel中Eloquent ORM 關聯關係的操做

Laravel中Eloquent ORM 關聯關係的操做

關聯數據

定義關聯關係

一對一php

<?php
class User extends Model{
    // 獲取關聯到用戶的手機
    public function phone()
    {
        // Phone 模型默認有一個 user_id 外鍵
        return $this->hasOne('App\Phone'); 
        // 外鍵應該在父級上有一個與之匹配的id(或者自定義 $primaryKey)
        // return $this->hasOne('App\Phone', 'foreign_key');
        // 用User的local_key,匹配Phone的foreign_key
       // return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
    }
}

$phone = User::find(1)->phone; // 獲取用戶的手機模型

相對的關聯數據庫

<?php
class Phone extends Model {
    //  獲取手機對應的用戶
    public function user()
    {
        return $this->belongsTo('App\User');
        // return $this->belongsTo('App\User', 'foreign_key');
        // return $this->belongsTo('App\User', 'foreign_key', 'other_key');
    }
}

$user = Phone::find(1)->user; // 查詢phone的id=1對應的user
$user = Phone::where('number','188**')->first()->user; // 查詢電話爲188的用戶

一對多數組

class User extends Model{
    // 獲取文章
    public function article()
    {
         return $this->hasMany('App\Comment');
         // return $this->hasMany('App\Comment', 'foreign_key');
         // return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
    }
}
$articles = User::find(1)->article; // 返回 userid爲1 的全部文章
$article = User::find(1)->article()->where('title', 'aa')->first(); // 返回某一篇

一對多(逆向)閉包

class Article extends Model{
    public function user()
    {
        return $this->belongsTo('App\User');
        // return $this->belongsTo('App\Post', 'foreign_key');
        // return $this->belongsTo('App\Post', 'foreign_key', 'other_key');
    }
}
$user = Article::find($id)->user; // 返回文章的做者

多對多app

須要三張數據表:users、roles 和 role_user,
role_user 表爲關係表,默認按照關聯模型名的字母順序命名,而且包含 user_id 和 role_id 兩個列。
class User extends Model{
    /**
     * 用戶角色
     */
    public function roles()
    {
         //默認去role_user查找關係
        // return $this->belongsToMany('App\Role');
        return $this->belongsToMany('App\Role',"role_users");
        // 定義鍵值
        // return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'role_id');
    }
}

$roles = User::find(1)->roles;
// 返回 [{"id":1,"name":"SuperManager","pivot":{"user_id":1,"role_id":1}},{"id":2,"name":"Manager","pivot":{"user_id":1,"role_id":2}}]

$roles = User::find(1)->roles()->orderBy('name')->get(); // 條件約束
注意咱們獲取到的每個 Role 模型都被自動賦上了 pivot 屬性。該屬性包含一個表明中間表的模型,而且能夠像Eloquent 模型同樣使用。

若是你的 pivot 表包含額外的屬性,必須在定義關聯關係時進行指定:post

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

若是你想要你的 pivot 表自動包含created_at 和 updated_at 時間戳,在關聯關係定義時使用 withTimestamps 方法:this

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

過中間表字段過濾關聯關係code

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

關聯查詢

存在的關聯查詢ci

// 獲取全部至少有一條評論的文章...
$posts = App\Post::has('comments')->get();
你還能夠指定操做符和數目來自定義查詢:

// 獲取全部至少有三條評論的文章...
$posts = Post::has('comments', '>=', 3)->get();
還可使用」.「來構造嵌套 has 語句,例如,你要獲取全部至少有一條評論及投票的文章:

// 獲取全部至少有一條評論得到投票的文章...
$posts = Post::has('comments.votes')->get();
若是你須要更強大的功能,可使用 whereHas 和 orWhereHas 方法將 where 條件放到 has 查詢上,這些方法容許你添加自定義條件約束到關聯關係條件約束,例如檢查一條評論的內容:

// 獲取全部至少有一條評論包含foo字樣的文章
$posts = Post::whereHas('comments', function ($query) {
    $query->where('content', 'like', 'foo%');
})->get();

無關聯結果查詢文檔

// 獲取全部沒有評論的博客文章
$posts = App\Post::doesntHave('comments')->get();

// 檢查評論內容:
$posts = Post::whereDoesntHave('comments', function ($query) {
   $query->where('content', 'like', 'foo%');
})->get();

統計關聯模型

若是你想要在不加載關聯關係的狀況下統計關聯結果數目,可使用 withCount 方法,該方法會放置一個 {relation}_count 字段到結果模型
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
    echo $post->comments_count;
}

// 添加約束條件到查詢同樣來添加多個關聯關係的「計數」:
$posts = Post::withCount(['votes', 'comments' => function ($query) {
    $query->where('content', 'like', 'foo%');
}])->get();
echo $posts[0]->votes_count;
echo $posts[0]->comments_count;

// 爲關聯關係計數結果設置別名,從而容許在一個關聯關係上進行多維度計數:

$posts = Post::withCount([
    'comments',
    'comments AS pending_comments' => function ($query) {
        $query->where('approved', false);
    }
])->get();

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

渴求式加載

當以屬性方式訪問數據庫關聯關係的時候,關聯關係數據是「懶惰式加載」的,這意味着關聯關係數據直到第一次訪問的時候才被加載。
$books = App\Book::all();
foreach ($books as $book) {
    echo $book->author->name;
}
// 該循環要執行26次查詢:1次是獲取書自己,剩下的25次查詢是爲每一本書獲取其做者。
$books = App\Book::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, ...)

渴求式加載多個關聯關係

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

嵌套的渴求式加載

// 加載全部書的做者及全部做者的我的聯繫方式:
$books = App\Book::with('author.contacts')->get();

帶條件約束的渴求式加載

//  加載 title 包含 first 的文章
$users = App\User::with(['posts' => function ($query) {
    $query->where('title', 'like', '%first%');
}])->get();

// 加載按created_at倒序的的文章
$users = App\User::with(['posts' => function ($query) {
    $query->orderBy('created_at', 'desc');
}])->get();

懶惰渴求式加載

$books = App\Book::all();
if ($someCondition) {
    $books->load('author', 'publisher');
}
// 設置更多的查詢條件到渴求式加載查詢上,能夠傳遞一個閉包到 load 方法:

$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);
// save 方法會自動添加 post_id 值到新的Comment 模型。

保存多個關聯模型,可使用 saveMany 方法:

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

create方法

該方法接收屬性數組、建立模型、而後插入數據庫。save 和 create 的不一樣之處在於 save 接收整個 Eloquent 模型實例而 create 接收原生 PHP 數組:

使用 create 方法以前確保先瀏覽屬性批量賦值文檔。

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

從屬關聯關係

更新 belongsTo 關聯, 使用associate 方法,該方法會在子模型設置外鍵:

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

移除 belongsTo 關聯的時候,使用dissociate 方法。該方法會設置關聯關係的外鍵爲 null:

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

多對多關聯 的 附加/分離

假定一個用戶可能有多個角色,同時一個角色屬於多個用戶,要經過在鏈接模型的中間表中插入記錄附加角色到用戶上,可使用 attach 方法:

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

// 以數組形式傳遞額外被插入數據到中間表:
$user->roles()->attach($roleId, ['expires' => $expires]);

移除一個多對多關聯記錄,使用 detach 方法。detach 方法將會從中間表中移除相應的記錄;
可是,兩個模型在數據庫中都保持不變:

// 從指定用戶中移除角色...
$user->roles()->detach($roleId);

// 從指定用戶移除全部角色...
$user->roles()->detach();

接收數組形式的 ID 做爲輸入:

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

同步關聯

sync 方法接收數組形式的 ID 並將其放置到中間表
// 任何不在該數組中的 ID 對應記錄將會從中間表中移除
$user->roles()->sync([1, 2, 3]);

// 還能夠和 ID 一塊兒傳遞額外的中間表值:
$user->roles()->sync([1 => ['expires' => true], 2, 3]);

// 若是不想要脫離存在的ID,可使用syncWithoutDetaching 方法:
$user->roles()->syncWithoutDetaching([1, 2, 3]);

切換關聯

// 若是當前沒有附加,則附加:若是給定ID當前被附加,則取消附加
$user->roles()->toggle([1, 2, 3]);

在中間表上保存額外數據

// 接收額外中間表屬性數組做爲第二個參數:
App\User::find(1)->roles()->save($role, ['expires' => $expires]);

更新中間表記錄 updateExistingPivot

// 更新中間表中已存在的行,接收中間記錄外鍵和屬性數組進行更新
$user = App\User::find(1);
$user->roles()->updateExistingPivot($roleId, $attributes);

觸發父級時間戳

當一個模型屬於另一個時,子模型更新時父模型的時間戳也被更新將頗有用

例如,當 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 = App\Comment::find(1);
$comment->text = 'Edit to this comment!';
$comment->save();
相關文章
相關標籤/搜索