整个laravel的操作,一般情况下,数据库的处理会占掉很大一部分。所以对数据 库处理的理解,显得尤为重要。关于其源码解析,网上有非常多的文献,但流程一般都含糊其辞,读完来龙去脉甚为不解。所以,我自己做了一次流程分析,并记录下全过程。
Laravel对不同数据库连接的实例封装了对应连接的PDO类,为上层使用数据库连接实例提供了统一的接口。我这里源码分析,都以以mySql为实例进行讲解,过程大致如下,
DB::table('users') --> 拿到PDO --> 调用核心类Illuminate\Database\Connection
这里先局部后全局,分三部分讲解。
- 数据库的连接与PHP-PDO的关系:解释laravel与数据库的接洽点;
- 数据库查询构造流程:理解是如何最后实现调用核心类Connection::table()函数的;
- 数据库启动大脉络分析:理解全流程,然后重点是如何拿到PDO的。
另外,例子中用到的laravel的门面(Facades)模式,原理比较简单,可以参考其官方文档。
说在前面的话Laravel目前支持的有四类数据库,laravel中对应的名称分别为:mysql,pgsql,sqlite,sqlsrv,即MySQL、Postgres、SQLite和SQL Server;同时,laravel还支持用户算定的数据库和驱动程序。
当操作数据库的查询构造器时,可以使用类似
DB::table('users')->get();
DB::table('users')->select();
DB::table('users')->insert();
DB::update();
语法,其中
DB::table('users')
部分就是获取查询构造器,后面的“->get()”等调用查询构造的方法实现相应数据操作。后面我们会讲到(详见第三节),这些查询会通过DatabaseManager::connection()再调用各个$methods。
public function __call($method, $parameters)
{
return $this->connection()->$method(...$parameters);
}
查询构造器的建立过程分为两个阶段:一个是数据库连接封装阶段,另一个是查询构造器生成阶段。
数据库连接封装又可以分为四个步骤:
一、数据库管理器阶段,在DatabaseServiceProvider类中的registerConnectionServices()函数中创建ConnectionFactory实例;
Laravel首先通过服务提供者“Illuminate\Database\DatabaseServiceProvider”注册了数据库管理服务(“DB”服务)和数据库连接工厂服务(“db.factory”服务),通过上述服务获取数据库管理DatabaseManager类和数据库连接工厂实例ConnectionFactory类的实例,其中数据库连接工厂实例作为数据库管理器实例的一个属性,在DatabaseServiceProvider类中的registerConnectionServices()函数中创建ConnectionFactory实例。
二、数据库连接工厂阶段,这一阶段主要是为连接数据库作配置准备,并生成连接器MySqlConnector;为了对上层提供统一的接口,Laravel在底层根据不同的配置调用了不同的数据库驱动扩展,框架上使用了简单工厂设计模式,用来根据配置文件获取不同的数据库连接实例。
三、数据库连接器阶段,连接器MySqlConnector会创建连接,并调用其子函数::createConnection() 和 ::createPdoConnection();Laravel针对不同的数据库有不同的实现,主要包括连接DSN名称及配置等。Laravel框架用四个类分别封装了默认支持的四个数据库连接的过程,通过connect()方法提供统一的接口。
四、数据库连接创建阶段,在这个阶段MySqlConnector的父类Connector会生成PDO实例,并完成连接。本质上,不同数据库连接的实例就是封装了对应连接的PDO类实例、请求语法类实例、和结果处理类实例,从而为上层使用数据库连接实例提供统一的接口。
第一节,数据库的连接与PHP-PDO的关系首先,我们要知道,数据最终是在类Illuminate\Database\Connectors\Connector.php中完成链接的。我们先分析一下其源码:
class Connector
{
use DetectsLostConnections;
//下面是连接时默认用到的参数,当然你可以在创建联接时更改
protected $options = [
PDO::ATTR_CASE => PDO::CASE_NATURAL, // 保留数据库驱动返回的列名,不强制列名为指定的大小写
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 抛出 exceptions 异常
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, // 不转换 NULL 和空字符串
PDO::ATTR_STRINGIFY_FETCHES => false, // 提取的时候将数值转换为字符串? => 不转换
PDO::ATTR_EMULATE_PREPARES => false, // 禁用预处理语句的模拟
];
// 尝试建立一个连接
public function createConnection($dsn, array $config, array $options)
{
// 先拿到连接数据库所需要的用户名和密码,这个一般在.env中设置,你可以看到有以下3项,
// DB_DATABASE=laraveldb DB_USERNAME=username DB_PASSWORD=password
list($username, $password) = [
$config['username'] ?? null, $config['password'] ?? null,
];
// 尝试调用实际建立连接的createPdoConnection函数,注意上面的$options已经作为设置参数传入
try {
return $this->createPdoConnection(
$dsn, $username, $password, $options
);
} catch (Exception $e) {
return $this->tryAgainIfCausedByLostConnection(
$e, $dsn, $username, $password, $options
);
}
}
// 实际建立数据库连接的函数
protected function createPdoConnection($dsn, $username, $password, $options)
{
if (class_exists(PDOConnection::class) && ! $this->isPersistentConnection($options)) {
return new PDOConnection($dsn, $username, $password, $options);
}
return new PDO($dsn, $username, $password, $options);
}
大致上,createPdoConnection会检查有没有PDOConnection这个类,实际上在laravel提供的默认源码中这个类是不存在的,你可以检查一下你的composer.json文件。如果想安装使用doctrine,可以参考以下官网
https://www.laraveldoctrine.org/docs/1.3/orm/installation
言归正传,createPdoConnection找不到PDOConnection这个类,就会调用后面那句
return new PDO($dsn, $username, $password, $options);
其中$dsn就是我们在.env中设置的数据库地址
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
如果打印出来,就是一段字符串,如下,
'mysql:host=127.0.0.1;:port=3306;dbname=laraveldb'
这里值得一提的是PDO,PDO是个什么东西?PDO是PHP提供的数据对象扩展(PHP Data Object),它为PHP访问数据库提供了一套轻量级的接口,从PHP5.1版以后开始提供。你可以参考官方网站,
http://php.net/manual/zh/book.pdo.php
因此不难明白,所谓的laravel连接数据库,只不过是调用了PHP中的PDO(或者说该类)的API函数,并进行一系列的操作的过程。同样,Qureybuilder的相关API,也只不过是PDO的一层封装外衣!
这里需要进一步说明的是,这个PDO创建之后,是直接返回给变量$connection的。以mySql为例,
namespace Illuminate\Database\Connectors;
use PDO;
class MySqlConnector extends Connector implements ConnectorInterface
{
public function connect(array $config)
{
$dsn = $this->getDsn($config);
$options = $this->getOptions($config);
$connection = $this->createConnection($dsn, $config, $options); // 在这里创建connection
if (! empty($config['database'])) {
$connection->exec("use `{$config['database']}`;");
}
$this->configureEncoding($connection, $config);
$this->configureTimezone($connection, $config);
$this->setModes($connection, $config);
return $connection;
}
...
}
这个类MySqlConnector是Illuminate\Database\Connectors\Connector的子类。
第二节 laravel中数据库查询构造流程当 connection 对象构建初始化完成后,我们可以用 DB 来进行数据库的 增删改查(CRUD,即( Create、Retrieve、Update、Delete)等操作。laravel的查询构造器让我们避免使用原生的sql语句,而是用一种语法上更容易理解的方式操作(Laravel官方称这样可以避免漏洞),例如
DB::table('table')->select('*')->where('user_id', 1);
第一,查询构造的这个过程是如何实现的呢?
当然,这种看似静态调用的方法,其实是laravel里的门面模式,实际上调用的并不是静态方法。这个简单的模式只有几行代码,
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?