成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Laravel核心解讀--Database(一)基礎(chǔ)介紹

reclay / 2294人閱讀

摘要:第三步注冊工廠啟動數(shù)據(jù)庫服務(wù)數(shù)據(jù)庫服務(wù)的啟動主要設(shè)置的連接分析器,讓能夠用服務(wù)連接數(shù)據(jù)庫。

在我們學習和使用一個開發(fā)框架時,無論使用什么框架,如何連接數(shù)據(jù)庫、對數(shù)據(jù)庫進行增刪改查都是學習的重點,在Laravel中我們可以通過兩種方式與數(shù)據(jù)庫進行交互:

DB, DB是與PHP底層的PDO直接進行交互的,通過查詢構(gòu)建器提供了一個方便的接口來創(chuàng)建及運行數(shù)據(jù)庫查詢語句。

Eloquent Model, Eloquent是建立在DB的查詢構(gòu)建器基礎(chǔ)之上,對數(shù)據(jù)庫進行了抽象的ORM,功能十分豐富讓我們可以避免寫復雜的SQL語句,并用優(yōu)雅的方式解決了數(shù)據(jù)表之間的關(guān)聯(lián)關(guān)系。

上面說的這兩個部分都包括在了Illuminate/Database包里面,除了作為Laravel的數(shù)據(jù)庫層Illuminate/Database還是一個PHP數(shù)據(jù)庫工具集, 在任何項目里你都可以通過composer install illuminate/databse安裝并使用它。

Database服務(wù)注冊和初始化

Database也是作為一種服務(wù)注冊到服務(wù)容器里提供給Laravel應用使用的,它的服務(wù)提供器是IlluminateDatabaseDatabaseServiceProvider

public function register()
{
    Model::clearBootedModels();

    $this->registerConnectionServices();

    $this->registerEloquentFactory();

    $this->registerQueueableEntityResolver();
}

第一步:Model::clearBootedModels()。在 Eloquent 服務(wù)啟動之前為了保險起見需要清理掉已經(jīng)booted的Model和全局查詢作用域

/**
 * Clear the list of booted models so they will be re-booted.
 *
 * @return void
 */
public static function clearBootedModels()
{
    static::$booted = [];

    static::$globalScopes = [];
}

第二步:注冊ConnectionServices

protected function registerConnectionServices()
{
    $this->app->singleton("db.factory", function ($app) {
        return new ConnectionFactory($app);
    });

    $this->app->singleton("db", function ($app) {
        return new DatabaseManager($app, $app["db.factory"]);
    });

    $this->app->bind("db.connection", function ($app) {
        return $app["db"]->connection();
    });
}

db.factory用來創(chuàng)建數(shù)據(jù)庫連接實例,它將被注入到DatabaseManager中,在講服務(wù)容器綁定時就說過了依賴注入的其中一個作用是延遲初始化對象,所以只要在用到數(shù)據(jù)庫連接實例時它們才會被創(chuàng)建。

db DatabaseManger 作為Database面向外部的接口,DB這個Facade就是DatabaseManager的靜態(tài)代理。應用中所有與Database有關(guān)的操作都是通過與這個接口交互來完成的。

db.connection 數(shù)據(jù)庫連接實例,是與底層PDO接口進行交互的底層類,可用于數(shù)據(jù)庫的查詢、更新、創(chuàng)建等操作。

所以DatabaseManager作為接口與外部交互,在應用需要時通過ConnectionFactory創(chuàng)建了數(shù)據(jù)庫連接實例,最后執(zhí)行數(shù)據(jù)庫的增刪改查是由數(shù)據(jù)庫連接實例來完成的。

第三步:注冊Eloquent工廠

protected function registerEloquentFactory()
{
    $this->app->singleton(FakerGenerator::class, function ($app) {
        return FakerFactory::create($app["config"]->get("app.faker_locale", "en_US"));
    });

    $this->app->singleton(EloquentFactory::class, function ($app) {
        return EloquentFactory::construct(
            $app->make(FakerGenerator::class), $this->app->databasePath("factories")
        );
    });
}

啟動數(shù)據(jù)庫服務(wù)

public function boot()
{
    Model::setConnectionResolver($this->app["db"]);

    Model::setEventDispatcher($this->app["events"]);
}

