详解PHP的Laravel框架中Eloquent对象关系映射使用


Posted in PHP onFebruary 26, 2016

零、什么是 Eloquent
Eloquent 是 Laravel 的 'ORM',即 'Object Relational Mapping',对象关系映射。ORM 的出现是为了帮我们把对数据库的操作变得更加地方便。

Eloquent 让一个 'Model类' 对应一张数据库表,并且在底层封装了很多 'function',可以让 Model 类非常方便地调用。
来看一段如下代码:

<?php

class Article extends \Eloquent {

protected $fillable = [];

}

'protected $fillable = [];' 这一行代码在这里没有任何价值,是 generator 自动生成的,在此我们不做讨论。

这个类简直再简单不过了,没有指定命名空间,没有构造函数,如果那一行没有意义的代码也不算上的话,这个文件就只有两个有实际意义的东西: 'Article' 和 '\Eloquent'。没错,Eloquent 就是这么?耪ㄌ欤?恍枰?坛幸幌 Eloquent 类,就可以干 'first() find() where() orderBy()' 等非常非常多的事情,这就是面向对象的强大威力。

一、Eloquent 基本用法
Eloquent 中文文档在:http://laravel-china.org/docs/eloquent

废话不多说,下面我将直接展示 Eloquent 的几种常见用法的代码。

找到 id 为 2 的文章打印其标题

$article = Article::find(2);

echo $article->title;

查找标题为“我是标题”的文章,并打印 id

$article = Article::where('title', '我是标题')->first();

echo $article->id;

查询出所有文章并循环打印出所有标题

$articles = Article::all(); // 此处得到的 $articles 是一个对象集合,可以在后面加上 '->toArray()' 变成多维数组。

foreach ($articles as $article) {

  echo $article->title;

}

查找 id 在 10~20 之间的所有文章并打印所有标题

$articles = Article::where('id', '>', 10)->where('id', '<', 20)->get();

foreach ($articles as $article) {

  echo $article->title;

}

查询出所有文章并循环打印出所有标题,按照 updated_at 倒序排序

$articles = Article::where('id', '>', 10)->where('id', '<', 20)->orderBy('updated_at', 'desc')->get();

foreach ($articles as $article) {

  echo $article->title;

}

基础使用要点
1. 每一个继承了 Eloquent 的类都有两个 '固定用法' 'Article::find($number)' 'Article::all()',前者会得到一个带有数据库中取出来值的对象,后者会得到一个包含整个数据库的对象合集。

2. 所有的中间方法如 'where()' 'orderBy()' 等都能够同时支持 '静态' 和 '非静态链式' 两种方式调用,即 'Article::where()...' 和 'Article::....->where()'。

3. 所有的 '非固定用法' 的调用最后都需要一个操作来 '收尾',本片教程中有两个 '收尾操作':'->get()' 和 '->first()'。

二、中间操作流
Builder 这个单词可以直译成构造器,但是“中间操作流”更容易理解,因为数据库操作大部分时候都是链式操作的。

中间操作流,请看代码:

Article::where('id', '>', 10)->where('id', '<', 20)->orderBy('updated_at', 'desc')->get();

这段代码的 `::where()->where()->orderBy()` 就是中间操作流。中间操作流用面向对象的方法来理解,可以总结成一句话:

创建一个对象,并不断修改它的属性,最后用一个操作来触发数据库操作。
如何找到中间操作流的蛛丝马迹

中间操作流这个东西,文档里几乎没有任何有价值的信息,那么,我们该怎么找出这个玩意儿呢?很简单,使用以下代码:

$builder = Article::where('title', "我是标题")->title;

然后你就会看到下面的错误:

详解PHP的Laravel框架中Eloquent对象关系映射使用

为什么会出现错误?因为 `Article::where()` 了之后依然是 `Builder` 对象,还不是 `Article` 对象,不能直接取 `title`。

“终结者”方法

所谓 “终结者” 方法,指的是在 N 个中间操作流方法对某个 Eloquent 对象进行加工以后,触发最终的数据库查询操作,得到返回值。

`first()` `get()` `paginate()` `count()` `delete()` 是用的比较多的一些 “终结者” 方法,他们会在中间操作流的最后出现,把 SQL 打给数据库,得到返回数据,经过加工返回一个 Article 对象或者一群 Article 对象的集合。

复杂用法示例

Article::where('id', '>', '100')->where('id', '<', '200')->orWhere('top', 1)->belongsToCategory()->where('category_level', '>', '1')->paginate(10);

三、模型间关系(关联)
1.一对一关系

顾名思义,这描述的是两个模型之间一对一的关系。这种关系是不需要中间表的。

