Database 查询构建器
上文我们说到执行 是由Connection对象执行table方法返回了一个QueryBuilder对象,QueryBuilder提供了一个方便的接口来创建及运行数据库查询语句,开发者在开发时使用QueryBuilder不需要写一行SQL语句就能操作数据库了,使得书写的代码更加的面向对象,更加的优雅。
通过上面的代码段可以看到Connection类的构造方法里除了注入了包装着Connector数据库连接器的闭包外 (就是参数里的 , 他是一个闭包,具体值在下面和上篇文章中都有提到) 还加载了两个重要的组件 :SQL语法编译器和 :SQL结果处理器。 我们看一下Connection的table方法,它返回了一个QueryBuilder实例, 其在实例化的时候Connection实例、Grammer实例和Processor实例会被作为参数传人QueryBuilder的构造方法中。
接下我们到QueryBuilder类文件 里看看它里面的源码
QueryBuilder构建SQL参数
下面再来看看where方法里都执行里什么, 为了方便阅读我们假定执行条件
所以上面 执行后QueryBuilder对象里的几个属性分别有了一下变化:
通过bindings属性里数组的key大家应该都能猜到如果执行select、orderBy等方法,那么这些方法就会把要绑定的值分别append到select和order这些数组里了,这些代码我就不贴在这里了,大家看源码的时候可以自己去看一下,下面我们主要来看一下get方法里都做了什么。
在执行get方法后,QueryBuilder首先会利用grammar实例编译SQL语句并执行,然后利用Processor实例处理结果集,最后返回经过处理后的结果集。 我们接下来看下这两个流程。
Grammar将构建的SQL参数编译成SQL语句
我们接着从 方法开始接着往下看Grammar类
在Grammar中,将SELECT语句分成来很多单独的部分放在了$selectComponents属性里,执行compileSelect时程序会检查QueryBuilder设置了$selectComponents里的哪些属性,然后执行已设置属性的编译器编译出每一部分的SQL来。还是用我们之前的例子 ,在这个例子中QueryBuilder分别设置了 (默认*)、 、 属性,那么我们见先来看看这三个属性的编译器:
compileColumns执行完后compileComponents里的变量$sql的值会变成 接下来看看 和 部分
每一种where查询(orWhere, WhereIn......)都有它自己的编译器函数来创建SQL语句,这帮助保持里代码的整洁和可维护性. 上面我们说过在执行 时$wheres属性里的值是:
在compileWheresToArray方法里会用$wheres中的每个数组元素去回调执行闭包,在闭包里:
然后根据type值把$where和QeueryBuilder作为参数去调用了Grammar的whereBasic方法:
whereBasic的返回为字符串 , compileWheresToArray方法的返回值为:
然后通过concatenateWhereClauses方法将compileWheresToArray返回的数组拼接成where语句
所以编译完 和 部分后compileComponents方法里返回的$sql的值会变成
然后在compileSelect方法里将这个由查查询语句里每部份组成的数组转换成真正的SQL语句:
得到 . 执行完了流程再回到QueryBuilder的 里:
Connection执行SQL语句
会获取要绑定到SQL语句里的值, 然后通过Connection实例的select方法去执行这条最终的SQL
在Connection的select方法里会把sql语句和绑定值传入一个闭包并执行这个闭包:
直到getPdoForSelect这个阶段Laravel才会连接上Mysql数据库:
我们在上一篇文章里讲过构造方法里 这个$pdo参数是一个包装里Connector的闭包:
所以在getPdo阶段才会执行这个闭包根据数据库配置创建连接器来连接上数据库并返回PDO实例。接下来的prepare、bindValues以及最后的execute和fetchAll返回结果集实际上都是通过PHP原生的PDO和PDOStatement实例来完成的。
通过梳理流程我们知道:
Laravel是在第一次执行SQL前去连接数据库的,之所以$pdo一开始是一个闭包因为闭包会保存创建闭包时的上下文里传递给闭包的变量,这样就能延迟加载,在用到连接数据库的时候再去执行这个闭包连上数据库。
在程序中判断SQL是否执行成功最准确的方法是通过捕获 异常
Processor后置处理结果集
processor是用来对SQL执行结果进行后置处理的,默认的processor的processSelect方法只是简单的返回了结果集:
之后在QueryBuilder的get方法里将结果集转换成了Collection对象返回给了调用者.
到这里QueryBuilder大体的流程就梳理完了,虽然我们只看了select一种操作但其实其他的update、insert、delete也是一样先由QueryBuilder编译完成SQL最后由Connection实例去执行然后返回结果,在编译的过程中QueryBuilder也会帮助我们进行防SQL注入。
领取专属 10元无门槛券
私享最新 技术干货