數(shù)據(jù)庫服務(wù)的啟動主要設(shè)置 Eloquent Model 的連接分析器(connection resolver),讓model能夠用db服務(wù)連接數(shù)據(jù)庫。還有就是設(shè)置數(shù)據(jù)庫事件的分發(fā)器 dispatcher,用于監(jiān)聽數(shù)據(jù)庫的事件。

DatabaseManager

上面說了DatabaseManager是整個數(shù)據(jù)庫服務(wù)的接口,我們通過DB門面進行操作的時候?qū)嶋H上調(diào)用的就是DatabaseManager,它會通過數(shù)據(jù)庫連接對象工廠(ConnectionFacotry)獲得數(shù)據(jù)庫連接對象(Connection),然后數(shù)據(jù)庫連接對象會進行具體的CRUD操作。我們先看一下DatabaseManager的構(gòu)造函數(shù):

public function __construct($app, ConnectionFactory $factory)
{
    $this->app = $app;
    $this->factory = $factory;
}

ConnectionFactory是在上面介紹的綁定db服務(wù)的時候傳遞給DatabaseManager的。比如我們現(xiàn)在程序里執(zhí)行了DB::table("users")->get(), 在DatabaseManager里并沒有table方法然后就會觸發(fā)魔術(shù)方法__call

class DatabaseManager implements ConnectionResolverInterface
{
    protected $app;
    protected $factory;
    protected $connections = [];

    public function __call($method, $parameters)
    {
        return $this->connection()->$method(...$parameters);
    }
    
    public function connection($name = null)
    {
        list($database, $type) = $this->parseConnectionName($name);

        $name = $name ?: $database;

        if (! isset($this->connections[$name])) {
            $this->connections[$name] = $this->configure(
                $this->makeConnection($database), $type
            );
        }

        return $this->connections[$name];
    }

}

connection方法會返回數(shù)據(jù)庫連接對象,這個過程首先是解析連接名稱parseConnectionName

protected function parseConnectionName($name)
{
    $name = $name ?: $this->getDefaultConnection();
    // 檢查connection name 是否以::read, ::write結(jié)尾  比如"ucenter::read"
    return Str::endsWith($name, ["::read", "::write"])
                        ? explode("::", $name, 2) : [$name, null];
}

public function getDefaultConnection()
{
    // laravel默認是mysql,這里假定是常用的mysql連接
    return $this->app["config"]["database.default"];
}

如果沒有指定連接名稱,Laravel會使用database配置里指定的默認連接名稱, 接下來makeConnection方法會根據(jù)連接名稱來創(chuàng)建連接實例:

protected function makeConnection($name)
{
    //假定$name是"mysql", 從config/database.php中獲取"connections.mysql"的配置
     $config = $this->configuration($name);

    //首先去檢查在應用啟動時是否通過連接名注冊了extension(閉包), 如果有則通過extension獲得連接實例
    //比如在AppServiceProvider里通過DatabaseManager::extend("mysql", function () {...})
    if (isset($this->extensions[$name])) {
        return call_user_func($this->extensions[$name], $config, $name);
    }
    
    //檢查是否為連接配置指定的driver注冊了extension, 如果有則通過extension獲得連接實例
    if (isset($this->extensions[$driver])) {
        return call_user_func($this->extensions[$driver], $config, $name);
    }
    
    // 通過ConnectionFactory數(shù)據(jù)庫連接對象工廠獲取Mysql的連接類    
    return $this->factory->make($config, $name);
}    
ConnectionFactory

上面makeConnection方法使用了數(shù)據(jù)庫連接對象工程來獲取數(shù)據(jù)庫連接對象,我們來看一下工廠的make方法:

/**
 * 根據(jù)配置創(chuàng)建一個PDO連接
 *
 * @param  array   $config
 * @param  string  $name
 * @return IlluminateDatabaseConnection
 */
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 parseConfig(array $config, $name)
{
    return Arr::add(Arr::add($config, "prefix", ""), "name", $name);
}

在建立連接之前, 先通過parseConfig向配置參數(shù)中添加默認的 prefix 屬性與 name 屬性。

接下來根據(jù)配置文件中是否設(shè)置了讀寫分離。如果設(shè)置了讀寫分離,那么就會調(diào)用 createReadWriteConnection 函數(shù),生成具有讀、寫兩個功能的 connection;否則的話,就會調(diào)用 createSingleConnection 函數(shù),生成普通的連接對象。

