laravel核心构架——DB Facade
laravel中文文档中数据库查询构造器中介绍了使用DB的静态接口的方式进行数据库查询,文档中的示例使用了
DB::table("...");DB::raw("...");
等,那么DB Facade除此之外还有哪些接口呢?
结论
为了节约时间,先说结果:DB Facede可使用的接口参考Illuminate\Database\MySqlConnection
类,API文档:Illuminate\Database\MySqlConnection | Laravel API
使用原理
DB类:
// vendor/laravel/framework/src/Illuminate/Support/Facades/DB.php
namespace Illuminate\Support\Facades;
/**
* @see \Illuminate\Database\DatabaseManager
* @see \Illuminate\Database\Connection
*/
class DB extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'db';
}
}
DB类非常简单,并没有table()
等静态方法,那么只能在DB的父类Facade中找答案:
// // vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php
namespace Illuminate\Support\Facades;
use Mockery;
use RuntimeException;
use Mockery\MockInterface;
abstract class Facade
{
...
}
Facade类
Facade是一个抽象类,Facade类有个一个重要的魔术方法__callStatic($method, $args)
,当我们调用DB的静态方法时,就会执行这个魔术方法。
// Illuminate\Support\Facades\Facade
/**
* Handle dynamic, static calls to the object.
*
* @param string $method
* @param array $args
* @return mixed
*
* @throws \RuntimeException
*/
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}
return $instance->$method(...$args);
}
这个方法中,通过$instance->$method(...$args);
的方式调用我们使用的静态方法(更多关于__callStatic
的信息参见:PHP: 重载 - Manual)。
而$instance
对象通过getFacadeRoot()
静态方法获得:
/**
* Get the root object behind the facade.
*
* @return mixed
*/
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
/**
* Resolve the facade root instance from the container.
*
* @param string|object $name
* @return mixed
*/
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) {
return $name;
}
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
return static::$resolvedInstance[$name] = static::$app[$name];
}
getFacade
方法调用了resolveFacadeInstance($name)
方法,而参数就是DB::getFacadeAccessor()
返回的字符串"db"
,最终在返回$app[$name]
。$app
应该就是laravel的Appliaction
对象。我在控制台中进行检验:
看到$instance
是一个Illuminate\Database\DatabaseManager
的实例。这也与laravel中文文档中对Facade的原理介绍一致(Facades |《Laravel 5.5 中文文档 5.5》| Laravel China 社区)。
DatabaseManager 类
DatabaseManager类中定义了_call
魔术方法:
// vendor/laravel/framework/src/Illuminate/Databases/DatabaseManager.php
namespace Illuminate\Database;
use PDO;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Illuminate\Database\Connectors\ConnectionFactory;
/**
* @mixin \Illuminate\Database\Connection
*/
class DatabaseManager implements ConnectionResolverInterface
{
...
/**
* Dynamically pass methods to the default connection.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
return $this->connection()->$method(...$parameters);
}
}
我们调用connection()
方法来查看调用方法的对象:
MySqlConnection类
最终就是MySqlConnection类的实例:
// vendor/laravel/framework/src/Illuminate/Databases/MySqlConnection.php
namespace Illuminate\Database;
use PDO;
use Illuminate\Database\Schema\MySqlBuilder;
use Illuminate\Database\Query\Processors\MySqlProcessor;
use Doctrine\DBAL\Driver\PDOMySql\Driver as DoctrineDriver;
use Illuminate\Database\Query\Grammars\MySqlGrammar as QueryGrammar;
use Illuminate\Database\Schema\Grammars\MySqlGrammar as SchemaGrammar;
class MySqlConnection extends Connection
{
...
}
虽然MySqlConnection
类中并没有定义我们调用的方法,但父类中Connection
中有定义:
// vendor/laravel/framework/src/Illuminate/Databases/Connection.php
namespace Illuminate\Database;
class Connection implements ConnectionInterface
{
...
/**
* 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()
);
}
/**
* Run a select statement and return a single result.
*
* @param string $query
* @param array $bindings
* @param bool $useReadPdo
* @return mixed
*/
public function selectOne($query, $bindings = [], $useReadPdo = true)
{
$records = $this->select($query, $bindings, $useReadPdo);
return array_shift($records);
}
...
}