laravel 5.1 查询底层原理 (Query Builder) 源码解析(上)
来源:互联网 发布:纪晓波 吴佩慈 知乎 编辑:程序博客网 时间:2024/06/06 07:28
原文地址
说明:本文主要学习Laravel Database模块的Query Builder源码。实际上,Laravel通过Schema Builder来设计数据库,通过Query Builder来CURD数据库。Query Builder并不复杂或神秘,只是在PDO扩展的基础上又开放封闭的包装了一层,提供了fluent api,使得书写的代码也很简洁流畅。在看下Query Builder源码之前,先大概探索下illuminate/database package
的目录结构。
开发环境: Laravel5.3 + PHP7
数据库连接的实例化
Query Builder主要在Query文件夹下,以一行简单又经常使用的代码为例来学习下内部实现的原理吧:
Route::get('/query_builder', function() { // Query Builder return DB::table('users')->where('id', '=', 1)->get();});// Illuminate/Support/Facades/DBclass DB extends Facade{ /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'db'; }}
在DatabaseServiceProvider已经注册了名为'db'的服务即DatabaseManager对象,则实际上魔术调用DatabaseManager中的table()方法,看下__call()
魔术方法源码:
// $method = 'table', $parameters = 'users' public function __call($method, $parameters) { return $this->connection()->$method(...$parameters); }
所以重点是connection()方法,该方法返回的是Connection对象,看下connection()方法源码:
public function connection($name = null) { // $name = 'mysql', $type = null list($name, $type) = $this->parseConnectionName($name); // 首次在$connections[]中没有'mysql' => $mysql_connection,所以需要根据配置创建对应DB连接 if (! isset($this->connections[$name])) { // 重点是makeConnection()创建了mysql连接实例 $connection = $this->makeConnection($name); // 由于$type是null,不是'write'或'read',所以实际上啥也没做 $this->setPdoForType($connection, $type); // 得到连接实例$connection后,还需要对该实例做准备工作,如绑定事件,设置connector $this->connections[$name] = $this->prepare($connection); } return $this->connections[$name]; } protected function parseConnectionName($name) { $name = $name ?: $this->getDefaultConnection(); // 检查是否以::read, ::write结尾 return Str::endsWith($name, ['::read', '::write']) ? explode('::', $name, 2) : [$name, null]; } public function getDefaultConnection() { // laravel默认是mysql,这里假定是常用的mysql连接 return $this->app['config']['database.default']; }
通过上面源码知道重点是makeConnection($name)
方法,该方法根据传入的mysql
名称,来实例化出一个Connection对象,重点看下makeConnection()
源码:
protected function makeConnection($name) { // 从config/database.php中获取'connections.mysql'的配置 $config = $this->getConfig($name); // 如果已经自定义了连接,如在AppServiceProvider的boot()中又使用DatabaseManager::extend()方法自定义了一个'mysql'连接实例, // 那就用该实例,这里假设没有自定义 if (isset($this->extensions[$name])) { return call_user_func($this->extensions[$name], $config, $name); } // $driver = 'mysql' $driver = $config['driver']; if (isset($this->extensions[$driver])) { return call_user_func($this->extensions[$driver], $config, $name); } // 通过ConnectionFactory类工厂模式获取Mysql的连接类 return $this->factory->make($config, $name); }
实际上最后还是通过\Illuminate\Database\Connectors\ConnectionFactory
来解析出对应的connection,这里使用了工厂模式,看下该工厂类的make()
方法源码:
public function make(array $config, $name = null) { $config = $this->parseConfig($config, $name); if (isset($config['read'])) { return $this->createReadWriteConnection($config); } return $this->createSingleConnection($config); } protected function createSingleConnection(array $config) { // $pdo是个闭包 $pdo = $this->createPdoResolver($config); return $this->createConnection( // $config['driver'] = 'mysql', $config['database'] = 'homestead'(数据库名称) $config['driver'], $pdo, $config['database'], $config['prefix'], $config ); } protected function createPdoResolver(array $config) { return function () use ($config) { return $this->createConnector($config)->connect($config); }; }
深入代码发现,最后是通过该工厂类的createConnection()
方法来造出的一个Connection对象,createConnection()
源码就是常见的傻瓜式的工厂构造函数:
protected function createConnection($driver, $connection, $database, $prefix = '', array $config = []) { // 容器中已经绑定了'db.connection.mysql'服务就解析出该服务,这里是没有注册的 if ($this->container->bound($key = "db.connection.{$driver}")) { return $this->container->make($key, [$connection, $database, $prefix, $config]); } // $driver = 'mysql' switch ($driver) { case 'mysql': return new MySqlConnection($connection, $database, $prefix, $config); case 'pgsql': return new PostgresConnection($connection, $database, $prefix, $config); case 'sqlite': return new SQLiteConnection($connection, $database, $prefix, $config); case 'sqlsrv': return new SqlServerConnection($connection, $database, $prefix, $config); } throw new InvalidArgumentException("Unsupported driver [$driver]"); }
总之,通过以上一步步分析就拿到了Connection这个对象了,DatabaseManager中的__call()
方法中最后执行的是(new MysqlConnection(*))->table('users')->where('id', 1)->get()
。
OK, 这里注意下MySqlConnection的构造参数$connection是个闭包,该闭包的值是ConnectionFactory::createPdoResolver()的返回值,看下闭包里的操作:
protected function createPdoResolver(array $config) { return function () use ($config) { return $this->createConnector($config)->connect($config); }; } public function createConnector(array $config) { if (! isset($config['driver'])) { throw new InvalidArgumentException('A driver must be specified.'); } if ($this->container->bound($key = "db.connector.{$config['driver']}")) { return $this->container->make($key); } switch ($config['driver']) { case 'mysql': return new MySqlConnector; case 'pgsql': return new PostgresConnector; case 'sqlite': return new SQLiteConnector; case 'sqlsrv': return new SqlServerConnector; } throw new InvalidArgumentException("Unsupported driver [{$config['driver']}]"); }
很简单就能知道该闭包一旦执行时,实际上执行的行为类似于(new MySqlConnector)->connect($config)
。
这里,就已经得到了链接器实例MySqlConnection了,该connection中还装着一个(new MySqlConnector)->connect($config)
,下文在其使用时再聊下其具体连接逻辑。
总结:第一步数据库连接实例化已经走完了,已经拿到了连接实例MySqlConnection,下一步将学习下connect()连接器是如何连接数据库的,和如何编译执行SQL语句得到user_id为1的结果值。到时见。
- laravel 5.1 查询底层原理 (Query Builder) 源码解析(上)
- laravel 5.1 查询底层原理 (Query Builder) 源码解析(中)
- laravel 5.1 查询底层原理 (Query Builder) 源码解析(下)
- 聊一聊laravel query builder 使用子查询
- laravel 5.5 -- Query Builder
- Laravel学习笔记(五)---操作数据库--查询构建器(Query Builder)
- ORMLite完全解析(三)官方文档第三章、自定义查询构造器 Custom Query Builder
- Yii - Query Builder and Query(查询生成器)
- Laravel源码(流程)解析
- AOP底层原理解析
- Yii查询生成器(Query Builder)用法实例教程
- Yii查询生成器(Query Builder)的用法
- Yii查询生成器(Query Builder)用法实例教程
- EventBus框架原理解析(结合源码)(上)
- 196 laravel 源码解析-3
- ArrayList底层实现源码解析
- yii Query Builder (yii 查询构造器) 官方指南翻译
- laravel 学习笔记 —— 查询构造器(上)
- python数据输入caffe实现回归
- C:数组实现十进制转换二进制
- JSON JSONArray 创建JSON 和 解析JSON
- 很好的sql去重
- 算法导论程序10--建堆(Python)
- laravel 5.1 查询底层原理 (Query Builder) 源码解析(上)
- Netty学习之二--Java socket编程(UDP实例)
- Swiper的API及自定义分页器等问题-淘宝触屏版首页制作
- Maven开发笔记(一)—— Maven中的依赖作用范围(scope)
- 训练自己的分类数据(only cpu)
- 阿里云搭建基于MatlabMPI的集群(二):Ubuntu16.04基于VNC的图形界面安装
- 射频/微波/毫米波/太赫兹闲说
- centos7安装python-pip
- 4——字符串分隔