protected function createSingleConnection(array $config)
{
    $pdo = $this->createPdoResolver($config);

    return $this->createConnection(
        $config["driver"], $pdo, $config["database"], $config["prefix"], $config
    );
}

protected function createConnection($driver, $connection, $database, $prefix = "", array $config = [])
{
    ......
    switch ($driver) {
        case "mysql":
            return new MySqlConnection($connection, $database, $prefix, $config);
        case "pgsql":
            return new PostgresConnection($connection, $database, $prefix, $config);
        ......                
    }

    throw new InvalidArgumentException("Unsupported driver [$driver]");
}


創(chuàng)建數(shù)據(jù)庫連接的方法createConnection里參數(shù)$pdo是一個閉包:

function () use ($config) {
    return $this->createConnector($config)->connect($config);
};

這就引出了Database服務(wù)中另一部份連接器Connector, Connection對象是依賴連接器連接上數(shù)據(jù)庫的,所以在探究Connection之前我們先來看看連接器Connector。

Connector

illuminate/database中連接器Connector是專門負責與PDO交互連接數(shù)據(jù)庫的,我們接著上面講到的閉包參數(shù)$pdo往下看

createConnector方法會創(chuàng)建連接器:

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"]}]");
}

這里我們還是以mysql舉例看一下Mysql的連接器。

class MySqlConnector extends Connector implements ConnectorInterface 
{
    public function connect(array $config)
    {
        //生成PDO連接數(shù)據(jù)庫時用的DSN連接字符串
        $dsn = $this->getDsn($config);
        //獲取要傳給PDO的選項參數(shù)
        $options = $this->getOptions($config);
        //創(chuàng)建一個PDO連接對象
        $connection = $this->createConnection($dsn, $config, $options);

        if (! empty($config["database"])) {
         $connection->exec("use `{$config["database"]}`;");
        }

        //為連接設(shè)置字符集和collation
        $this->configureEncoding($connection, $config);
        //設(shè)置time zone
        $this->configureTimezone($connection, $config);
        //為數(shù)據(jù)庫會話設(shè)置sql mode
        $this->setModes($connection, $config);

      return $connection;
    }
}

這樣就通過連接器與PHP底層的PDO交互連接上數(shù)據(jù)庫了。

Connection

所有類型數(shù)據(jù)庫的Connection類都是繼承了Connection父類:

class MySqlConnection extends Connection
{
     ......
}

class Connection implements ConnectionInterface
{
    public function __construct($pdo, $database = "", $tablePrefix = "", array $config = [])
    {
        $this->pdo = $pdo;

        $this->database = $database;

        $this->tablePrefix = $tablePrefix;

        $this->config = $config;

        $this->useDefaultQueryGrammar();

        $this->useDefaultPostProcessor();
    }
    ......   
    public function table($table)
    {
        return $this->query()->from($table);
    }
    ......
    public function query()
    {
        return new QueryBuilder(
            $this, $this->getQueryGrammar(), $this->getPostProcessor()
        );
    }
    ......
}

Connection就是DatabaseManager代理的數(shù)據(jù)庫連接對象了, 所以最開始執(zhí)行的代碼DB::table("users")->get()經(jīng)過我們上面講的歷程,最終是由Connection來完成執(zhí)行的,table方法返回了一個QueryBuilder對象,這個對象里定義里那些我們經(jīng)常用到的where, get, first等方法, 它會根據(jù)調(diào)用的方法生成對應的SQL語句,最后通過Connection對象執(zhí)行來獲得最終的結(jié)果。 詳細內(nèi)容我們等到以后講查詢構(gòu)建器的時候再看。

總結(jié)

說的東西有點多,我們來總結(jié)下文章里講到的Database的這幾個組件的角色

名稱 作用
DB DatabaseManager的靜態(tài)代理
DatabaseManager Database面向外部的接口,應用中所有與Database有關(guān)的操作都是通過與這個接口交互來完成的。
ConnectionFactory 創(chuàng)建數(shù)據(jù)庫連接對象的類工廠
Connection 數(shù)據(jù)庫連接對象,執(zhí)行數(shù)據(jù)庫操作最后都是通過它與PHP底層的PDO交互來完成的
Connector 作為Connection的成員專門負責通過PDO連接數(shù)據(jù)庫

