Laravel作为PHP的一款开发框架,在开发者中有大量的粉丝。
Laravel提供了强大的功能及灵活的组件供开发者使用,今天来看下数据库操作在laravel中是如何实现的。
配置
数据库相关的配置文件在config/database.php中,connections数组可以配置自己需要的数据源,这里可以灵活选择
<?php
// 默认数据库配置
DB::table('user')
->where(['id' => 1])
->get();
// 选择其他数据库配置
DB::connection('mysql1')
->table('people')
->where(['id' => 2])
->get();
查询构造器
重要的类:laravel/framework/src/Illuminate/Database/Query/Builder.php
laravel的数据库操作很多是通过它来完成的,这里面封装了对数据库查询的各种操作。
当我们调用各种方法之后,总要通过get() 或者 first() 或者 find()等方法获取结果,这里以get为例,
/**
* Execute the query as a "select" statement.
*
* @param array|string $columns
* @return \Illuminate\Support\Collection
*/
public function get($columns = ['*'])
{
return collect($this->onceWithColumns(Arr::wrap($columns), function () {
return $this->processor->processSelect($this, $this->runSelect());
}));
}
/**
* Run the query as a "select" statement against the connection.
*
* @return array
*/
protected function runSelect()
{
return $this->connection->select(
$this->toSql(), $this->getBindings(), ! $this->useWritePdo
);
}
通过调用connection->select后获取执行结果
重要类:Illuminate/Database/Connection.php
/**
* Run a select statement against the database.
*
* @param string $query
* @param array $bindings
* @param bool $useReadPdo
* @return array
*/
public function select($query, $bindings = [], $useReadPdo = true)
{
return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
if ($this->pretending()) {
return [];
}
// For select statements, we'll simply execute the query and return an array
// of the database result set. Each element in the array will be a single
// row from the database table, and will either be an array or objects.
$statement = $this->prepared(
$this->getPdoForSelect($useReadPdo)->prepare($query)
);
$this->bindValues($statement, $this->prepareBindings($bindings));
$statement->execute();
return $statement->fetchAll();
});
}
猛一看,其实还是pdo的操作。
Eloquent Orm
如果你对查询构造器不满意,还可以通过Orm的方式进行数据操作,Laravel中我们可以通过Model的形式进行。
重要类:
Model基类:src/Illuminate/Database/Eloquent/Model.php
src/Illuminate/Database/Eloquent/Builder.php
Eloquent/Builder中的$query实际上还是查询构造器:Database/Query/Builder.php
Model中通过__call及__callStatic的代理去执行各种方法:
/**
* Dynamically handle calls into the query instance.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
if ($method === 'macro') {
$this->localMacros[$parameters[0]] = $parameters[1];
return;
}
if ($this->hasMacro($method)) {
array_unshift($parameters, $this);
return $this->localMacros[$method](...$parameters);
}
if (static::hasGlobalMacro($method)) {
$callable = static::$macros[$method];
if ($callable instanceof Closure) {
$callable = $callable->bindTo($this, static::class);
}
return $callable(...$parameters);
}
if ($this->hasNamedScope($method)) {
return $this->callNamedScope($method, $parameters);
}
if (in_array($method, $this->passthru)) {
return $this->toBase()->{$method}(...$parameters);
}
$this->forwardCallTo($this->query, $method, $parameters);
return $this;
}
<?php
/**
* Dynamically handle calls into the query instance.
*
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \BadMethodCallException
*/
public static function __callStatic($method, $parameters)
{
if ($method === 'macro') {
static::$macros[$parameters[0]] = $parameters[1];
return;
}
if ($method === 'mixin') {
return static::registerMixin($parameters[0], $parameters[1] ?? true);
}
if (! static::hasGlobalMacro($method)) {
static::throwBadMethodCallException($method);
}
$callable = static::$macros[$method];
if ($callable instanceof Closure) {
$callable = $callable->bindTo(null, static::class);
}
return $callable(...$parameters);
}
/**
* Dynamically handle calls into the query instance.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
if ($method === 'macro') {
$this->localMacros[$parameters[0]] = $parameters[1];
return;
}
if ($this->hasMacro($method)) {
array_unshift($parameters, $this);
return $this->localMacros[$method](...$parameters);
}
if (static::hasGlobalMacro($method)) {
$callable = static::$macros[$method];
if ($callable instanceof Closure) {
$callable = $callable->bindTo($this, static::class);
}
return $callable(...$parameters);
}
if ($this->hasNamedScope($method)) {
return $this->callNamedScope($method, $parameters);
}
if (in_array($method, $this->passthru)) {
return $this->toBase()->{$method}(...$parameters);
}
$this->forwardCallTo($this->query, $method, $parameters);
return $this;
}
所以EloquentOrm的多数方法还是通过$this->query去执行也就是,QueryBuilder
DB Facade
我们一般通过DB::table(‘xxx’)就可以去执行数据库操作,那这是如何实现的呢, 这要通过DBFacade及DatabaseServiceProvider去看
重要类:
src/Illuminate/Database/DatabaseServiceProvider.php
src/Illuminate/Support/Facades/DB.php
<?php
class DB extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'db';
}
}
class DatabaseServiceProvider extends ServiceProvider
{
/**
* Register the primary database bindings.
*
* @return void
*/
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();
});
$this->app->singleton('db.transactions', function ($app) {
return new DatabaseTransactionsManager;
});
}
}
框架启动的时候会通过DatabaserServiceProvider->register自动完成db的绑定,db是啥,其实就是DatabaseManager对象, DatabaseManager还是通过__call代理去调用Connection的方法
/**
* @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);
}
}
总结一下:
使用手册: