当前位置: 首页 > 后端技术 > PHP

Laravel核心解读--数据库(一)基础介绍

时间:2023-03-30 00:51:36 PHP

我们在学习和使用一个开发框架的时候,无论我们使用什么框架,如何连接数据库,对数据库进行增删改查都是重点学习。在Laravel中,我们可以通过两种方式与数据库进行交互:DB,DB直接与PHP的底层PDO交互,通过查询构建器提供方便的接口来创建和运行数据库查询语句。Eloquent模型,Eloquent是基于DB的查询构建器。它抽象了数据库的ORM。丰富的功能让我们可以避免编写复杂的SQL语句,优雅的解决数据表之间的关系。.上面提到的两部分都包含在Illuminate/Database包中。Illuminate/Database除了是Laravel的数据库层,也是一个PHP数据库工具集。您可以在任何项目中通过composerinstallilluminate/databse安装和使用它。数据库服务注册和初始化数据库在Laravel应用程序中也被注册为服务容器中的服务。它的服务提供者是Illuminate\Database\DatabaseServiceProviderpublicfunctionregister(){Model::clearBootedModels();$this->registerConnectionServices();$this->registerEloquentFactory();$this->registerQueueableEntityResolver();}第1步:Model::clearBootedModels()。在Eloquent服务启动之前,为了安全,需要清理bootedModel和globalqueryscope/***清除bootedmodels列表,这样它们就会重新启动。**@returnvoid*/publicstaticfunctionclearBootedModels(){static::$booted=[];static::$globalScopes=[];}步骤2:注册ConnectionServicesprotectedfunctionregisterConnectionServices(){$this->app->singleton('db.factory',function($app){returnnewConnectionFactory($app);});$this->app->singleton('db',function($app){returnnewDatabaseManager($app,$app['db.factory']);});$this->app->bind('db.connection',function($app){return$app['db']->connection();});}db.factory用于创建数据库连接实例将被注入到DatabaseManager中。说到服务容器绑定,说到依赖注入的作用之一就是延迟对象的初始化,所以只有在使用数据库连接实例的时候才会创建。dbDatabaseManger是Database的对外接口,DB的Facade是DatabaseManager的静态代理。应用程序中所有与数据库相关的操作都是通过与该接口交互完成的。db.connection数据库连接实例,是与底层PDO接口交互的底层类,可用于数据库的查询、更新、创建等操作。因此DatabaseManager作为一个接口与外界进行交互。当应用程序需要它时,通过ConnectionFactory创建一个数据库连接实例。最后,数据库的增删改查都是由数据库连接实例来完成的。第3步:注册Eloquent工厂保护函数registerEloquentFactory(){$this->app->singleton(FakerGenerator::class,function($app){returnFakerFactory::create($app['config']->get('app.faker_locale','en_US'));});$this->app->singleton(EloquentFactory::class,function($app){returnEloquentFactory::construct($app->make(FakerGenerator::class),$this->app->databasePath('工厂'));});}启动数据库服务publicfunctionboot(){Model::setConnectionResolver($this->app['db']);Model::setEventDispatcher($this->app['events']);}数据库服务的启动主要是设置EloquentModel的连接解析器(connectionresolver),让模型可以使用db服务连接到数据库。还有一个dispatcherforsettingdatabaseevents,用来监听数据库事件。DatabaseManager表示DatabaseManager是整个数据库服务的接口。当我们通过DB门面进行操作时,实际上调用的是DatabaseManager,它会通过数据库连接对象工厂(ConnectionFacotry)获取数据库连接对象(Connection),然后数据库连接对象会被执行具体的CRUD操作。我们看一下DatabaseManager的构造函数:publicfunction__construct($app,ConnectionFactory$factory){$this->app=$app;$this->factory=$factory;}ConnectionFactory是上面介绍的绑定dbservice传递给DatabaseManager时。比如我们现在在程序中执行DB::table('users')->get(),DatabaseManager中没有table方法,那么就会触发魔术方法__call:classDatabaseManagerimplementsConnectionResolverInterface{protected$app;受保护的$工厂;受保护的$connections=[];公共函数__call($method,$parameters){return$this->connection()->$method(...$parameters);}publicfunctionconnection($name=null){list($database,$type)=$this->parseConnectionName($name);$name=$name?:$database;如果(!isset($this->connections[$name])){$this->connections[$name]=$this->configure($this->makeConnection($database),$type);}返回$this->connections[$name];}}connection方法会返回数据库连接对象,这个过程首先是解析连接名parseConnectionNameprotectedfunctionparseConnectionName($name){$name=$name?:$this->getDefaultConnection();//检查连接名是否以::read,::write结尾如'ucenter::read'返回Str::endsWith($name,['::read','::write'])?explode('::',$name,2):[$name,null];}publicfunctiongetDefaultConnection(){//laravel默认是mysql,这里假设是常用的mysql连接return$this->app['config']['database.default'];}如果没有指定连接名,Laravel会使用数据库配置中指定的默认连接名,接下来的makeConnection方法会根据连接名创建连接实例:protectedfunctionmakeConnection($name){//假设$name是'mysql',从config/database.php获取'connections。mysql'配置$config=$this->configuration($name);//首先检查应用启动时是否通过连接名注册了扩展(闭包),如果是则通过扩展获取连接实例//例如在AppServiceProvider中通过DatabaseManager::extend('mysql',function(){...})if(isset($this->extensions[$name])){returncall_user_func($this->extensions[$name],$config,$name);}//检查扩展是否为连接配置中指定的驱动程序注册,如果是,则通过扩展获取连接实例if(isset($this->extensions[$driver])){returncall_user_func($this->扩展[$driver],$config,$name);}//通过ConnectionFactory数据库连接对象获取Mysql连接类factoryreturn$this->factory->make($config,$name);}ConnectionFactory上面的makeConnection方法使用数据库连接对象project获取数据库连接对象,我们看看工厂的make方法:/***根据配置创建PDO连接**@paramarray$config*@paramstring$name*@return\Illuminate\Database\Connection*/publicfunctionmake(数组$config,$name=null){$config=$this->parseConfig($config,$name);如果(isset($config['read'])){返回$this->createReadWriteConnection($config);}return$this->createSingleConnection($config);}受保护函数parseConfig(array$config,$name){returnArr::add(Arr::add($config,'prefix',''),'name',$名称);}在建立连接之前,首先通过parseConfig将默认的prefix属性和name属性添加到配置参数中,然后根据配置文件中是否设置了读写分离。如果设置了读写分离,会调用createReadWriteConnection函数生成一个同时具备读写功能的连接;否则,将调用createSingleConnection函数生成一个公共连接对象。受保护函数createSingleConnection(array$config){$pdo=$this->createPdoResolver($config);返回$this->createConnection($config['driver'],$pdo,$config['database'],$config['prefix'],$config);}protectedfunctioncreateConnection($driver,$connection,$数据库,$prefix='',array$config=[]){......switch($driver){case'mysql':returnnewMySqlConnection($connection,$database,$prefix,$config);case'pgsql':returnnewPostgresConnection($connection,$database,$prefix,$config);...}thrownewInvalidArgumentException("Unsupporteddriver[$driver]");}创建数据库连接的方法createConnection中的参数$pdo是一个闭包:function()use($config){return$this->createConnector($config)->connect($config);};这导致连接器,数据库服务的另一部分。Connection对象依赖于连接器连接到数据库,所以在探索Connection之前让我们先了解一下Connector。illuminate/database中的ConnectorConnector专门负责与PDO交互连接数据库。我们顺着上面提到的闭包参数$pdo往下看。createConnector方法将创建一个连接器:publicfunctioncreateConnector(array$config){if(!isset($config['driver'])){thrownewInvalidArgumentException('必须指定驱动程序。');}if($this->container->bound($key="db.connector.{$config['driver']}")){return$this->container->make($key);}switch($config['driver']){case'mysql':returnnewMySqlConnector;case'pgsql':返回新的PostgresConnector;case'sqlite':返回新的SQLiteConnector;case'sqlsrv':返回新的SqlServerConnector;}thrownewInvalidArgumentException("Unsupporteddriver[{$config['driver']}]");}这里还是以Mysql为例,看一下Mysql连接器。classMySqlConnectorextendsConnectorimplementsConnectorInterface{publicfunctionconnect(array$config){//生成PDO连接数据库时使用的DSN连接字符串$dsn=$this->getDsn($config);//获取要传递给PDO选项参数的DSN连接字符串$options=$this->getOptions($config);//创建PDO连接对象$connection=$this->createConnection($dsn,$config,$options);if(!empty($config['database'])){$connection->exec("use`{$config['database']}`;");}//设置连接的字符集和排序规则$this->configureEncoding($connection,$config);//设置时区$this->configureTimezone($connection,$config);//为数据库会话设置sql模式$this->setModes($connection,$config);返回$连接;}}这个通过connector和PHP底层的PDO交互来连接数据库。Connection所有类型数据库的Connection类都继承Connection父类:,数组$config=[]){$this->pdo=$pdo;$this->database=$database;$this->tablePrefix=$tablePrefix;$this->config=$config;$this->useDefaultQueryGrammar();$this->useDefaultPostProcessor();}......publicfunctiontable($table){return$this->query()->from($table);}......publicfunctionquery(){returnnewQueryBuilder($this,$this->getQueryGrammar(),$this->getPostProcessor());}......}Connection是DatabaseManager代理的数据库连接对象,所以一开始执行的代码DB::table('users')->get()经历了我们上面提到的过程,最后它由Connection执行。table方法返回一个QueryBuilder对象,在这个对象中定义了我们经常用到的where、get、first等方法,它会根据调用的方法生成对应的SQL语句,最后通过Connection对象执行得到最终结果。稍后我们将在讨论查询构建器时查看详细信息。有很多东西要总结。我们把文中提到的Database的这些组件的角色名称总结为DBDatabaseManager的静态代理。DatabaseManagerDatabase是一个外部接口。应用程序中所有与数据库相关的操作都是通过这个接口进行的。交互完成。ConnectionFactory创建一个数据库连接对象类工厂Connection数据库连接对象,最后通过它与PHP底层PDO的交互来进行数据库操作。Connector作为Connection的一员,负责通过PDO连接数据库。我们需要先了解这些组件。在这些功能的基础上,继续查看查询构建器的代码。本文已收录在Laravel源码学习系列文章中,欢迎访问阅读。