laravel中的數據庫也是以服務提供者進行初始化的名爲DatabaseServiceProvider,在config文件的providers數組中有寫。路徑爲vendor\laravel\framework\src\Illuminate\Database\DatabaseServiceProvider.phpphp
跟以往的serviceProvider同樣在register方法中註冊,在boot方法中引導加載。mysql
來看一下register方法。爲了保險起見它先經過Model將以前的加載數據給清除掉了。隨後開始註冊各類數據庫鏈接所用到的對象,經過singleton方法註冊一個單例的延遲加載對象到容器中。將DB門面類綁定到了DatabaseManager類中。laravel
public function register() { Model::clearBootedModels(); $this->registerConnectionServices(); $this->registerEloquentFactory(); $this->registerQueueableEntityResolver(); } protected function registerConnectionServices() { // The connection factory is used to create the actual connection instances on // the database. We will inject the factory into the manager so that it may // make the connections while they are actually needed and not of before. $this->app->singleton('db.factory', function ($app) { return new ConnectionFactory($app); }); // The database manager is used to resolve various connections, since multiple // connections might be managed. It also implements the connection resolver // interface which may be used by other components requiring connections. $this->app->singleton('db', function ($app) { return new DatabaseManager($app, $app['db.factory']); }); $this->app->bind('db.connection', function ($app) { return $app['db']->connection(); }); } /** * Register the Eloquent factory instance in the container. * * @return void */ 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') ); }); } /** * Register the queueable entity resolver implementation. * * @return void */ protected function registerQueueableEntityResolver() { $this->app->singleton(EntityResolver::class, function () { return new QueueEntityResolver; }); }
數據庫鏈接對象ConnectionFactory。這個鏈接工廠類之中的各類方法都是在建立配置,以及經過配置數組,返回對應的數據庫鏈接實例。這個類中的方法大可能是對數據庫的鏈接作一些配置,而後根據這些配置來返回相應的數據庫鏈接實例。sql
/** * Create a new connection instance. * * @param string $driver * @param \PDO|\Closure $connection * @param string $database * @param string $prefix * @param array $config * @return \Illuminate\Database\Connection * * @throws \InvalidArgumentException */ protected function createConnection($driver, $connection, $database, $prefix = '', array $config = []) { if ($resolver = Connection::getResolver($driver)) { return $resolver($connection, $database, $prefix, $config); } 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]"); }
數據庫管理對象DatabaseManager。這個數據庫管理類之中的各類方法也是經過各類數據庫配置來調用ConnectionFactory工廠來返回數據庫鏈接實例,它會經過配置read,write來返回相應的讀寫pdo實例。以及包含了數據庫實例的建立與斷開銷燬等。set、get各類配置。如setPdoForType方法來設置數據庫鏈接的讀寫分離(設置只讀、只寫)。那麼這個類的上游方法在哪裏呢。它是從哪裏被調用的呢?咱們開頭提了一句,DB門面類所綁定的類,就是這個類,可是若是咱們去這個類中尋找經常使用的talbe()、query()等方法,確定是一無所得的,不過咱們會發現__call()方法,這個魔術方法會在調用不存在的方法時執行,看一下它的內容。它只有一句代碼,從$this->connection()這個對象中,執行相應的方法並返回結果。是的,laravel的源碼封裝度過高了,這裏咱們暫時只須要知道$this->connection()表明了數據庫鏈接實例就好。數據庫
public function __call($method, $parameters) { return $this->connection()->$method(...$parameters); }
剛剛說到數據庫鏈接實例,如今咱們就來探尋這個實例是如何被建立出來的。以下makeConnection方法所示,經過剛剛說到的ConnectionFactory來返回數據庫鏈接實例。數組
/** * Prepare the read / write mode for database connection instance. * * @param \Illuminate\Database\Connection $connection * @param string $type * @return \Illuminate\Database\Connection */ protected function setPdoForType(Connection $connection, $type = null) { if ($type == 'read') { $connection->setPdo($connection->getReadPdo()); } elseif ($type == 'write') { $connection->setReadPdo($connection->getPdo()); } return $connection; } /** * Make the database connection instance. * * @param string $name * @return \Illuminate\Database\Connection */ protected function makeConnection($name) { $config = $this->configuration($name); // First we will check by the connection name to see if an extension has been // registered specifically for that connection. If it has we will call the // Closure and pass it the config allowing it to resolve the connection. if (isset($this->extensions[$name])) { return call_user_func($this->extensions[$name], $config, $name); } // Next we will check to see if an extension has been registered for a driver // and will call the Closure if so, which allows us to have a more generic // resolver for the drivers themselves which applies to all connections. if (isset($this->extensions[$driver = $config['driver']])) { return call_user_func($this->extensions[$driver], $config, $name); } return $this->factory->make($config, $name); }
好的,看到DatabaseManager如何建立出數據庫鏈接實例,又要把視線跳到以前說的ConnectionFactory類中了。$this->factory->make($config, $name);最後返回了make方法,咱們就從這個方法入手,請看下列代碼。app
/** * Establish a PDO connection based on the configuration. * * @param array $config * @param string $name * @return \Illuminate\Database\Connection */ public function make(array $config, $name = null) { //合併配置數組 $config = $this->parseConfig($config, $name); if (isset($config['read'])) { return $this->createReadWriteConnection($config); } return $this->createSingleConnection($config); } /** * Create a single database connection instance. * * @param array $config * @return \Illuminate\Database\Connection */ protected function createSingleConnection(array $config) { $pdo = $this->createPdoResolver($config); return $this->createConnection( $config['driver'], $pdo, $config['database'], $config['prefix'], $config ); } /** * Create a new connection instance. * * @param string $driver * @param \PDO|\Closure $connection * @param string $database * @param string $prefix * @param array $config * @return \Illuminate\Database\Connection * * @throws \InvalidArgumentException */ protected function createConnection($driver, $connection, $database, $prefix = '', array $config = []) { if ($resolver = Connection::getResolver($driver)) { return $resolver($connection, $database, $prefix, $config); } 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]"); }
能夠看到,通過一系列的方法跳轉,咱們終於經過數據庫配置,獲得了mysql等數據庫的鏈接對象。ide
數據庫工廠類ConnectionFactory返回的實例鏈接,咱們拿mysql舉例。返回的即是MySqlConnection這個類\vendor\laravel\framework\src\Illuminate\Database\MySqlConnection.php函數
進入這個類文件,能夠看見都是獲取grammar相關的方法,這些方法暫時先不去看它。祕密在於它的父類Connection類。(\vendor\laravel\framework\src\Illuminate\Database\Connection.php)先看它的構造方法,從這個方法咱們能夠知道,全部模型對象也好,DB對象也好,底層都是經過pdo去鏈接執行的,另外呢,tablePrefix數據表前綴,以及數據庫鏈接配置也是在這裏進行加載的。useDefaultQueryGrammar這倆個方法就不去深究了。咱們來看點有趣的。post
/** * Create a new database connection instance. * * @param \PDO|\Closure $pdo * @param string $database * @param string $tablePrefix * @param array $config * @return void */ public function __construct($pdo, $database = '', $tablePrefix = '', array $config = []) { $this->pdo = $pdo; // First we will setup the default properties. We keep track of the DB // name we are connected to since it is needed when some reflective // type commands are run such as checking whether a table exists. $this->database = $database; $this->tablePrefix = $tablePrefix; $this->config = $config; // We need to initialize a query grammar and the query post processors // which are both very important parts of the database abstractions // so we initialize these to their default values while starting. $this->useDefaultQueryGrammar(); $this->useDefaultPostProcessor(); }
有趣的是什麼呢?就是這個table()方法了。咱們都知道,在laravel中既可使用模型的方式去進行數據庫操做,也可使用DB::table()的方式,而咱們平時使用較多的table()方法的真身,就在這個類裏了。這個table方法只作了一件事,就是從$this->query()這個對象中from一個表,而後返回的對象就能夠經過鏈式調用的方式去作其餘操做了。接下來看這個$this->query(),這個方法也只作了一件事,new一個QueryBuilder對象。
/** * Begin a fluent query against a database table. * * @param string $table * @return \Illuminate\Database\Query\Builder */ public function table($table) { return $this->query()->from($table); } /** * Get a new query builder instance. * * @return \Illuminate\Database\Query\Builder */ public function query() { return new QueryBuilder( $this, $this->getQueryGrammar(), $this->getPostProcessor() ); }
而QueryBuilder這個對象就是咱們平時使用的eloquent orm 的入口,咱們平時使用的那些方便的數據庫操做方法都是從這裏進入。
經過一系列跳轉,咱們會發現,這個QueryBuilder的真身在
\vendor\laravel\framework\src\Illuminate\Database\Query\Builder.php這個類中,名爲Builder類。
來到這個類文件中,稍微瀏覽一下,感受發現了寶藏,裏面那些where()、join()、get()、find()方法,不正是咱們經常使用的各類方便的orm方法麼?
來來回回繞了這麼大一個圈終於找到,咱們經常使用的函數是從何而來,可是如今還有一個疑問了。如今咱們一路跟蹤到的線索,都是從DB::table()這種方式跟蹤而來,那麼model的方式是怎麼調用的呢?
讓咱們隨便新建一個模型類,而後找到它的父類Model
vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php
瀏覽一番事後,咱們發現了比較眼熟的fill()、all()、save()等方法,而後咱們會發現這些方法中,大部分都有$this->newModelQuery();這麼一句,咱們根據這一線索一路跟蹤,一路尾行,最終咱們會發現new QueryBuilder這句代碼又出現了。
/** * Get a new query builder for the model's table. * * @return \Illuminate\Database\Eloquent\Builder */ public function newQuery() { return $this->registerGlobalScopes($this->newQueryWithoutScopes()); } /** * Register the global scopes for this builder instance. * * @param \Illuminate\Database\Eloquent\Builder $builder * @return \Illuminate\Database\Eloquent\Builder */ public function registerGlobalScopes($builder) { foreach ($this->getGlobalScopes() as $identifier => $scope) { $builder->withGlobalScope($identifier, $scope); } return $builder; } /** * Get a new query builder instance for the connection. * * @return \Illuminate\Database\Query\Builder */ protected function newBaseQueryBuilder() { $connection = $this->getConnection(); return new QueryBuilder( $connection, $connection->getQueryGrammar(), $connection->getPostProcessor() ); }
是的,再次跳轉後,咱們便又回到了\vendor\laravel\framework\src\Illuminate\Database\Query\Builder.php這個類中。
至於那些model裏沒寫的方法爲何能夠直接調用?你去model類裏找一找看有沒有__call這個魔術方法,看它裏面寫了些什麼
/** * Handle dynamic method calls into the model. * * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { if (in_array($method, ['increment', 'decrement'])) { return $this->$method(...$parameters); } return $this->newQuery()->$method(...$parameters); }
到這裏,數據庫服務是怎麼啓動的,DB門面、model類爲何能直接執行orm方法,相信咱們已經有清晰的認識了。至於orm是怎麼轉化成sql語句執行的,且聽下回分解~