假如我们有两个模型:User 和 Account,分别对应注册用户和消费者,他们是一对一的关系,那么如果我们要使用 Eloquent 提供的一对一关系方法,表结构应该是这样的:

user: id ... ... account_id

account: id ... ... user_id

假设我们需要在 User 模型中查询对应的 Account 表的信息,那么代码应该是这样的。 `/app/models/User.php`:

<?php

class User extends Eloquent {

 

 protected $table = 'users';

 public function hasOneAccount()

 {

   return $this->hasOne('Account', 'user_id', 'id');

 }

}

然后,当我们需要用到这种关系的时候,该如何使用呢?如下:

$account = User::find(10)->hasOneAccount;

此时得到的 `$account` 即为 `Account` 类的一个实例。

这里最难的地方在于后面的两个 foreign_key 和 local_key 的设置,大家可以就此记住:在 User 类中,无论 hasOne 谁,第二个参数都是 `user_id`,第三个参数一般都是 `id`。由于前面的 `find(10)` 已经锁定了 id = 10,所以这段函数对应的 SQL 为: `select * from account where user_id=10`。

这段代码除了展示了一对一关系该如何使用之外,还传达了三点信息,也是我对于大家使用 Eloquent 时候的建议:

(1). 每一个 Model 中都指定表名

(2). has one account 这样的关系写成 `hasOneAccount()` 而不是简单的 `account()`

(3). 每次使用模型间关系的时候都写全参数,不要省略
相应的,如果使用 belongsTo() 关系,应该这么写:

<?php

class Account extends Eloquent {

 protected $table = 'accounts';

 

 public function belongsToUser()

 {

  return $this->belongsTo('User', 'user_id', 'id');

 }

}

2.一对多关系

学会了前面使用一对一关系的基础方法,后面的几种关系就简单多了。

我们引入一个新的Model:Pay,付款记录。表结构应该是这样的:

user: id ... ...

pay: id ... ... user_id

User 和 Pay 具有一对多关系,换句话说就是一个 User 可以有多个 Pay,这样的话,只在 Pay 表中存在一个 `user_id` 字段即可。 `/app/models/User.php`:

<?php

class User extends Eloquent {

 

 protected $table = 'users';

 public function hasManyPays()

 {

  return $this->hasMany('Pay', 'user_id', 'id');

 }

}

然后,当我们需要用到这种关系的时候,该如何使用呢?如下:

$accounts = User::find(10)->hasManyPays()->get();

此时得到的 `$accounts` 即为 `Illuminate\Database\Eloquent\Collection` 类的一个实例。大家应该也已经注意到了,这里不是简单的 `-> hasOneAccount` 而是 `->hasManyPays()->get()`,为什么呢?因为这里是 `hasMany`,操作的是一个对象集合。

相应的 belongsTo() 的用法跟上面一对一关系一样:

<?php

class Pay extends Eloquent {

 protected $table = 'pays';

 

 public function belongsToUser()

 {

  return $this->belongsTo('User', 'user_id', 'id');

 }

}

3.多对多关系

多对多关系和之前的关系完全不一样,因为多对多关系可能出现很多冗余数据,用之前自带的表存不下了。

我们定义两个模型:Article 和 Tag,分别表示文章和标签,他们是多对多的关系。表结构应该是这样的:

article: id ... ...

tag: id ... ...

article_tag: article_id tag_id

在 Model 中使用:

<?php

class Tag extends Eloquent {

 protected $table = 'tags';

 

 public function belongsToManyArticle()

 {

  return $this->belongsToMany('Article', 'article_tag', 'tag_id', 'article_id');

 }

}

需要注意的是,第三个参数是本类的 id,第四个参数是第一个参数那个类的 id。

使用跟 hasMany 一样:

$tagsWithArticles = Tag::take(10)->get()->belongsToManyArticle()->get();

这里会得到一个非常复杂的对象,可以自行 `var_dump()`。跟大家说一个诀窍,`var_dump()` 以后,用 Chrome 右键 “查看源代码”,就可以看到非常整齐的对象/数组展开了。

在这里给大家展示一个少见用法(奇技淫巧):

public function parent_video()

{

  return $this->belongsToMany($this, 'video_hierarchy', 'video_id', 'video_parent_id');

}

public function children_video()

{

  return $this->belongsToMany($this, 'video_hierarchy', 'video_parent_id', 'video_id');

}

对,你没有看错,可以 belongsToMany 自己。
其他关系

Eloquent 还提供 “远层一对多关联”、“多态关联” 和 “多态的多对多关联” 这另外三种用法,经过上面的学习,我们已经掌握了 Eloquent 模型间关系的基本概念和使用方法,剩下的几种不常用的方法就留到我们用到的时候再自己探索吧。

