0

0

Laravel框架-EloquentORM进阶部分的详细介绍

黄舟

黄舟

发布时间:2017-03-21 09:18:19

|

1893人浏览过

|

来源于php中文网

原创

关联关系

one to one
假设user模型关联了phone模型,要定义这样一个关联,需要在user模型中定义一个phone方法,该方法返回一个hasone方法定义的关联

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方法了

belongsTo('App\User');        // return $this->belongsTo('App\User', 'foreign_key');
        // return $this->belongsTo('App\User', 'foreign_key', 'other_key');

    }
}

One To Many

假设有一个帖子,它有很多关联的评论信息,这种情况下应该使用一对多的关联,使用hasMany方法

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方法

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();

反向关联关系

反向关系与正向关系实现一样

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字段判断该记录喜欢的是帖子还是评论,表结构有了,接下来就该定义模型了

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

PHP高级开发技巧与范例
PHP高级开发技巧与范例

PHP是一种功能强大的网络程序设计语言,而且易学易用,移植性和可扩展性也都非常优秀,本书将为读者详细介绍PHP编程。 全书分为预备篇、开始篇和加速篇三大部分,共9章。预备篇主要介绍一些学习PHP语言的预备知识以及PHP运行平台的架设;开始篇则较为详细地向读者介绍PKP语言的基本语法和常用函数,以及用PHP如何对MySQL数据库进行操作;加速篇则通过对典型实例的介绍来使读者全面掌握PHP。 本书

下载
$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的问题,使用预加载功能可以使用关联查询出所有结果

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 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\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 关系

中间表查询条件#

当查询时需要对使用中间表作为查询条件时,可以使用wherePivotwherePivotInorWherePivotorWherePivotIn添加查询条件。

$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属性实现该功能

belongsTo('App\Post');
    }
}

现在,更新评论的时候,帖子的updated_at字段也会被更新

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

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
全国统一发票查询平台入口合集
全国统一发票查询平台入口合集

本专题整合了全国统一发票查询入口地址合集,阅读专题下面的文章了解更多详细入口。

19

2026.02.03

短剧入口地址汇总
短剧入口地址汇总

本专题整合了短剧app推荐平台,阅读专题下面的文章了解更多详细入口。

27

2026.02.03

植物大战僵尸版本入口地址汇总
植物大战僵尸版本入口地址汇总

本专题整合了植物大战僵尸版本入口地址汇总,前往文章中寻找想要的答案。

15

2026.02.03

c语言中/相关合集
c语言中/相关合集

本专题整合了c语言中/的用法、含义解释。阅读专题下面的文章了解更多详细内容。

3

2026.02.03

漫蛙漫画网页版入口与正版在线阅读 漫蛙MANWA官网访问专题
漫蛙漫画网页版入口与正版在线阅读 漫蛙MANWA官网访问专题

本专题围绕漫蛙漫画(Manwa / Manwa2)官网网页版入口进行整理,涵盖漫蛙漫画官方主页访问方式、网页版在线阅读入口、台版正版漫画浏览说明及基础使用指引,帮助用户快速进入漫蛙漫画官网,稳定在线阅读正版漫画内容,避免误入非官方页面。

13

2026.02.03

Yandex官网入口与俄罗斯搜索引擎访问指南 Yandex中文登录与网页版入口
Yandex官网入口与俄罗斯搜索引擎访问指南 Yandex中文登录与网页版入口

本专题汇总了俄罗斯知名搜索引擎 Yandex 的官网入口、免登录访问地址、中文登录方法与网页版使用指南,帮助用户稳定访问 Yandex 官网,并提供一站式入口汇总。无论是登录入口还是在线搜索,用户都能快速获取最新稳定的访问链接与使用指南。

114

2026.02.03

Java 设计模式与重构实践
Java 设计模式与重构实践

本专题专注讲解 Java 中常用的设计模式,包括单例模式、工厂模式、观察者模式、策略模式等,并结合代码重构实践,帮助学习者掌握 如何运用设计模式优化代码结构,提高代码的可读性、可维护性和扩展性。通过具体示例,展示设计模式如何解决实际开发中的复杂问题。

3

2026.02.03

C# 并发与异步编程
C# 并发与异步编程

本专题系统讲解 C# 异步编程与并发控制,重点介绍 async 和 await 关键字、Task 类、线程池管理、并发数据结构、死锁与线程安全问题。通过多个实战项目,帮助学习者掌握 如何在 C# 中编写高效的异步代码,提升应用的并发性能与响应速度。

2

2026.02.03

Python 强化学习与深度Q网络(DQN)
Python 强化学习与深度Q网络(DQN)

本专题深入讲解 Python 在强化学习(Reinforcement Learning)中的应用,重点介绍 深度Q网络(DQN) 及其实现方法,涵盖 Q-learning 算法、深度学习与神经网络的结合、环境模拟与奖励机制设计、探索与利用的平衡等。通过构建一个简单的游戏AI,帮助学习者掌握 如何使用 Python 训练智能体在动态环境中作出决策。

3

2026.02.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Laravel---API接口
Laravel---API接口

共7课时 | 0.6万人学习

PHP自制框架
PHP自制框架

共8课时 | 0.6万人学习

PHP面向对象基础课程(更新中)
PHP面向对象基础课程(更新中)

共12课时 | 0.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号