Laravel 模型关联基础教程详解


Posted in PHP onSeptember 17, 2019

在 Laravel 中定义模型关联是每个 Laravel 开发者可能已经做过不止一次的事情。但是在试图实现关联时可能会遇到各种问题。因为 Laravel 有各种各样的关联,你应该选择哪一个?当涉及到查询模型时,我们如何充分利用模型关联的功能?

Laravel 的模型关联可能会让人糊涂。如果你不完全理解 Laravel 的关联在这一点上是如何工作的,别担心,读完这篇文章后,你会更好地理解它。

我们应该使用哪个模型关联?

要回答这个问题,首先你要知道有哪些可用的选项。Laravel 有 3 种不同的关联类型。

  • 一对一
  • 一对多
  • 多对多

我们将逐个探讨不同的关联类型并解释一下应该什么时候使用它们。

一对一

一对一关联是目前存在的最基本的关联。这种关联意味着 A 模型只能链接到 B 模型,相反也是如此。举个例子,一个 User 模型和一个 Passport 模型会成为一对一的关联。一个用户只能拥有一张通行证,同样,一张通行证也只属于一个用户。

让我们看看如何在代码中定义这种关联。

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
  public function passport() {
    return $this->hasOne(App\Passport::class);
  }
}

在 User 模型中我们创建了一个 passport 方法。我们通过 hasOne 方法告诉 Laravel User 模型有一个 Passport 。

注意:
所有用于定义关联的方法都有可选的额外参数,你可以在这些参数中定义本地键和外键。默认情况下,Laravel会假设你在用户模型中定义了 passport_id ,因为你试图创建与 passport 模型的关联。创建迁移文件时也请注意这一点!

在 Passport 模型中,我们需要定义逆向的关联。我们要让 Passport 模型知道它属于 User 模型。我们可以使用 belongsTo 方法来实现这一点。

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Passport extends Model
{
  public function user() {
    return $this->belongsTo(App\User::class);
  }
}

一对多

你可以在 Laravel 中定义的下一个关联是一对多关联。 这种类型的关联意味着一个类型A的模型可以链接到多个类型B的模型。但是类型B的模型只属于一个类型A的模型。

例如,User 模型和 Invoice 模型之间的关联是一对多关联。 用户可以拥有多个账单,但账单仅属于一个用户。

在代码中是这样写的:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
  public function invoices() {
    return $this->hasMany(App\Invoice::class);
  }
}

它看起来就像我们之前用于定义一对一关联的代码,对吧?

我们现在要做的就是让 Invoice 模型知道它属于 User 模型。 让我们定义一对多关联的反向对应关联吧。

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Invoice extends Model
{
  public function user() {
    return $this->belongsTo(App\User::class);
  }
}

多对多

最后要定义的关联是多对多关联。 这种类型的关联意味着类型A的一个模型可以链接到类型B的多个模型,反之亦然。

例如,Invoice  模型和 Product  模型之间的关联将是多对多关联。 账单可以包含多个产品,而产品可以属于多个账单。

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Invoice extends Model
{
  public function products() {
    return $this->belongsToMany(App\Product::class);
  }
}

你可以像这样定义这种关联的反向关系:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
  public function invoices() {
    return $this->belongsToMany(App\Invoice::class);
  }
}

多对多关联实现起来稍微困难一些,因为它们需要数据库中的中间表。 你可以通过创建迁移文件在 Laravel 中创建此中间表。

远程关联

远程一对一

has one through 关联通过单个中间关联模型实现。 如果每个供应商都有一个用户,并且每个用户与一个用户历史记录相关联,那么供应商可以通过用户访问用户的历史记录。

这就是定义这种关联所需的数据库表:

suppliers:
- idproducts:
- id
- supplier_idproduct_history:
- id
- product_id

即使 product_history 表不包含 supplier_id 列,供应商也可以通过使用 「has one through」 关系访问 product_history 记录。

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Supplier extends Model
{
  public function productHistory() {
    return $this->hasOneThrough(App\History::class, App\Product::class);
  }
}

传递给 hasOneThrough 方法的第一个参数是希望访问模型的名称。 第二个参数是中间模型的名称。

远程一对多

「has many through」 关联相当于 「has one through」 关联,只是对于多个记录的。 让我们使用前面的示例,但我们改变一件事:产品现在可以有多个历史条目而不是一个。 数据库表保持不变。

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Supplier extends Model
{
  public function productHistory() {
    return $this->hasManyThrough(App\History::class, App\Product::class);
  }
}

这样,供应商模型可以访问产品的历史记录条目。

查询关联

查询一个关联非常简单。因为我们定义了 Passport 的一对一关联和 Invoice 的一对多关联,所以我们可以在 User 模型中使用它们。在 User 模型的每个实例上,我们都可以得到对应的 Passport 和 Invoice。

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

// 查询 passport 关联
$user->passport->expiration_date;

// 查询 invoice 关联
foreach($user->invoices as $invoice) {
  $invoice->total_amount;
}

也可以查询关联的反向关联。 如果您有账单,则可以获得该账单的用户。

<?php
$invoice = \App\Invoice::find(1);
// Get the user
$invoice->user->first_name;

查询多对多关联的工作方式与其他关联完全相同。 此外,多对多关联有一个pivot 属性。 此属性表示中间表,可以像任何其他模型一样使用。

