illuminate/database 是完整的php数据库工具包,即ORM(Object-Relational Mapping)类库。
提供丰富的查询构造器,和多个驱动的服务。作为Laravel的数据库层使用,也可以单独使用。
加载composer之后,创建Illuminate\Database\Capsule\Manager对象,配置连接数据后可将其全局化,之后调用全局化的变量使用构造方法等进行查询。
- require_once './vendor/autoload.php';
-
- use Illuminate\Container\Container;
- use Illuminate\Database\Capsule\Manager as DB;
- use Illuminate\Events\Dispatcher;
-
- class BasedDB
- {
- private $config;
- public function __construct($config)
- {
- $this->config = [
- 'driver' => 'mysql',
- 'host' => $config["host"],
- 'database' => $config["database"],
- 'username' => $config["username"],
- 'password' => $config["password"],
- 'charset' => $config["charset"],
- 'collation' => $config["collation"],
- 'prefix' => $config["prefix"],
- ];
- $this->init();
- }
- public function init()
- {
- $db = new DB();
- $db->addConnection($this->config);
- $db->setEventDispatcher(new Dispatcher(new Container));
- $db->setAsGlobal(); //设置静态全局可用
- $db->bootEloquent();
- }
-
- }
-
- class TestDB extends BasedDB
- {
- public function test1()
- {
- //构造查询
- $info = DB::table('userinfo')->where('id', '=', 2)->get();
- var_dump($info);
- }
- }
- $config = [
- 'driver' => 'mysql',
- 'host' => 'localhost',
- 'database' => 'test',
- 'username' => 'root',
- 'password' => 'qwe110110',
- 'charset' => 'utf8',
- 'collation' => 'utf8_general_ci',
- 'prefix' => '',
- ];
- $t = new TestDB($config);
- $t->test1();
从代码可见从Illuminate\Database\Capsule\Manager::addConnection($config)开始。
addConnection仅设置配置。该类的构造参数中包含Illuminate\Container\Container类,通过Manager::setupContainer()函数传入Container类对象,获取绑定的配置。
Container类中Container::bound()判断是否有绑定的内容。使用Container::bind()绑定内容,会删除共享实例中对应的别名内容。绑定时若为指定绑定类的具体类或者说闭包,会自动处理,若未执行则实际处理类为指定的类,利用反射返回对应实例,否则直接返回已有实例。
Manager::setEventDispatcher() 从字面意思是设置事件调度程序。传入参数Illuminate\Events\Dispatcher类实例。通过查看代码,Dispatcher可以用Dispatcher::listen()、Dispatcher::push()、Dispatcher::dispatch()、Dispatcher::until()可以设置监听或调度监听。
Manager::setAsGlobal()将Manager全局化,可以用调用静态对象方法调用。
Manager::bootEloquent()启动Eloquent,其中调用Manager::getEventDispatcher()获取默认的程序调度,Eloquent::setEventDispatcher设置Eloquent的事件调度。Manager::getEventDispatcher()获取绑定的events内容。
到此为止初始化结束,没有出现获取pdo的内容。
调用Manager::table()开始查询,通过调用Manager自己的静态类调用connection,即static::$instance->connection()。
之后调用DatabaseManager::connection()方法。
DatabaseManager通过解析传入的配置名获取配置。若配置名为空则默认为default,可以调用DatabaseManager::setDefaultConnection()设置默认配置名。根据配置名获取配置,通过\Illuminate\Database\Connectors\ConnectionFactory工厂类匹配对应驱动,返回对应pdo对象。
比如返回Illuminate\Database\MySqlConnector对象。该类处理对应驱动特殊处理方法,比如数据库连接、设置事务隔离级别等。
table方法最后调用的是Illuminate\Database\Connection::table(),其为MySqlConnector父类。DatabaseManager::__call($method, $parameters)处理传如的数据并执行,该方法实际返回ConnectionFactory类创建的Illuminate\Database\Query\Builder对象,模式构建器调用结束的时候,将构造器对象作为参数,整合成查询字符串执行查询。
以例子中代码为例,数据库静态类为DB。
- //$columns 数组 $column 字符串 $groups 数组 $type 包括asc(正序)和desc(倒叙)
- $obj = DB::table("table_name")->select($columns)
- ->distinct()->addSelect($column)->where($where)
- ->groupBy($groups)->having($having)
- ->orderBy($column,$type);
- $info = $obj->get()
- //返回为Illuminate\Support\Collection对象
-
-
- //select 包含子集
- $query = "avg(age)";
- $info = DB::table('userinfo')->selectSub($query, 'avg')->get();
-
- $query = "avg(age)+:num as avg";
- $info = Capsule::table('userinfo')->selectRaw($query, ['num' => 1])->get();
-
- //join
- function test8()
- {
- Capsule::enableQueryLog();
- $info1 = Capsule::table('userinfo')
- ->select('id', Capsule::raw('MAX(age) as max_age'))
- ->where('age', '>', 1)
- ->whereRaw('LENGTH(name)>= ? ', [5], "or")
- ->groupBy('id');
- $info2 = Capsule::table('userinfo', "u")->select(['u.*'])->joinSub($info1, 'tab1', function ($join) {
- $join->on('u.id', '=', 'tab1.id');
- })->get();
- //var_dump($info2->all());
- $logs = Capsule::getQueryLog();
- var_dump(end($logs));
- }
- test8();
- //输出结果
- array(3) {
- 'query' =>
- string(184) "select `u`.* from `userinfo` as `u` inner join (select `id`, MAX(age) as max_age from `userinfo` where `age` > ? or LENGTH(name)>= ? group by `id`) as `tab1` on `u`.`id` = `tab1`.`id`"
- 'bindings' =>
- array(2) {
- [0] =>
- int(1)
- [1] =>
- int(5)
- }
- 'time' =>
- double(7.27)
- }
使用Builder对象就需要遵守它的规则,比如写了max()这种统计查询的内容,必须使用groupBy()查询,不然报错”In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'test.userinfo.id'; this is incompatible with sql_mode=only_full_group_by in“。
相对起来,使用复杂查询,用查询构造还是比较麻烦。
根据代码可以绑定config类,或者自定义配置;可以设置events。
- //Illuminate\Database\Capsule\Manager
-
- use Illuminate\Container\Container;
-
- class Manager
- {
- use CapsuleManagerTrait;
- protected $manager;
-
- /**
- * Create a new database capsule manager.
- *
- * @param \Illuminate\Container\Container|null $container
- * @return void
- */
- public function __construct(Container $container = null)
- {
- $this->setupContainer($container ?: new Container);
-
- // Once we have the container setup, we will setup the default configuration
- // options in the container "config" binding. This will make the database
- // manager work correctly out of the box without extreme configuration.
- $this->setupDefaultConfiguration();
-
- $this->setupManager();
- }
- /**
- * Register a connection with the manager.
- *
- * @param array $config
- * @param string $name
- * @return void
- */
- public function addConnection(array $config, $name = 'default')
- {
- $connections = $this->container['config']['database.connections'];
-
- $connections[$name] = $config;
-
- $this->container['config']['database.connections'] = $connections;
- }
- /**
- * Get the current event dispatcher instance.
- *
- * @return \Illuminate\Contracts\Events\Dispatcher|null
- */
- public function getEventDispatcher()
- {
- if ($this->container->bound('events')) {
- return $this->container['events'];
- }
- }
- /**
- * Setup the default database configuration options.
- *
- * @return void
- */
- protected function setupDefaultConfiguration()
- {
- $this->container['config']['database.fetch'] = PDO::FETCH_OBJ;
-
- $this->container['config']['database.default'] = 'default';
- }
- /**
- * Set the event dispatcher instance to be used by connections.
- *
- * @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
- * @return void
- */
- public function setEventDispatcher(Dispatcher $dispatcher)
- {
- $this->container->instance('events', $dispatcher);
- }
- /**
- * Bootstrap Eloquent so it is ready for usage.
- *
- * @return void
- */
- public function bootEloquent()
- {
- Eloquent::setConnectionResolver($this->manager);
-
- // If we have an event dispatcher instance, we will go ahead and register it
- // with the Eloquent ORM, allowing for model callbacks while creating and
- // updating "model" instances; however, it is not necessary to operate.
- if ($dispatcher = $this->getEventDispatcher()) {
- Eloquent::setEventDispatcher($dispatcher);
- }
- }
- }
-
-
- //Illuminate\Support\Traits\CapsuleManagerTrait
- trait CapsuleManagerTrait
- {
- /**
- * Make this capsule instance available globally.
- *
- * @return void
- */
- public function setAsGlobal()
- {
- static::$instance = $this;
- }
- /**
- * Setup the IoC container instance.
- *
- * @param \Illuminate\Contracts\Container\Container $container
- * @return void
- */
- protected function setupContainer(Container $container)
- {
- $this->container = $container;
-
- if (! $this->container->bound('config')) {
- $this->container->instance('config', new Fluent);
- }
- }
- }
-
- //\Illuminate\Contracts\Container\Container
- use Illuminate\Contracts\Container\Container as ContainerContract;
- class Container implements ArrayAccess, ContainerContract
- {
- /**
- * Determine if the given abstract type has been bound.
- *
- * @param string $abstract
- * @return bool
- */
- public function bound($abstract)
- {
- return isset($this->bindings[$abstract]) ||
- isset($this->instances[$abstract]) ||
- $this->isAlias($abstract);
- }
- /**
- * Register a binding with the container.
- *
- * @param string $abstract
- * @param \Closure|string|null $concrete
- * @param bool $shared
- * @return void
- *
- * @throws \TypeError
- */
- public function bind($abstract, $concrete = null, $shared = false)
- {
- $this->dropStaleInstances($abstract);
-
- // If no concrete type was given, we will simply set the concrete type to the
- // abstract type. After that, the concrete type to be registered as shared
- // without being forced to state their classes in both of the parameters.
- if (is_null($concrete)) {
- $concrete = $abstract;
- }
-
- // If the factory is not a Closure, it means it is just a class name which is
- // bound into this container to the abstract type and we will just wrap it
- // up inside its own Closure to give us more convenience when extending.
- if (! $concrete instanceof Closure) {
- if (! is_string($concrete)) {
- throw new TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null');
- }
-
- $concrete = $this->getClosure($abstract, $concrete);
- }
-
- $this->bindings[$abstract] = compact('concrete', 'shared');
-
- // If the abstract type was already resolved in this container we'll fire the
- // rebound listener so that any objects which have already gotten resolved
- // can have their copy of the object updated via the listener callbacks.
- if ($this->resolved($abstract)) {
- $this->rebound($abstract);
- }
- }
- /**
- * Register an existing instance as shared in the container.
- *
- * @param string $abstract
- * @param mixed $instance
- * @return mixed
- */
- public function instance($abstract, $instance)
- {
- $this->removeAbstractAlias($abstract);
-
- $isBound = $this->bound($abstract);
-
- unset($this->aliases[$abstract]);
-
- // We'll check to determine if this type has been bound before, and if it has
- // we will fire the rebound callbacks registered with the container and it
- // can be updated with consuming classes that have gotten resolved here.
- $this->instances[$abstract] = $instance;
-
- if ($isBound) {
- $this->rebound($abstract);
- }
-
- return $instance;
- }
- /**
- * Remove an alias from the contextual binding alias cache.
- *
- * @param string $searched
- * @return void
- */
- protected function removeAbstractAlias($searched)
- {
- if (! isset($this->aliases[$searched])) {
- return;
- }
-
- foreach ($this->abstractAliases as $abstract => $aliases) {
- foreach ($aliases as $index => $alias) {
- if ($alias == $searched) {
- unset($this->abstractAliases[$abstract][$index]);
- }
- }
- }
- }
- }
- //Illuminate\Database\Capsule\Manager
- use Illuminate\Database\DatabaseManager;
- class Manager
- {
- protected $manager;
-
- /**
- * Create a new database capsule manager.
- *
- * @param \Illuminate\Container\Container|null $container
- * @return void
- */
- public function __construct(Container $container = null)
- {
- $this->setupContainer($container ?: new Container);
-
- // Once we have the container setup, we will setup the default configuration
- // options in the container "config" binding. This will make the database
- // manager work correctly out of the box without extreme configuration.
- $this->setupDefaultConfiguration();
-
- $this->setupManager();
- }
- /**
- * Build the database manager instance.
- *
- * @return void
- */
- protected function setupManager()
- {
- $factory = new ConnectionFactory($this->container);
-
- $this->manager = new DatabaseManager($this->container, $factory);
- }
- /**
- * Dynamically pass methods to the default connection.
- *
- * @param string $method
- * @param array $parameters
- * @return mixed
- */
- public static function __callStatic($method, $parameters)
- {
- return static::connection()->$method(...$parameters);
- }
- /**
- * Get a connection instance from the global manager.
- *
- * @param string|null $connection
- * @return \Illuminate\Database\Connection
- */
- public static function connection($connection = null)
- {
- return static::$instance->getConnection($connection);
- }
- /**
- * Get a registered connection instance.
- *
- * @param string|null $name
- * @return \Illuminate\Database\Connection
- */
- public function getConnection($name = null)
- {
- return $this->manager->connection($name);
- }
- /**
- * Get a fluent query builder instance.
- *
- * @param \Closure|\Illuminate\Database\Query\Builder|string $table
- * @param string|null $as
- * @param string|null $connection
- * @return \Illuminate\Database\Query\Builder
- */
- public static function table($table, $as = null, $connection = null)
- {
- return static::$instance->connection($connection)->table($table, $as);
- }
- }
-
- //Illuminate\Database\DatabaseManager
- use Illuminate\Database\Connectors\ConnectionFactory;
- /**
- * @mixin \Illuminate\Database\Connection
- */
- class DatabaseManager implements ConnectionResolverInterface
- {
- public function __construct($app, ConnectionFactory $factory)
- {
- $this->app = $app;
- $this->factory = $factory;
-
- $this->reconnector = function ($connection) {
- $this->reconnect($connection->getNameWithReadWriteType());
- };
- }
- /**
- * Get a database connection instance.
- *
- * @param string|null $name
- * @return \Illuminate\Database\Connection
- */
- public function connection($name = null)
- {
- [$database, $type] = $this->parseConnectionName($name);
-
- $name = $name ?: $database;
-
- // If we haven't created this connection, we'll create it based on the config
- // provided in the application. Once we've created the connections we will
- // set the "fetch mode" for PDO which determines the query return types.
- if (! isset($this->connections[$name])) {
- $this->connections[$name] = $this->configure(
- $this->makeConnection($database), $type
- );
- }
-
- return $this->connections[$name];
- }
- /**
- * 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);
- }
- }
-
- //Illuminate\Database\Connectors\ConnectionFactory
- class ConnectionFactory
- {
- /**
- * Establish a PDO connection based on the configuration.
- *
- * @param array $config
- * @param string|null $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());
- }
- /**
- * Parse and prepare the database configuration.
- *
- * @param array $config
- * @param string $name
- * @return array
- */
- protected function parseConfig(array $config, $name)
- {
- return Arr::add(Arr::add($config, 'prefix', ''), 'name', $name);
- }
- /**
- * Create a read / write database connection instance.
- *
- * @param array $config
- * @return \Illuminate\Database\Connection
- */
- protected function createReadWriteConnection(array $config)
- {
- $connection = $this->createSingleConnection($this->getWriteConfig($config));
-
- return $connection->setReadPdo($this->createReadPdo($config));
- }
- /**
- * Create a read / write database connection instance.
- *
- * @param array $config
- * @return \Illuminate\Database\Connection
- */
- protected function createReadWriteConnection(array $config)
- {
- $connection = $this->createSingleConnection($this->getWriteConfig($config));
-
- return $connection->setReadPdo($this->createReadPdo($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
- );
- }
- /**
- * Get the write configuration for a read / write connection.
- *
- * @param array $config
- * @return array
- */
- protected function getWriteConfig(array $config)
- {
- return $this->mergeReadWriteConfig(
- $config, $this->getReadWriteConfig($config, 'write')
- );
- }
- /**
- * Merge a configuration for a read / write connection.
- *
- * @param array $config
- * @param array $merge
- * @return array
- */
- protected function mergeReadWriteConfig(array $config, array $merge)
- {
- return Arr::except(array_merge($config, $merge), ['read', 'write']);
- }
- /**
- * Get a read / write level configuration.
- *
- * @param array $config
- * @param string $type
- * @return array
- */
- protected function getReadWriteConfig(array $config, $type)
- {
- return isset($config[$type][0])
- ? Arr::random($config[$type])
- : $config[$type];
- }
- /**
- * 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 connector instance based on the configuration.
- *
- * @param array $config
- * @return \Illuminate\Database\Connectors\ConnectorInterface
- *
- * @throws \InvalidArgumentException
- */
- 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']}].");
- }
- /**
- * 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}].");
- }
- }
-
- //Illuminate\Database\MySqlConnection
- class MySqlConnection extends Connection
- {
- }
-
- //Illuminate\Database\Connection
- use Illuminate\Database\Query\Builder as QueryBuilder;
- class Connection implements ConnectionInterface
- {
- /**
- * Begin a fluent query against a database table.
- *
- * @param \Closure|\Illuminate\Database\Query\Builder|string $table
- * @param string|null $as
- * @return \Illuminate\Database\Query\Builder
- */
- public function table($table, $as = null)
- {
- return $this->query()->from($table, $as);
- }
- /**
- * Get a new query builder instance.
- *
- * @return \Illuminate\Database\Query\Builder
- */
- public function query()
- {
- return new QueryBuilder(
- $this, $this->getQueryGrammar(), $this->getPostProcessor()
- );
- }
- }
-
- //Illuminate\Database\Query\Builder
- class Builder
- {
- /**
- * Set the table which the query is targeting.
- *
- * @param \Closure|\Illuminate\Database\Query\Builder|string $table
- * @param string|null $as
- * @return $this
- */
- public function from($table, $as = null)
- {
- if ($this->isQueryable($table)) {
- return $this->fromSub($table, $as);
- }
-
- $this->from = $as ? "{$table} as {$as}" : $table;
-
- return $this;
- }
- /**
- * Determine if the value is a query builder instance or a Closure.
- *
- * @param mixed $value
- * @return bool
- */
- protected function isQueryable($value)
- {
- return $value instanceof self ||
- $value instanceof EloquentBuilder ||
- $value instanceof Relation ||
- $value instanceof Closure;
- }
- /**
- * Makes "from" fetch from a subquery.
- *
- * @param \Closure|\Illuminate\Database\Query\Builder|string $query
- * @param string $as
- * @return $this
- *
- * @throws \InvalidArgumentException
- */
- public function fromSub($query, $as)
- {
- [$query, $bindings] = $this->createSub($query);
-
- return $this->fromRaw('('.$query.') as '.$this->grammar->wrapTable($as), $bindings);
- }
- ……
- //之后都是构建构造器的 没调试 看的也不是太懂
- }
注:
本地使用php版本7.4,illuminate/database为v8.83.27
illuminate/database composer地址:illuminate/database - Packagist
illuminate/database girbub地址:GitHub - illuminate/database: [READ ONLY] Subtree split of the Illuminate Database component (see laravel/framework)