php orm 映射原理,深入理解laravel中的Eloquent ORM、查询构建器的核心原理

一、基础魔术方法:__call和__callstatic

深入理解Eloquent ORM之前,先让我们来理解两个基础的PHP魔术方法:

1.__call():当调用类中不存在的方法时,就会调用__call();

2.__callstatic:当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用。

二、引用到的基础概念:new self()和new static()

无论是new static()还是new self(),都是new了一个新的对象。他们的区别只有在继承中才能体现出来,如果没有任何继承,那么这两者是没有区别的。new self()返回的实例是万年不变的,无论谁去调用,都返回同一个类的实例,而new static()则是由调用者决定的。

说白了,就是继承同一个父类的两个子类,父类中new static 得到的是子类对象,new self得到的是父类自身。以上这两个基础概念很重要,一定要掌握,如果不清楚可以查阅其他资料,不然咱们的Eloquent ORM就没法理解了。

三、laravel数据库操作的几个核心概念

laravel主要提供了DB门脸类原生SQL操作和Eloquent ORM两种操作数据库的方式,两种操作都提供了相应的操作数据库的链式接口,因此,我们先把这些基础的概念先罗列出来,便于后面从底层去研究实现原理。

1.链式操作实现:这个的实现很简单,就是在每个可以链式操作的方法最后返回一个$this,这样,一个类中所有返回$this的方法就构成一条操作链,这样laravelAPI文档中,所有返回值是this的方法,都可以支持链式,比如这样一条语句:

$user = DB::table('users')->where('name', 'xxx')->first();

table返回一个Builder构建器实例,where和first都是构建器的方法,where返回$this,$this调用first,有多个链式方法继续下去,都可以在后面一个一个的串起来。

2.查询构建器:其实就是一个操作数据库的方法类,里面提供了几乎所有的数据库操作方法,值得一提的是,laravel的构建器有两种,对应数据库操作的两种方式,一种是原生SQL操作的查询构建器,一种是Eloquent ORM的查询构建器,Eloquent ORM查询构建器实际上也是调用前一种构建器来实现数据库操作,只是针对模型关系进一步封装。

针对原生SQL的查询构建器对应的是这个类:Illuminate\Database\Query\Builder,

而针对Eloquent ORM的构建器对应的是这个类:Illuminate\Database\Eloquent\Builder,

两种构建器提供的方法很像,同时Eloquent查询构建器提供的方法的实现也是基于前一个查询构建器。但是这毕竟不是同一个查询构建器,因此,DB::table('...')这样的方式返回的构建器是Illuminate\Database\Query\Builder,可以用Illuminate\Database\Query\Builder内提供的所有方法,而在模型实例(可以当做构建器实例,至于为什么下面会讲解)可以使用的构建器方法是Illuminate\Database\Eloquent\Builder提供的,不要混为一谈。

在此强调:所谓查询构建器,就是操作数据库的方法拢在一起组成个一个类!

在laravel的ORM中,一个类对应一张表,而一个模型可以当做一个Illuminate\Database\Eloquent\Builder的构建器使用(注意注意:不是Illuminate\Database\Query\Builder),所以,模型实例可以直接调用Illuminate\Database\Query\Builder中提供的操作数据表的方法,下一节会讲述具体如何实现。

3.集合与Eloquent ORM:在常用的Eloquent 模型方法中,只有模型中的all()、newCollection()和构建器中的get()、findMany()这几个方法返回的是集合或模型数组(其他的方法不常用,另外几个不常用的返回集合方法可以详细阅读laravelAPI),可以使用相应的集合方法进行操作,其他的一般返回的都是查询构建器。

这里提醒下初学laravel滴同学,返回集合的方法,和返回查询构建器的方法,是不能链在一起操作的!所以,在all()方法后不能支持模型的链式操作,比如:App\Filight::all()->where()....这就是错误的,因为all()返回的是集合,而where是查询构建器方法,两者风马牛不相及,所以,优雅的laravel并不是什么都可以链式,一定要注意这个中的不同。

至于如何判定哪些方法返回的是集合,哪些方法放回的是构建器,可以查看laravelAPI:

Builder:https://laravel.com/api/5.7/Illuminate/Database/Eloquent/Builder.html

Model:https://laravel.com/api/5.7/Illuminate/Database/Eloquent/Model.html

看这两个类方法中,返回的是Builder的,就是构建器方法,Collection的就是集合。

四、为什么Eloquent 模型可以当做查询构建器来用?

这是一个很核心的思路,弄清了这个思路,对laravel的Eloquent底层的核心实现会有很清晰的认识,实际上,这才是本文的一道压轴菜,手册中对这个概念讲解的很简单,网络上系统讲解的不多,因此我才会在前面不厌其烦的把一些基础概念拿出来讲,这样大家可以借助前面的铺垫,来一起啃一啃这块大骨头。

我们先来看一个简单的Eloquent模型应用:

$flights = App\Flight::where('active', 1) ->orderBy('name', 'desc')->take(10)->get();

上面这条语句很简单,就是从Flight模型中找出'active'等于1的数据,然后按照name排序,在取出10条,用get返回一个集合,这是手册中的代码,如果不清楚可以去查阅手册。

现在,我们来看一下每个模型,都会继承的基类:Illuminate\Database\Eloquent\Model这个类(vendor这个目录中自己往下翻喽),可是翻遍整个Model类,1600多行的代码中,怎么也找不到where,orderBy,take,get这4个方法,怎么回事?既然Flight模型实例明明可以调用这四个方法,就说明Flight类中肯定有这四个方法的定义,为什么我们在类里面没有看到这四个方法呢?四个方法是哪来的?凭空生成的?有违常理啊!

这时候,我们想到了前面基础中讲到的__call()和__callstatic()这两个魔术方法,也就是说,如果实例调用的方法在类定义中不存在,就会分别调用这两个方法,那我们来看看Model父类中这两个方法的代码:

public function__call($method, $parameters)

{

if (in_array($method, ['increment', 'decrement'])) {  return $this->$method(...$parameters);}

return $this->forwardCallTo($this->newQuery(), $method, $parameters);

}

public static function __callStatic($method, $parameters)

{

return (new static)->$method(...$parameters);

}

看到没,魔术在这里。

先看__callstatic中标黑色的代码,return (new static)->$method(...$parameters);加入我们调用一个不存在的静态方法,就直接new static一个实例出来,然后调用这个实例的同名方法,然而,这个实例的同名方法也不存在啊。没关系,实例同名方法不存在就会调用__call方法,同时把参数传进去。

这下有点明白了吧?也就是说:Flight::where()执行的时候,laravel一看,哇,没有where方法,那就执行__callStatic()魔术方法,然后执行(new static)->$method(...$parameters);也就是Flight实例->where()方法,然后再去找一遍,实例方法里面还是没有where,然后执行__call()方法,现在我们来看看__call()到底做了什么事情:

return $this->forwardCallTo($this->newQuery(), $method, $parameters);

一看这句代码,就豁然开朗了,__call方法就是创建一个查询构建器Illuminate\Database\Eloquent\Builder的实例,然后把相应的查询方法名where和参数传进去,这样就可以带着参数执行$this->newQuery()这个产生的查询构建器的where方法了。

到此,整个Eloquent ORM核心的关卡也就打通了,剩下newQuery具体怎么实现,forwardCallTo怎么实现,都是小事情啦,其实也就是执行一个创建查询构建器,调用查询构建器方法的过程,细节可以详细阅读源码,其实,就算不阅读,能理解ELoquentORM的关键环节,在应用上也就基本足够了。

五、小结

通过前面的讲述,我相信大家已经基本能够明白ELoquentORM中可能会遇到难点,模型操作的基础细节其实很重要,比如,没有分清楚两种查询构建器的区别,就很可能会在模型的构建器中使用原生构建器中的方法,然后纠结不知道问题出错在哪里,为什么ELoquentORM模型可以当做查询构建器用,但是ELoquentORM模型又不是查询构建器,弄通这篇文章的内容,相信会让您对ELoquentORM模型有个深入的体会。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值