举个例子,假设连接的表有 created_at 字段,我们就可以使用 pivot 来获取 created_at 字段。

<?php
$invoice = \App\Invoice::find(1);
// 获取 product 的 `created_at` 字段
foreach($invoice->products as $product) {
  $product->pivot->created_at;
}

查询 has one through 和 has many through 的工作方式与其他关联完全相同。

添加约束

可以在查询时向关系添加约束。看看下面的示例:

<?php
$user->passport()->where('active', 1)->orderBy('expiration_date');

检查关联是否存在

有时候你希望检查模型中是否有添加某些关联, Laravel有一些方法可以帮助你用来检查:

<?php
// 找到拥有护照的所有用户
$users = App\User::has('passport')->get();

// 找到没拥有护照的所有用户
$users = App\User::doesntHave('passport')->get();

// 找到拥有 5 个及以上产品的发票
$invoices = App\Invoice::has('products', '>=', 5)->get();

希望这篇文章能让你对Laravel的模型关联有更好的理解。谢谢你的阅读!也希望大家多多支持三水点靠木。

PHP 相关文章推荐
PHP+.htaccess实现全站静态HTML文件GZIP压缩传输(一)
Feb 15 PHP
php 删除记录实现代码
Mar 12 PHP
用sql命令修改数据表中的一个字段为非空(not null)的语句
Jun 04 PHP
ThinkPHP采用模块和操作分析
Apr 18 PHP
php中根据变量的类型 选择echo或dump
Jul 05 PHP
在项目中寻找代码的坏命名
Jul 14 PHP
PHP 透明水印生成代码
Aug 27 PHP
php查看当前Session的ID实例
Mar 16 PHP
php实现遍历多维数组的方法
Nov 25 PHP
深入浅析yii2-gii自定义模板的方法
Apr 26 PHP
php curl批处理实现可控并发异步操作示例
May 09 PHP
python进程与线程小结实例分析
Nov 11 PHP
PHP实现的微信APP支付功能示例【基于TP5框架】
Sep 16 #PHP
php实现的支付宝网页支付功能示例【基于TP5框架】
Sep 16 #PHP
PHP实现的AES 128位加密算法示例
Sep 16 #PHP
Laravel项目中timeAgo字段语言转换的改善方法示例
Sep 16 #PHP
php解决crontab定时任务不能写入文件问题的方法分析
Sep 16 #PHP
PHP实现一个限制实例化次数的类示例
Sep 16 #PHP
thinkPHP5框架路由常用知识点汇总
Sep 15 #PHP
You might like
php自动适应范围的分页代码
2008/08/05 PHP
php中\r \r\n \t的区别示例介绍
2014/02/08 PHP
php实现html标签闭合检测与修复方法
2015/07/09 PHP
ThinkPHP3.2.2实现持久登录(记住我)功能的方法
2016/05/16 PHP
thinkphp3.2实现在线留言提交验证码功能
2017/07/19 PHP
JQueryEasyUI datagrid框架的基本使用
2013/04/08 Javascript
如何让页面加载完成后执行js
2013/06/26 Javascript
WEB前端设计师常用工具集锦
2014/12/09 Javascript
javascript相关事件的几个概念
2015/05/21 Javascript
Javascript实现Array和String互转换的方法
2015/12/21 Javascript
Bootstrap~多级导航(级联导航)的实现效果【附代码】
2016/03/08 Javascript
jQuery获取访问者IP地址的方法(基于新浪API与QQ查询接口)
2016/05/25 Javascript
angularjs实现的前端分页控件示例
2017/02/10 Javascript
jQuery上传多张图片带进度条样式(DEMO)
2017/03/02 Javascript
JavaScript定时器setTimeout()和setInterval()详解
2017/08/18 Javascript
20个最常见的jQuery面试问题及答案
2018/05/23 jQuery
JS的函数调用栈stack size的计算方法
2018/06/24 Javascript
记录一篇关于redux-saga的基本使用过程
2018/08/18 Javascript
微信公众号网页分享功能开发的示例代码
2020/05/27 Javascript
原生JS实现天气预报
2020/06/16 Javascript
解决uWSGI的编码问题详解
2017/03/24 Python
Django中的forms组件实例详解
2018/11/08 Python
Python利用matplotlib做图中图及次坐标轴的实例
2019/07/08 Python
pytorch::Dataloader中的迭代器和生成器应用详解
2020/01/03 Python
Python利用Scrapy框架爬取豆瓣电影示例
2020/01/17 Python
python 中的[:-1]和[::-1]的具体使用
2020/02/13 Python
用什么库写 Python 命令行程序(示例代码详解)
2020/02/20 Python
在python中logger setlevel没有生效的解决
2020/02/21 Python
基于python实现计算且附带进度条代码实例
2020/03/31 Python
详解使用双缓存解决Canvas clearRect引起的闪屏问题
2019/04/29 HTML / CSS
Fanatics官网:运动服装、球衣、运动装备
2020/10/12 全球购物
六道php面试题附答案
2014/06/05 面试题
《彭德怀和他的大黑骡子》教学反思
2014/04/12 职场文书
霸气押韵的班级口号
2014/06/09 职场文书
竞选班长演讲稿500字
2014/08/22 职场文书
安全先进班组材料
2014/12/26 职场文书