在上目录中咔咔使用了query作为案例演示,这个使用在框架中是不建议使用的,因为在维护的方面会有一定的难度。
本节案例将会使用框架常用的查询数据库方式进行查询。
在上图中可以看到使用了平时最常用的查询方式,接下来将会对这组案例进行详细分析。
同样代码会来到Db类的__callStatic这个方法,这个方法就是在调用没有声明的静态方法会进行执行的。
这个方法跟__call方法是有区别的,__call方法是调用不存在的方法会进行调用,一定要注意俩者的区别。
对于上图方法中static::connect()执行最后会返回 object(think\db\Query)这个对象,至于内部流程的执行可以参考第二目录的内容。
所以执行流程会来到thinkphp/library/think/db/Query.php这个类的table方法。
参数就是table中传递的数据库表名tp_test。
按照上图提供的代码会对传递过来的表名进行三次判断。
根据传递过来的字符串以上三个判断均不成立,于是会执行到下面流程。
在table这个方法中可以看到最后的执行流程就是将传递过来的表名存放在属性options这个里边。
并且最后会将think\db\Query Object这个对象进行返回。
立即学习“PHP免费学习笔记(深入)”;
where方法解析
table方法分析完成后会紧接着执行where方法,同样还是在类thinkphp/library/think/db/Query.php
上图中在这个类中可以看到一个方法func_get_args,这个方法会返回一个包含函数参数列表的数组。
这个方法平时都是跟call_user_func_array同时使用,之前咔咔也使用这俩个方法进行过一次案例实验。
然后会使用函数array_shift删除数组中的第一个元素(red),并返回被删除元素的值。
下图第一个结果为func_get_args这个方法获取出来的数据,第二组结果为array_shift这个方法返回的结果。
俩组结果返回的值可以进行对比一下,可以更好的理解array_shift的使用场景。
紧接着会进行分析查询表达式,也就是方法parseWhereExp做的事情。
在这个方法中需要注意一个点就是关于传递过来的这俩个参数。
参数一为查询逻辑,参数二就是在使用案例时传入的参数。
在代码的第一行就需要我们来学习的一个知识点instanceof。
instanceof可以判断某个对象是否是某个类的实例,判断一个对象是否实现了某个接口。
关于这个的使用案例在文章ThinkPHP源码解析之控制器这一文中做了详细的说明。
根据学习instanceof的作用可以清晰的明白第一个判断不会进行执行。
在继续学习以下的执行流程,根据咔咔圈出来的框来进行对代码进行简单的解析。
根据上图首先会对查询逻辑的符号全部转为小写
然后在进行判断$field instanceof Where传递过来的参数是否为Where类的实例。
最后一个判断就是$field instanceof Expression跟上一步是判断同样的功能。
所以说代码最终的执行逻辑就是下图圈到的部分。
还记得在案例过程中给where传递的参数就是一个数组。
如果将参数改为where('t_id',1)则就会走is_string($field)的这个流程,这个流程就交给大家了,咔咔就不去解析。
这里咔咔还是使用数组作为参数进行解析,那么代码依然会执行本类的parseArrayWhereItems这个方法
在这个方法中先需要知道key会返回什么,从当前内部指针位置返回元素键名。
所以代码会去执行if语句的判断,根据上边的所有判断都不符合所以会执行这段代码$where[] = [$key, is_array($val) ? 'IN' : '=', $val];
这段代码会判断循环数组的value值是否为数组,如果为数组就是in,反之为=,由于value为1所以数组的第二个值为=。
那么最终where的值就是下图打印的数据。
由于where不为空,代码执行流程会执行到下图位置,最终在返回本类实例。
find()执行流程
接着代码会还是执行本类的find方法,查找单条记录。
由于find中是没有传递参数的,所以代码会执行到$this->parseOptions();分析表达式(可用于查询或者写入操作)
就目前写的案例而言,这段看似很长的代码大家好好看看都可以看明白,最终依然是返回当前的所有参数。
以下就是返回的所以结果
真正的查询数据是这块代码$result = $this->connection->find($this);,这段代码会执行到文件thinkphp/library/think/db/Connection.php
从这块代码可以看到当查询一条数据时框架默认给加上了limit为1,至于为什么这么加你就需要查看一下sql优化方面的知识了。
在这里就是关于sql语句的生成,代码自己好好看看就会明白,咔咔解析的只是执行流程和具体代码简单的了解一下即可。
至于具体实现流程咔咔在后期如果有机会会单独把每个方法进行深度解析,那时就是主要针对代码的解析。
最终返回结果如下
以上就是关于Db在结合连接器,查询器,生成器实现的数据库查询功能。
截止到这里关于Db的场景就分析到这里,接下来咔咔将会对Model进行简单的分析。
还是之前的案例,我们来使用这个方法打印一下结果来看一下是什么。
看到上图就知道是框架最终给生成的SQL语句,那么接下来咔咔就会带大家一起来探讨一下,这个sql语句是如何生成的。
下图为本次演示的案例,也就是咔咔下图圈出来的地方。
从上图圈出来的地方进行代码追踪会到文件thinkphp/library/think/Db.php,并且会去执行本类的__callStatic方法,这个方法就不在进行解释了,在上文和之前也已经提到过多次了。
并且返回结果也不去做声明了,上文也提到了,这里只需要知道最终返回结果为返回 object(think\db\Query)
根据上图的返回结果可以知道最终回去调用object(think\db\Query)这个类的getLastSql这个方法
根据这个方法可以知道是获取最近一次查询的sql语句。
这里就会有点疑问了,关于属性connection到底是什么,这里在进行一次简单的简析。
关于这种属性的声明一般都会在本类的构造函数或者父类的构造函数中进行声明,这也是在阅读源码时的一个小窍门。
于是我们首先就需要来到本类的构造函数来看一眼。
通过上图可以得知,此处使用了依赖注入的方式,所以Connection就是一个对象,并且也是框架中所谓的连接器。
所以说这个Connection对象就是下图打印出来的。
根据上图得知使用的类文件应该就是think\db\connector\Mysql那么就会执行这个类里边的getLastSql方法。
但是来到这个类执行你会发现这个类里边根本是没有这个方法。
根据上图的继承关系,我们就知道这个方法是在thinkphp/library/think/db/Connection.php这个类文件里边。
下图即是这个方法执行过程,可以看到存在俩个参数,但是这俩个参数还是一头雾水根本不知道是什么。
根据代码追踪我们对上图所出现的俩个参数先进行简单的说明
追踪$this->queryStr这个属性值
走到这里估计有点蒙了吧!对于这个值有点确定不了了,指定不是靠打印可以获取到结果的。
当然还有另一种办法就是进行debug来断点调试。
但是既然咔咔带大家看源码呢!就不会用上边的俩种方式,会直接从源码中找到蛛丝马迹。
根据咔咔上边给提供的案例,执行的最后一步就是find方法,这个方法也是在thinkphp/library/think/db/Connection.php这个类里边,寻找单条记录。
那么我们就在这个方法中进行一点点的寻找,这里咔咔已经给大家圈好了,就是下图咔咔圈其起来的地方。
根据上图咔咔给的代码注释,第一个参数就是生成的SQL语句,来继续追踪这个方法看一下,此时这个方法依然会在本类thinkphp/library/think/db/Connection.php这个文件中实现query方法。
在这个方法中一眼就可以看见对于这个queryStr属性的设置,是直接给这个属性赋值,那么也就是说这个属性的值就是上一个SQL语句生成的SQL语句。
所以说这个getLastSql获取的就是在这个语句之前执行的SQL语句,也只能获取出最近执行的那个SQL语句。
以上就是关于getLastSql的实现原理,在这里需要注意的就是关于SQL的生成,这块属实有点复杂。
截止到这里关于数据库中Db类的操作场景分析以及关于结合连接器,查询器,生成器就到这里结束了。
这里咔咔主要就是使用了俩种案例来进行执行,第一种为原生案例,第二种为框架封装的案例。
使用了这俩种案例来对源码进行了深度解析,但是在文档还有很多的实现方法,其它的方法只需要根据咔咔给的提示然后一点点解析即可。
不需要对所有的方法都进行执行,不管任何方法走的都是上文分析的方法,也是很简单。
最后在演示了一下关于使用getLastSql来获取最后一次执行的SQL语句查询,这里的实现原理主要就是在Db类操作数据库时,不管是使用find方法还是select方法最终都会走向一个方法那就是query方法。
同样在这个方法中存在一个属性值queryStr,也就是在这个时候将SQL语句赋值进去的,然后在使用getLastSql这个方法使用queryStr和bind属性在对SQL进行拼接,最总返回SQL语句。
坚持学习、坚持写博、坚持分享是咔咔从业以来一直所秉持的信念。希望在偌大互联网中咔咔的文章能带给你一丝丝帮助。我是咔咔,下期见。
一、Db类库巧妙结合连接器、查询器、sql生成器使用二、关于getLastSql的实现过程总结