详解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扩展函数
Oct 09 PHP
收集的DedeCMS一些使用经验
Mar 17 PHP
php heredoc和phpwind的模板技术使用方法小结
Mar 28 PHP
php横向重复区域显示二法
Sep 25 PHP
PHP学习笔记之三 数据库基本操作
Jan 17 PHP
PHP开发中的错误收集,不定期更新。
Feb 03 PHP
调整优化您的LAMP应用程序的5种简单方法
Jun 26 PHP
一个典型的PHP分页实例代码分享
Jul 28 PHP
php根据分类合并数组的方法实例详解
Nov 06 PHP
CentOS6.5 编译安装lnmp环境
Dec 21 PHP
php计算2个日期的差值函数分享
Feb 02 PHP
php中使用base HTTP验证的方法
Apr 20 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
windows下开发并编译PHP扩展的方法
2011/03/18 PHP
PHP获取redis里不存在的6位随机数应用示例【设置24小时过时】
2017/06/07 PHP
解决laravel session失效的问题
2019/10/14 PHP
基于jquery实现点击左右按钮图片横向滚动
2013/04/11 Javascript
标题过长使用javascript按字节截取字符串
2014/04/24 Javascript
JavaScript获取表单内所有元素值的方法
2015/04/02 Javascript
javascript中$(function() {});写与不写有哪些区别
2015/08/10 Javascript
jquery+json实现分页效果
2016/03/07 Javascript
网页中JS函数自动执行常用三种方法
2016/03/30 Javascript
JavaScript获取键盘按键的键码(参照表)
2017/01/10 Javascript
Vue中添加过渡效果的方法
2017/03/16 Javascript
认识jQuery的Promise的具体使用方法
2017/10/10 jQuery
详解Vue Elementui中的Tag与页面其它元素相互交互的两三事
2018/09/25 Javascript
ES6的解构赋值实例详解
2019/05/06 Javascript
解决layui轮播图有数据不显示的情况
2019/09/16 Javascript
[01:10:30]DOTA2-DPC中国联赛正赛 Dragon vs Dynasty BO3 第一场 3月4日
2021/03/11 DOTA
Python使用Flask框架同时上传多个文件的方法
2015/03/21 Python
python控制nao机器人身体动作实例详解
2019/04/29 Python
Python如何读写字节数据
2020/08/05 Python
使用Python提取文本中含有特定字符串的方法示例
2020/12/09 Python
css3实现图片遮罩效果鼠标hover以后出现文字
2013/11/05 HTML / CSS
详解如何解决H5开发使用wx.hideMenuItems无效果不生效
2021/01/20 HTML / CSS
英国羊绒服装购物网站:Pure Collection
2018/10/22 全球购物
美国牛仔品牌:True Religion
2018/11/16 全球购物
BIFFI美国站:意大利BIFFI BOUTIQUES豪华多品牌时装零售公司
2020/02/11 全球购物
Jones Bootmaker官网:优质靴子和鞋子在线
2020/11/30 全球购物
购房意向书
2014/04/01 职场文书
先进工作者申报材料
2014/12/23 职场文书
小学二年级数学教学计划
2015/01/20 职场文书
项目建议书
2015/02/04 职场文书
大学生党员个人总结
2015/02/13 职场文书
廉洁自律个人总结
2015/02/14 职场文书
2015年电厂工作总结范文
2015/05/13 职场文书
迎新生晚会主持词
2015/06/30 职场文书
如何书写先进事迹材料?
2019/07/02 职场文书
MySQL数据库事务的四大特性
2022/04/20 MySQL