我們需要先理解了這幾個組件的作用,在這些基礎(chǔ)之上再去順著看查詢構(gòu)建器的代碼。

本文已經(jīng)收錄在系列文章Laravel源碼學習里,歡迎訪問閱讀。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/28542.html

相關(guān)文章

  • Laravel核心解讀--完結(jié)篇

    摘要:過去一年時間寫了多篇文章來探討了我認為的框架最核心部分的設(shè)計思路代碼實現(xiàn)。為了大家閱讀方便,我把這些源碼學習的文章匯總到這里。數(shù)據(jù)庫算法和數(shù)據(jù)結(jié)構(gòu)這些都是編程的內(nèi)功,只有內(nèi)功深厚了才能解決遇到的復雜問題。 過去一年時間寫了20多篇文章來探討了我認為的Larave框架最核心部分的設(shè)計思路、代碼實現(xiàn)。通過更新文章自己在軟件設(shè)計、文字表達方面都有所提高,在剛開始決定寫Laravel源碼分析地...

    laoLiueizo 評論0 收藏0
  • Laravel核心解讀 -- 用戶認證系統(tǒng)(基礎(chǔ)介紹)

    摘要:系統(tǒng)的核心是由的認證組件的看守器和提供器組成。使用的認證系統(tǒng),幾乎所有東西都已經(jīng)為你配置好了。其配置文件位于,其中包含了用于調(diào)整認證服務(wù)行為的注釋清晰的選項配置。 用戶認證系統(tǒng)(基礎(chǔ)介紹) 使用過Laravel的開發(fā)者都知道,Laravel自帶了一個認證系統(tǒng)來提供基本的用戶注冊、登錄、認證、找回密碼,如果Auth系統(tǒng)里提供的基礎(chǔ)功能不滿足需求還可以很方便的在這些基礎(chǔ)功能上進行擴展。這篇...

    RebeccaZhong 評論0 收藏0
  • Laravel核心解讀--用戶認證系統(tǒng)的實現(xiàn)細節(jié)

    摘要:通過裝載看守器和用戶提供器裝載看守器和用戶提供器用到的方法比較多,用文字描述不太清楚,我們通過注解這個過程中用到的方法來看具體的實現(xiàn)細節(jié)。 用戶認證系統(tǒng)的實現(xiàn)細節(jié) 上一節(jié)我們介紹來Laravel Auth系統(tǒng)的基礎(chǔ)知識,說了他的核心組件都有哪些構(gòu)成,這一節(jié)我們會專注Laravel Auth系統(tǒng)的實現(xiàn)細節(jié),主要關(guān)注Auth也就是AuthManager是如何裝載認證用的看守器(Guard)...

    NicolasHe 評論0 收藏0
  • Laravel核心解讀--Database(四) 模型關(guān)聯(lián)

    摘要:為關(guān)聯(lián)關(guān)系設(shè)置約束子模型的等于父模型的上面設(shè)置的字段的值子類實現(xiàn)這個抽象方法通過上面代碼看到創(chuàng)建實例時主要是做了一些配置相關(guān)的操作,設(shè)置了子模型父模型兩個模型的關(guān)聯(lián)字段和關(guān)聯(lián)的約束。不過當查詢父模型時,可以預加載關(guān)聯(lián)數(shù)據(jù)。 Database 模型關(guān)聯(lián) 上篇文章我們主要講了Eloquent Model關(guān)于基礎(chǔ)的CRUD方法的實現(xiàn),Eloquent Model中除了基礎(chǔ)的CRUD外還有一個...

    gekylin 評論0 收藏0
  • Laravel核心解讀 -- 外觀模式

    摘要:外觀模式的目的在于降低系統(tǒng)的復雜程度。在不引入抽象外觀類的情況下,增加新的子系統(tǒng)可能需要修改外觀類或客戶端的源代碼,違背了開閉原則。 外觀模式 外觀模式(Facade Pattern):外部與一個子系統(tǒng)的通信必須通過一個統(tǒng)一的外觀對象進行,為子系統(tǒng)中的一組接口提供一個一致的界面,外觀模式定義了一個高層接口,這個接口使得這一子系統(tǒng)更加容易使用。外觀模式又稱為門面模式,它是一種對象結(jié)構(gòu)型模...

    zoomdong 評論0 收藏0

發(fā)表評論

0條評論

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