您的位置:首页 > 编程语言 > PHP开发

laravel 5.5 -- Eloquent 模型关联

2017-08-03 20:57 766 查看

一对一

正向关联

一个
User
模型关联一个
Phone
模型。

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

public function phone() {
# 它会自动假设 Phone 模型拥有 user_id 外键。如果你想要重写这个约定,则可以传入第二个参数到 hasOne 方法里。
return $this->hasOne('App\Phone', 'foreign_key');
}

public function phone() {
# Eloquent 假设外键会和上层模型的 id 字段(或者自定义的 $primaryKey)的值相匹配。否则,请
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
}
# 允许定义默认模型

# 使用
$phone = User::find(1)->phone;  # 当找不到 id=1 的 phone 记录,会查看是否有默认模型,没有则返回 null。
// select * from `by_users` where `by_users`.`id` = '1' limit 1
// select * from `by_phones` where `by_phones`.`user_id` = '1' and `by_phones`.`user_id` is not null limit 1
$email = App\Phone::where('content', '13334233434')->first()->user->email;
// select * from `by_phones` where `content` = '13334233434' limit 1
// select * from `by_users` where `by_users`.`id` = '1' limit 1


反向关联

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

// 使用:同正向关联


关联默认模型

belongsTo,haoOne
允许定义默认模型

public function user()
{
return $this->belongsTo('App\User')->withDefault(); // 空模型
# 或者
return $this->belongsTo('App\User')->withDefault([
'name' => '游客',
]);
# 或者
return $this->belongsTo('App\User')->withDefault(function ($user) {
$user->name = '游客';
});
}


一对多

正向关联

一篇博客文章可能会有无限多个评论。

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

# Controller
$comments = App\Post::find(1)->comments;  // 返回满足条件的评论的集合
// select * from `by_posts` where `by_posts`.`id` = '1' limit 1
// select * from `by_comments` where `by_comments`.`post_id` = '1' and `by_comments`.`post_id` is not null
foreach ($comments as $comment) {
//
}

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


反向关联

// 同一对一的反向关联 :语义化为 ( 一条评论只属于一篇文章 )
return $this->belongsTo('App\Post');


多对多

正向关联

一个用户可能拥有多种身份,而一种身份能同时被多个用户拥有。

需要使用三个数据表:
users
roles
role_user
role_user
表命名是以相关联的两个模型数据表来依照字母顺序命名,并包含了
user_id
role_id
字段。

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

// 使用:等同于 hasMany
$roles = App\User::find(1)->roles;
// select * from `by_users` where `by_users`.`id` = '1' limit 1
/*
select
`by_roles`.*,
`by_role_user`.`user_id` as `p
4000
ivot_user_id`,
`by_role_user`.`role_id` as `pivot_role_id`
from
`by_roles` inner join `by_role_user`
on
`by_roles`.`id` = `by_role_user`.`role_id`
where
`by_role_user`.`user_id` = '1'
*/


如前文提到那样,Eloquent 会合并两个关联模型的名称并依照字母顺序命名。当然你也可以随意重写这个约定。可通过传递第二个参数至
belongsToMany
方法来实现:

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

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


反向关联

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


操作中间表

获取中间表字段

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

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

# 我们取出的每个 Role 模型对象,都会被自动赋予 pivot 属性。


# 默认情况下,pivot 对象只提供模型的键。如果你的 pivot 数据表包含了其它的属性,则可以在定义关联方法时指定那些字段:

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


# 如果你想要中间表自动维护 created_at 和 updated_at 时间戳,可在定义关联方法时加上  withTimestamps 方法:

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


自定义中间表模型

public function users()
{
return $this>belongsToMany('App\User')->using('App\UserRole');
}


namespace App;

use Illuminate\Database\Eloquent\Relations\Pivot;

class UserRole extends Pivot
{
//
}


使用中间表来过滤关联数据

你可以使用
wherePivot
wherePivotIn
来增加中间件表过滤条件:

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

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


重命名 pivot

你可以使用
wherePivot
wherePivotIn
来增加中间件表过滤条件:

return $this->belongsToMany('App\Podcast')
->as('subscription')
->withTimestamps();

// 使用
$users = User::with('podcasts')->get();

foreach ($users->flatMap->podcasts as $podcast) {
echo $podcast->subscription->created_at;
}


远层一对多

一个
Country
模型可能通过中间的
Users
模型关联到多个
Posts
模型。

countries
id - integer
name - string

users
id - integer
country_id - integer
name - string

posts
id - integer
user_id - integer
title - string


class Country extends Model
{
public function posts()
{
return $this->hasManyThrough(
'App\Post',
'App\User',
// 'country_id',   // Foreign key on users table...
// 'user_id',      // Foreign key on posts table...
// 'id',           // Local key on countries table...
// 'id'            // Local key on users table...
);
}
}


$posts = App\Country::find(1)->posts;
// select * from `by_countries` where `by_countries`.`id` = '1' limit 1
// select `by_posts`.*, `by_users`.`country_id` from `by_posts` inner join `by_users` on `by_users`.`id` = `by_posts`.`user_id` where `by_users`.`country_id` = '1'


多态关联

用户可以「评论」文章和视频。

数据库设计

posts
id - integer
title - string
body - text

videos
id - integer
title - string
url - string

comments
id - integer
body - text
commentable_id - integer
commentable_type - string   // posts or videos


namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model {
/**
* 获取所有拥有的 commentable 模型。
*/
public function commentable() {
return $this->morphTo();
}
}

class Post extends Model {
/**
* 获取所有文章的评论。
*/
public function comments() {
return $this->morphMany('App\Comment', 'commentable');
}
}