重要技巧:关系预载入
你也许已经发现了,在一对一关系中,如果我们需要一次性查询出10个 User 并带上对应的 Account 的话,那么就需要给数据库打 1 + 10 条 SQL,这样性能是很差的。我们可以使用一个重要的特性,关系预载入:http://laravel-china.org/docs/eloquent#eager-loading

直接上代码:

$users = User::with('hasOneAccount')->take(10)->get()

这样生成的 SQL 就是这个样子的:

select * from account where id in (1, 2, 3, ... ...)

这样 1 + 10 条 SQL 就变成了 1 + 1 条,性能大增。

PHP 相关文章推荐
解析PHP的session过期设置
Jun 29 PHP
php读取csv实现csv文件下载功能
Dec 18 PHP
使用php清除bom示例
Mar 03 PHP
phpstorm编辑器乱码问题解决
Dec 01 PHP
php数组函数array_key_exists()小结
Dec 10 PHP
详解WordPress中用于更新和获取用户选项数据的PHP函数
Mar 08 PHP
PHP判断JSON对象是否存在的方法(推荐)
Jul 06 PHP
PHP实现四种基础排序算法的运行时间比较(推荐)
Aug 11 PHP
thinkPHP模板引擎用法示例
Dec 08 PHP
PHP实现的链式队列结构示例
Sep 15 PHP
php用wangeditor3实现图片上传功能
Aug 22 PHP
PHP7新特性
Mar 09 PHP
PHP文件缓存smarty模板应用实例分析
Feb 26 #PHP
PHP计算当前坐标3公里内4个角落的最大最小经纬度实例
Feb 26 #PHP
PHP实现根据时间戳获取周几的方法
Feb 26 #PHP
PHP将二维数组某一个字段相同的数组合并起来的方法
Feb 26 #PHP
关于PHP 如何用 curl 读取 HTTP chunked 数据
Feb 26 #PHP
PHP中array_keys和array_unique函数源码的分析
Feb 26 #PHP
Json_encode防止汉字转义成unicode的方法
Feb 25 #PHP
You might like
删除及到期域名的查看(抢域名必备哦)
2008/05/14 PHP
9个PHP开发常用功能函数小结
2011/07/15 PHP
destoon实现资讯信息前面调用它所属分类的方法
2014/07/15 PHP
CI框架源码解读之URI.php中_fetch_uri_string()函数用法分析
2016/05/18 PHP
php实现微信公众平台发红包功能
2018/06/14 PHP
深入探讨JavaScript、JQuery屏蔽网页鼠标右键菜单及禁止选择复制
2014/06/10 Javascript
javascript将浮点数转换成整数的三个方法
2014/06/23 Javascript
jquery引用方法时传递参数原理分析
2014/10/13 Javascript
js字符串完全替换函数分享
2014/12/03 Javascript
Node.js程序中的本地文件操作用法小结
2016/03/06 Javascript
bootstrap下拉菜单使用方法解析
2017/01/13 Javascript
JavaScript实现替换字符串中最后一个字符的方法
2017/03/07 Javascript
关于react-router的几种配置方式详解
2017/07/24 Javascript
微信小程序实现轮播图效果
2017/09/07 Javascript
JavaScript生成指定范围的时间列表
2018/03/19 Javascript
JS求解两数之和算法详解
2020/04/28 Javascript
小程序富文本提取图片可放大缩小
2020/05/26 Javascript
[13:40]TI3青蛙君全程回顾 DOTA2我们为梦想再战
2013/09/13 DOTA
Python编程中的文件读写及相关的文件对象方法讲解
2016/01/19 Python
Python实现的概率分布运算操作示例
2017/08/14 Python
python代码如何注释
2020/06/01 Python
pycharm远程连接vagrant虚拟机中mariadb数据库
2020/06/05 Python
python能自学吗
2020/06/18 Python
html5的新玩法——语音搜索
2013/01/03 HTML / CSS
HTML5地理定位实例
2014/10/15 HTML / CSS
澳大利亚最大的网上油画销售画廊:Direct Art Australia
2018/04/15 全球购物
彪马法国官网:PUMA法国
2019/12/15 全球购物
SIMON MILLER官网:洛杉矶的生活方式品牌
2020/10/19 全球购物
校园活动策划书范文
2014/01/10 职场文书
前台文员我鉴定
2014/01/12 职场文书
公司年底活动方案
2014/08/17 职场文书
总经理检讨书范文
2015/02/16 职场文书
兴趣班停课通知
2015/04/24 职场文书
安全生产隐患排查制度
2015/08/05 职场文书
Redis命令处理过程源码解析
2022/02/12 Redis
python数字图像处理实现图像的形变与缩放
2022/06/28 Python