国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Laravel學習筆記之Query Builder源碼解析(上)

Steve_Wang_ / 2142人閱讀

摘要:說明本文主要學習模塊的源碼。這里,就已經得到了鏈接器實例了,該中還裝著一個,下文在其使用時再聊下其具體連接邏輯。

說明:本文主要學習Laravel Database模塊的Query Builder源碼。實際上,Laravel通過Schema Builder來設計數據庫,通過Query Builder來CURD數據庫。Query Builder并不復雜或神秘,只是在PDO擴展的基礎上又開放封閉的包裝了一層,提供了fluent api,使得書寫的代碼也很簡潔流暢。在看下Query Builder源碼之前,先大概探索下illuminate/database package的目錄結構。

開發環境: Laravel5.3 + PHP7

Folder/File Description
Capsule Capsule文件夾下只有一個Manager類,主要實現了容器實例化,DatabaseManager和ConnectionFactory的實例化
Connectors 里面包含了四種DB的鏈接器:MySQLConnector,PostgresConnector,SQLiteConnector,SqlServerConnector,是主要的組件之一,用來CRUD時鏈接對應的DB
Console 該文件內包含migration和seed的命令,如php artisan db:seed, php artisan migrate
Eloquent 該文件夾內包含的就是Eloquent的主要實現類,如重點的Model類,Builder類,Relations子文件夾內包含的表的關系類。是核心的組件,也是類最多的文件夾
Events 裝載事件類的文件夾
Migrations 實際執行migrate相關命令的類
Query Query Builder的代碼主要在這個文件夾,主要的類是Builder類,還包括Grammars和Processors兩大類別,根據四個不同的DB分門別類
Schema 是設計database的主要參與類,主要的類是Builder類和Blueprint類,還有Grammars類別,根據四個不同的DB分門別類
Connection class 數據庫鏈接類,封裝了PDO,是重要的類
DatabaseManager class 在DatabaseServiceProvider注冊為"db",通常會通過該manager來"向下走"到對應的數據庫實現類,是重要的類
Seeder class 主要負責seed命令時的操作
數據庫連接的實例化

Query Builder主要在Query文件夾下,以一行簡單又經常使用的代碼為例來學習下內部實現的原理吧:

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已經注冊了名為"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);
    }

實際上最后還是通過IlluminateDatabaseConnectorsConnectionFactory來解析出對應的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的結果值。到時見。

RightCapital招聘Laravel DevOps

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/21959.html

相關文章

  • Laravel 學習筆記 Query Builder 源碼解析(下)

    摘要:,看下源碼返回很容易知道返回值是,然后將該值存儲在變量中,這時。看下的源碼去除掉字符后為返回從源碼中可知道返回值為,這時。 說明:本文主要學習下Query Builder編譯Fluent Api為SQL的細節和執行SQL的過程。實際上,上一篇聊到了IlluminateDatabaseQueryBuilder這個非常重要的類,這個類含有三個主要的武器:MySqlConnection, M...

    qpal 評論0 收藏0
  • Laravel 學習筆記 Query Builder 源碼解析(中)

    說明:本篇主要學習數據庫連接階段和編譯SQL語句部分相關源碼。實際上,上篇已經聊到Query Builder通過連接工廠類ConnectionFactory構造出了MySqlConnection實例(假設驅動driver是mysql),在該MySqlConnection中主要有三件利器:IlluminateDatabaseMysqlConnector;IlluminateDatabaseQuery...

    zhou_you 評論0 收藏0
  • Laravel源碼解析Model

    摘要:根據單一責任開發原則來講,在的開發過程中每個表都應建立一個對外服務和調用。類似于這樣解析的數據操作分兩種它們除了有各自的特色外,基本的數據操作都是通過調用方法去完成整個。內并沒有太多的代碼,大多都是處理數據庫鏈接。 showImg(https://segmentfault.com/img/bVbhjvY?w=600&h=296); 前言 提前預祝猿人們國慶快樂,吃好、喝好、玩好,我會在...

    CloudwiseAPM 評論0 收藏0
  • Laravel學習筆記Schema Builder 和 Migration System()

    摘要:看下兩個方法的源碼同樣是使用了對象來添加命令和。 說明:本文主要學習Schema Builder和Migration System的使用及相關原理。傳統上在設計database時需要寫大量的SQL語句,但Laravel提供了Schema Builder這個神器使得在設計database時使用面向對象方法來做,不需要寫一行SQL,并且還提供了另一個神器Migration System,可...

    nevermind 評論0 收藏0
  • Laravel學習筆記Container源碼解析

    摘要:實際上的綁定主要有三種方式且只是一種的,這些已經在學習筆記之實例化源碼解析聊過,其實現方法并不復雜。從以上源碼發現的反射是個很好用的技術,這里給出個,看下能干些啥打印結果太長了,就不粘貼了。 說明:本文主要學習Laravel中Container的源碼,主要學習Container的綁定和解析過程,和解析過程中的依賴解決。分享自己的研究心得,希望對別人有所幫助。實際上Container的綁...

    huayeluoliuhen 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<