class Video extends Model {
/**
* 获取所有视频的评论。
*/
public function comments(){
return $this->morphMany('App\Comment', 'commentable');
}
}


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

foreach ($post->comments as $comment) {
//
}


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

$commentable = $comment->commentable;
# 返回 Post 或 Video 实例


自定义多态关联的类型字段

use Illuminate\Database\Eloquent\Relations\Relation;

Relation::morphMap([
'posts' => App\Post::class,
'videos' => App\Video::class,
]);
# 你需要在你自己的 AppServiceProvider 中的 boot 函数注册这个 morphMap ,或者创建一个独立且满足你要求的服务提供者。


多态多对多关联

数据库设计

博客的
Post
Video
模型可以共用多态关联至
Tag
模型。post 有多个标签,一个标签有多个 post 。

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


正向关联

namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model{
/**
* 获取该文章的所有标签。
*/
public function tags(){
return $this->morphToMany('App\Tag', 'taggable');
}
}


反向关联

namespace App;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model{
/**
* 获取所有被赋予该标签的文章。
*/
public function posts(){
return $this->morphedByMany('App\Post', 'taggable');
}

/**
* 获取所有被赋予该标签的视频。
*/
public function videos(){
return $this->morphedByMany('App\Video', 'taggable');
}
}


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


$tag = App\Tag::find(1);
foreach ($tag->videos as $video) {
//
}


查找关联

查找关联是否存在

// 获取那些至少拥有一条评论的文章...
$posts = App\Post::has('comments')->get();
// select * from `by_posts` where exists (select * from `by_comments` where `by_posts`.`id` = `by_comments`.`post_id`)
$posts = App\Post::doesntHave('comments')->get();
// select * from `by_posts` where not exists (select * from `by_comments` where `by_posts`.`id` = `by_comments`.`post_id`)


// 获取所有至少有三条评论的文章...
$posts = Post::has('comments', '>=', 3)->get();
// select * from `by_posts` where (select count(*) from `by_comments` where `by_posts`.`id` = `by_comments`.`post_id`) > 4


// 获取所有至少有一张卡的手机的用户...
$posts = User::has('phones.cards')->get();
// select * from `by_users` where exists (select * from `by_phones` where `by_users`.`id` = `by_phones`.`user_id` and exists (select * from `by_cards` where `by_phones`.`id` = `by_cards`.`phone_id`))


# 如果你想要更高级的用法,则可以使用 whereHas 和 orWhereHas 方法,在 has 查找里设置「where」条件。此方法可以让你增加自定义条件至关联条件中,例如对评论内容进行检查:
//  获取那些至少有一条评论包含 foo 的文章
$posts = Post::whereHas('comments', function ($query) {
$query->where('content', 'like', 'foo%');
})->get();
// select * from `by_posts` where exists (select * from `by_comments` where `by_posts`.`id` = `by_comments`.`post_id` and `content` like 'foo%')

$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_count' => 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;
}
// 若存在着 25 本书,则循环就会执行 26 次查找:1 次是查找所有书籍,其它 25 次则是在查找每本书的作者。


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

foreach ($books as $book) {
echo $book->author->name;
}
// 对于该操作则只会执行两条 SQL 语句:
# 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();
# 预加载所有书籍的作者,及所有作者的个人联系方式


$users = App\Book::with('author:id,name')->get();    # 这个方法,应该总是包含 id 字段


预加载条件限制

$users = App\User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%');
}])->get();
# 在这个例子里,Eloquent 只会预加载标题包含 first 的文章


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


延迟预加载

有时你可能需要在上层模型被获取后才预加载关联。

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

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


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


public function format(Book $book)
{
$book->loadMissing('author');     // 当没有加载时候,加载

return [
'name' => $book->name,
'author' => $book->author->name
];
}


插入 & 更新关联模型

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.']),
]);


create 方法

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

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

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


save
允许传入一个完整的 Eloquent 模型实例,但
create
只允许传入原始的 PHP 数组。

你需要先在你的模型上定义一个
fillable
guarded
属性,因为所有的 Eloquent 模型都针对批量赋值(Mass-Assignment)做了保护。

更新「从属」关联

$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]);


// 移除用户身上某一身份...
$user->roles()->detach($roleId);

// 移除用户身上所有身份...
$user->roles()->detach();


# 为了方便,attach 与 detach 都允许传入 ID 数组:
$user = App\User::find(1);

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

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


更新关联

# sync 方法可以用数组形式的 IDs 插入中间的数据表。任何一个不存在于给定数组的 IDs 将会在中间表内被删除。操作完成之后,只有那些在给定数组内的 IDs 会被保留在中间表中。
$user->roles()->sync([1, 2, 3]);


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


如果你不想删除现有的 IDs ,你可以
syncWithoutDetaching
方法:

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


切换关联

// 存在就删除,不存在就创建
$user->roles()->toggle([1, 2, 3]);


在中间表上保存额外数据

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


更新中间表记录

# 这个方法接收中间记录的外键和属性数组进行更新
$user = App\User::find(1);

$user->roles()->updateExistingPivot($roleId, $attributes);


连动父级时间戳

当一个模型
belongsTo
belongsToMany
另一个模型时,像是一个
Comment
属于一个
Post
。这对于子级模型被更新时,要更新父级的时间戳相当有帮助。举例来说,当一个
Comment
模型被更新时,你可能想要「连动」更新
Post
所属的
updated_at
时间戳。

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model{
/**
* 所有的关联将会被连动。
*
* @var array
*/
protected $touches = ['post'];

/**
* 获取拥有此评论的文章。
*/
public function post(){
return $this->belongsTo('App\Post');
}
}


$comment = App\Comment::find(1);
$comment->text = 'Edit to this comment!';
$comment->save();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  laravel laravel5-5