详解PHP的Laravel框架中Eloquent对象关系映射使用
零、什么是 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;
然后你就会看到下面的错误:
为什么会出现错误?因为 `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 条,性能大增。
上一篇: java集合类源码分析之Set详解
推荐阅读
-
详解PHP的Laravel框架中Eloquent对象关系映射使用
-
详解PHP的Laravel框架中Eloquent对象关系映射使用
-
详解PHP的Laravel框架中Eloquent对象关系映射使用,laraveleloquent_PHP教程
-
关于PHP的Laravel框架中Eloquent对象关系映射的使用
-
详解PHP的Laravel框架中Eloquent对象关系映射使用,laraveleloquent
-
详解PHP的Laravel框架中Eloquent对象关系映射使用_php技巧
-
详解PHP的Laravel框架中Eloquent对象关系映射使用_PHP
-
关于PHP的Laravel框架中Eloquent对象关系映射的使用
-
详解PHP的Laravel框架中Eloquent对象关系映射使用_PHP
-
详解PHP的Laravel框架中Eloquent对象关系映射使用,laraveleloquent