摘要:說明本文主要學(xué)習(xí)模塊的源碼。這里,就已經(jīng)得到了鏈接器實(shí)例了,該中還裝著一個(gè),下文在其使用時(shí)再聊下其具體連接邏輯。
說明:本文主要學(xué)習(xí)Laravel Database模塊的Query Builder源碼。實(shí)際上,Laravel通過Schema Builder來設(shè)計(jì)數(shù)據(jù)庫(kù),通過Query Builder來CURD數(shù)據(jù)庫(kù)。Query Builder并不復(fù)雜或神秘,只是在PDO擴(kuò)展的基礎(chǔ)上又開放封閉的包裝了一層,提供了fluent api,使得書寫的代碼也很簡(jiǎn)潔流暢。在看下Query Builder源碼之前,先大概探索下illuminate/database package的目錄結(jié)構(gòu)。
開發(fā)環(huán)境: Laravel5.3 + PHP7
Folder/File | Description |
---|---|
Capsule | Capsule文件夾下只有一個(gè)Manager類,主要實(shí)現(xiàn)了容器實(shí)例化,DatabaseManager和ConnectionFactory的實(shí)例化 |
Connectors | 里面包含了四種DB的鏈接器:MySQLConnector,PostgresConnector,SQLiteConnector,SqlServerConnector,是主要的組件之一,用來CRUD時(shí)鏈接對(duì)應(yīng)的DB |
Console | 該文件內(nèi)包含migration和seed的命令,如php artisan db:seed, php artisan migrate |
Eloquent | 該文件夾內(nèi)包含的就是Eloquent的主要實(shí)現(xiàn)類,如重點(diǎn)的Model類,Builder類,Relations子文件夾內(nèi)包含的表的關(guān)系類。是核心的組件,也是類最多的文件夾 |
Events | 裝載事件類的文件夾 |
Migrations | 實(shí)際執(zhí)行migrate相關(guān)命令的類 |
Query | Query Builder的代碼主要在這個(gè)文件夾,主要的類是Builder類,還包括Grammars和Processors兩大類別,根據(jù)四個(gè)不同的DB分門別類 |
Schema | 是設(shè)計(jì)database的主要參與類,主要的類是Builder類和Blueprint類,還有Grammars類別,根據(jù)四個(gè)不同的DB分門別類 |
Connection class | 數(shù)據(jù)庫(kù)鏈接類,封裝了PDO,是重要的類 |
DatabaseManager class | 在DatabaseServiceProvider注冊(cè)為"db",通常會(huì)通過該manager來"向下走"到對(duì)應(yīng)的數(shù)據(jù)庫(kù)實(shí)現(xiàn)類,是重要的類 |
Seeder class | 主要負(fù)責(zé)seed命令時(shí)的操作 |
Query Builder主要在Query文件夾下,以一行簡(jiǎn)單又經(jīng)常使用的代碼為例來學(xué)習(xí)下內(nèi)部實(shí)現(xiàn)的原理吧:
Route::get("/query_builder", function() { // Query Builder return DB::table("users")->where("id", "=", 1)->get(); }); // Illuminate/Support/Facades/DB class DB extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return "db"; } }
在DatabaseServiceProvider已經(jīng)注冊(cè)了名為"db"的服務(wù)即DatabaseManager對(duì)象,則實(shí)際上魔術(shù)調(diào)用DatabaseManager中的table()方法,看下__call()魔術(shù)方法源碼:
// $method = "table", $parameters = "users" public function __call($method, $parameters) { return $this->connection()->$method(...$parameters); }
所以重點(diǎn)是connection()方法,該方法返回的是Connection對(duì)象,看下connection()方法源碼:
public function connection($name = null) { // $name = "mysql", $type = null list($name, $type) = $this->parseConnectionName($name); // 首次在$connections[]中沒有"mysql" => $mysql_connection,所以需要根據(jù)配置創(chuàng)建對(duì)應(yīng)DB連接 if (! isset($this->connections[$name])) { // 重點(diǎn)是makeConnection()創(chuàng)建了mysql連接實(shí)例 $connection = $this->makeConnection($name); // 由于$type是null,不是"write"或"read",所以實(shí)際上啥也沒做 $this->setPdoForType($connection, $type); // 得到連接實(shí)例$connection后,還需要對(duì)該實(shí)例做準(zhǔn)備工作,如綁定事件,設(shè)置connector $this->connections[$name] = $this->prepare($connection); } return $this->connections[$name]; } protected function parseConnectionName($name) { $name = $name ?: $this->getDefaultConnection(); // 檢查是否以::read, ::write結(jié)尾 return Str::endsWith($name, ["::read", "::write"]) ? explode("::", $name, 2) : [$name, null]; } public function getDefaultConnection() { // laravel默認(rèn)是mysql,這里假定是常用的mysql連接 return $this->app["config"]["database.default"]; }
通過上面源碼知道重點(diǎn)是makeConnection($name)方法,該方法根據(jù)傳入的mysql名稱,來實(shí)例化出一個(gè)Connection對(duì)象,重點(diǎn)看下makeConnection()源碼:
protected function makeConnection($name) { // 從config/database.php中獲取"connections.mysql"的配置 $config = $this->getConfig($name); // 如果已經(jīng)自定義了連接,如在AppServiceProvider的boot()中又使用DatabaseManager::extend()方法自定義了一個(gè)"mysql"連接實(shí)例, // 那就用該實(shí)例,這里假設(shè)沒有自定義 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); }
實(shí)際上最后還是通過IlluminateDatabaseConnectorsConnectionFactory來解析出對(duì)應(yīng)的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是個(gè)閉包 $pdo = $this->createPdoResolver($config); return $this->createConnection( // $config["driver"] = "mysql", $config["database"] = "homestead"(數(shù)據(jù)庫(kù)名稱) $config["driver"], $pdo, $config["database"], $config["prefix"], $config ); } protected function createPdoResolver(array $config) { return function () use ($config) { return $this->createConnector($config)->connect($config); }; }
深入代碼發(fā)現(xiàn),最后是通過該工廠類的createConnection()方法來造出的一個(gè)Connection對(duì)象,createConnection()源碼就是常見的傻瓜式的工廠構(gòu)造函數(shù):
protected function createConnection($driver, $connection, $database, $prefix = "", array $config = []) { // 容器中已經(jīng)綁定了"db.connection.mysql"服務(wù)就解析出該服務(wù),這里是沒有注冊(cè)的 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這個(gè)對(duì)象了,DatabaseManager中的__call()方法中最后執(zhí)行的是(new MysqlConnection(*))->table("users")->where("id", 1)->get()。
OK, 這里注意下MySqlConnection的構(gòu)造參數(shù)$connection是個(gè)閉包,該閉包的值是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"]}]"); }
很簡(jiǎn)單就能知道該閉包一旦執(zhí)行時(shí),實(shí)際上執(zhí)行的行為類似于(new MySqlConnector)->connect($config)。
這里,就已經(jīng)得到了鏈接器實(shí)例MySqlConnection了,該connection中還裝著一個(gè)(new MySqlConnector)->connect($config),下文在其使用時(shí)再聊下其具體連接邏輯。
總結(jié):第一步數(shù)據(jù)庫(kù)連接實(shí)例化已經(jīng)走完了,已經(jīng)拿到了連接實(shí)例MySqlConnection,下一步將學(xué)習(xí)下connect()連接器是如何連接數(shù)據(jù)庫(kù)的,和如何編譯執(zhí)行SQL語句得到user_id為1的結(jié)果值。到時(shí)見。
RightCapital招聘Laravel DevOps
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/21959.html
摘要:,看下源碼返回很容易知道返回值是,然后將該值存儲(chǔ)在變量中,這時(shí)。看下的源碼去除掉字符后為返回從源碼中可知道返回值為,這時(shí)。 說明:本文主要學(xué)習(xí)下Query Builder編譯Fluent Api為SQL的細(xì)節(jié)和執(zhí)行SQL的過程。實(shí)際上,上一篇聊到了IlluminateDatabaseQueryBuilder這個(gè)非常重要的類,這個(gè)類含有三個(gè)主要的武器:MySqlConnection, M...
說明:本篇主要學(xué)習(xí)數(shù)據(jù)庫(kù)連接階段和編譯SQL語句部分相關(guān)源碼。實(shí)際上,上篇已經(jīng)聊到Query Builder通過連接工廠類ConnectionFactory構(gòu)造出了MySqlConnection實(shí)例(假設(shè)驅(qū)動(dòng)driver是mysql),在該MySqlConnection中主要有三件利器:IlluminateDatabaseMysqlConnector;IlluminateDatabaseQuery...
摘要:根據(jù)單一責(zé)任開發(fā)原則來講,在的開發(fā)過程中每個(gè)表都應(yīng)建立一個(gè)對(duì)外服務(wù)和調(diào)用。類似于這樣解析的數(shù)據(jù)操作分兩種它們除了有各自的特色外,基本的數(shù)據(jù)操作都是通過調(diào)用方法去完成整個(gè)。內(nèi)并沒有太多的代碼,大多都是處理數(shù)據(jù)庫(kù)鏈接。 showImg(https://segmentfault.com/img/bVbhjvY?w=600&h=296); 前言 提前預(yù)祝猿人們國(guó)慶快樂,吃好、喝好、玩好,我會(huì)在...
摘要:看下兩個(gè)方法的源碼同樣是使用了對(duì)象來添加命令和。 說明:本文主要學(xué)習(xí)Schema Builder和Migration System的使用及相關(guān)原理。傳統(tǒng)上在設(shè)計(jì)database時(shí)需要寫大量的SQL語句,但Laravel提供了Schema Builder這個(gè)神器使得在設(shè)計(jì)database時(shí)使用面向?qū)ο蠓椒▉碜觯恍枰獙懸恍蠸QL,并且還提供了另一個(gè)神器Migration System,可...
摘要:實(shí)際上的綁定主要有三種方式且只是一種的,這些已經(jīng)在學(xué)習(xí)筆記之實(shí)例化源碼解析聊過,其實(shí)現(xiàn)方法并不復(fù)雜。從以上源碼發(fā)現(xiàn)的反射是個(gè)很好用的技術(shù),這里給出個(gè),看下能干些啥打印結(jié)果太長(zhǎng)了,就不粘貼了。 說明:本文主要學(xué)習(xí)Laravel中Container的源碼,主要學(xué)習(xí)Container的綁定和解析過程,和解析過程中的依賴解決。分享自己的研究心得,希望對(duì)別人有所幫助。實(shí)際上Container的綁...
閱讀 1227·2021-11-12 10:34
閱讀 1133·2021-09-30 09:56
閱讀 802·2019-08-30 15:54
閱讀 2758·2019-08-30 11:14
閱讀 1658·2019-08-29 16:44
閱讀 3341·2019-08-29 16:35
閱讀 2621·2019-08-29 16:22
閱讀 2583·2019-08-29 15:39