入门指南
简介
几乎每个现代 web 应用都会与数据库进行交互。Laravel 使用原始 SQL、流畅的查询构建器和 Eloquent ORM 在各种支持的数据库上极其简单地进行数据库交互。目前,Laravel 对五种数据库提供官方支持:
- MariaDB 10.10 + (版本政策)
- MySQL 5.7+ (版本政策)
- PostgreSQL 11.0+ (版本政策)
- SQLite 3.8.8+
- SQL Server 2017+ (版本政策)
配置
Laravel 的数据库服务的配置位于应用程序的 config/database.php
配置文件中。在这个文件中,您可以定义所有数据库连接,并指定默认使用哪个连接。这个文件中的大部分配置选项都由您应用程序的环境变量的值驱动。此文件中提供了对 Laravel 支持的大多数数据库系统的示例。
默认情况下,Laravel 的示例环境配置已经准备好与 Laravel Sail 一起使用,它是一个用于在本地机器上开发 Laravel 应用程序的 Docker 配置。但是,您可以根据需要自由修改本地数据库的配置。
SQLite 配置
SQLite 数据库包含在文件系统中的单个文件中。您可以使用终端中的 touch
命令创建一个新的 SQLite 数据库:touch database/database.sqlite
。创建数据库后,可以通过在 DB_DATABASE
环境变量中放置数据库的绝对路径来轻松配置环境变量以指向此数据库:
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite
为了在 SQLite 连接中启用外键约束,您应该将 DB_FOREIGN_KEYS
环境变量设置为 true
:
DB_FOREIGN_KEYS=true
Microsoft SQL Server 配置
要使用 Microsoft SQL Server 数据库,您应该确保已安装 sqlsrv
和 pdo_sqlsrv
PHP扩展以及可能需要的任何依赖项,如 Microsoft SQL ODBC 驱动程序。
使用 URL 配置
通常,数据库连接是使用多个配置值(例如主机、数据库、用户名、密码等)进行配置的。每个配置值都有其对应的环境变量。这意味着在生产服务器上配置数据库连接信息时,您需要管理多个环境变量。
某些托管数据库提供商(如AWS和Heroku)提供一个包含数据库的所有连接信息的单个数据库“URL”,该URL以单个字符串的形式提供。示例数据库URL可能如下所示:
mysql://root:password@127.0.0.1/forge?charset=UTF-8
这些URL通常遵循标准模式约定:
driver://username:password@host:port/database?options
为了方便起见,Laravel 支持使用这些URL作为使用多个配置选项配置数据库的替代方法。如果 url
(或对应的 DATABASE_URL
环境变量)配置选项存在,则将使用它来提取数据库连接和凭据信息。
读取和写入连接
有时您可能希望对 SELECT
语句使用一个数据库连接,对 INSERT
、UPDATE
和 DELETE
语句使用另一个数据库连接。Laravel 使这成为轻而易举的,并且无论您是使用原始查询、查询构建器还是 Eloquent ORM,都将始终使用正确的连接。
为了了解如何配置读/写连接,请看下面的例子:
'mysql' => [
'read' => [
'host' => [
'192.168.1.1',
'196.168.1.2',
],
],
'write' => [
'host' => [
'196.168.1.3',
],
],
'sticky' => true,
'driver' => 'mysql',
'database' => 'database',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
],
请注意,配置数组中添加了三个键:read
、write
和 sticky
。read
和 write
键具有包含一个键(host
)的数组值。读和写连接的其余数据库选项将从主 mysql
配置数组合并。
只需在读和写数组中放置项目,如果您希望覆盖主 mysql
数组的值。因此,在这种情况下,192.168.1.1
将用作“读”连接的主机,而 192.168.1.3
将用于“写”连接。数据库凭据、前缀、字符集和主 mysql
数组中的所有其他选项将在两个连接之间共享。当主机配置数组中存在多个值时,每个请求将随机选择一个数据库主机。
sticky 选项
sticky
选项是一个可选值,用于允许在当前请求周期内写入数据库的记录立即进行读取。如果启用了 sticky
选项并且在当前请求周期内对数据库执行了“写”操作,任何进一步的“读”操作都将使用“写”连接。这确保了请求周期内写入的任何数据都可以立即从数据库中读取。您可以决定是否希望应用程序采用这种行为。
运行 SQL 查询
配置完数据库连接后,可以使用 DB
门面运行查询。DB
门面为每种类型的查询(select
、update
、insert
、delete
和 statement
)提供了方法。
运行 SELECT 查询
要运行基本的 SELECT
查询,可以使用 DB
门面上的 select
方法:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Show a list of all of the application's users.
*/
public function index(): View
{
$users = DB::select('select * from users where active = ?', [1]);
return view('user.index', ['users' => $users]);
}
}
传递给 select
方法的第一个参数是 SQL 查询,第二个参数是需要绑定到查询的参数绑定。通常,这些是where
子句限制的值。参数绑定提供了对抗 SQL 注入的保护。
select
方法将始终返回一个结果数组。数组中的每个结果都将是一个表示数据库记录的 PHP stdClass
对象:
use Illuminate\Support\Facades\DB;
$users = DB::select('select * from users');
foreach ($users as $user) {
echo $user->name;
}
选择标量值
有时您的数据库查询可能会导致单个标量值。您可以直接使用 scalar
方法检索此值,而无需从记录对象中检索查询的标量结果:
$burgers = DB::scalar(
"select count(case when food = 'burger' then 1 end) as burgers from menu"
);
选择多个结果集
如果应用程序调用返回多个结果集的存储过程,可以使用 selectResultSets
方法检索存储过程返回的所有结果集:
[$options, $notifications] = DB::selectResultSets(
"CALL get_user_options_and_notifications(?)", $request->user()->id
);
使用命名绑定
您可以使用命名绑定而不是使用?来表示参数绑定并执行查询:
$results = DB::select('select * from users where id = :id', ['id' => 1]);
运行插入语句
要执行插入语句,可以使用 DB
门面上的 insert
方法。与 select
一样,此方法接受 SQL 查询作为第一个参数,绑定作为第二个参数:
use Illuminate\Support\Facades\DB;
DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);
运行更新语句
应使用 update
方法来更新数据库中的现有记录。方法返回受该语句影响的行数:
use Illuminate\Support\Facades\DB;
$affected = DB::update(
'update users set votes = 100 where name = ?',
['Anita']
);
运行删除语句
应使用 delete
方法来从数据库中删除记录。与 update
一样,该方法将返回受影响的行数:
use Illuminate\Support\Facades\DB;
$deleted = DB::delete('delete from users');
运行通用语句
某些数据库语句不返回任何值。对于这些类型的操作,可以使用 DB
门面上的 statement
方法:
DB::statement('drop table users');
运行未准备好的语句
有时,您可能希望执行不绑定任何值的SQL语句。您可以使用 DB
门面的 unprepared
方法来实现这一点:
DB::unprepared('update users set votes = 100 where name = "Dries"');
由于未准备好的语句不绑定参数,因此可能容易受到 SQL 注入攻击。您不应允许用户控制的值在未准备好的语句中。
隐式提交
在事务中使用 DB
门面的 statement
和 unprepared
方法时,必须小心避免引发隐式提交的语句。这些语句会导致数据库引擎间接提交整个事务,使 Laravel 无法意识到数据库的事务级别。这样的语句示例是创建一个数据库表:
DB::unprepared('create table a (col varchar(1) null)');
请参阅 MySQL 手册以获取触发隐式提交的所有语句的列表。
使用多个数据库连接
如果您的应用程序在 config/database.php
配置文件中定义了多个连接,可以使用 DB
门面提供的 connection
方法访问每个连接。传递给 connection
方法的连接名称应对应于 config/database.php
配置文件中列出的连接之一,或者在运行时使用config助手进行配置:
use Illuminate\Support\Facades\DB;
$users = DB::connection('sqlite')->select(/* ... */);
使用 getPdo
方法在连接实例上可以访问原始的底层 PDO 实例:
$pdo = DB::connection()->getPdo();
监听查询事件
如果希望为应用程序执行的每个 SQL 查询指定一个闭包,可以使用 DB
门面的 listen
方法。这个方法可以用于记录查询或进行调试。可以在服务提供者的 boot
方法中注册查询监听器闭包:
<?php
namespace App\Providers;
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
DB::listen(function (QueryExecuted $query) {
// $query->sql;
// $query->bindings;
// $query->time;
});
}
}
监控累积查询时间
现代 web 应用的一个常见性能瓶颈是它们在查询数据库时花费的时间。幸运的是,Laravel 可以在单个请求期间查询数据库的时间超过阈值时调用您选择的闭包或回调函数。要开始使用,请提供查询时间阈值(以毫秒为单位)和闭包,以 whenQueryingForLongerThan
方法调用。可以在服务提供者的 boot
方法中调用此方法:
<?php
namespace App\Providers;
use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Events\QueryExecuted;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
// ...
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) {
// Notify development team...
});
}
}
数据库事务
可以使用 DB
门面提供的 transaction
方法在数据库事务中运行一组操作。如果在事务闭包中抛出异常,事务将自动回滚并重新抛出异常。如果闭包成功执行,事务将自动提交。在使用 transaction
方法时,不需要手动回滚或提交:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
});
处理死锁
transaction
方法接受一个可选的第二个参数,用于定义在发生死锁时事务应重试的次数。一旦这些尝试耗尽,将抛出异常:
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
DB::update('update users set votes = 1');
DB::delete('delete from posts');
}, 5);
手动使用事务
如果想手动开始一个事务,并完全控制回滚和提交,可以使用 DB
门面提供的 beginTransaction
方法:
use Illuminate\Support\Facades\DB;
DB::beginTransaction();
可以使用 rollBack
方法回滚事务:
DB::rollBack();
最后,可以使用 commit
方法提交事务:
DB::commit();
DB
门面的transaction
方法控制查询构建器和 Eloquent ORM 的事务。
连接到数据库 CLI
如果想要连接到数据库的 CLI,可以使用 db
Artisan命令:
php artisan db
如果需要,可以通过指定数据库连接名称来连接到不是默认连接的数据库连接:
php artisan db mysql
检查数据库
使用 db:show
和 db:table
Artisan命令,可以了解您的数据库及其关联的表的有价值的信息。要查看数据库的概述,包括其大小、类型、打开连接数和表的摘要,可以使用 db:show
命令:
php artisan db:show
可以通过通过 --database
选项在命令中提供数据库连接名称来指定要检查的数据库连接:
php artisan db:show --database=pgsql
如果希望在命令的输出中包含表行计数和数据库视图详细信息,可以分别提供 --counts
和 --views
选项。在大型数据库上,检索行计数和视图详细信息可能很慢:
php artisan db:show --counts --views
表概述
如果要获取数据库中个别表的概述,可以执行 db:table
Artisan命令。该命令提供了数据库表的总体概述,包括其列、类型、属性、键和索引:
php artisan db:table users
监控数据库
使用 db:monitor
Artisan命令,您可以指示Laravel在您的数据库管理超过指定数量的打开连接时调度一个Illuminate\Database\Events\DatabaseBusy
事件。
要开始,应调度 db:monitor
命令每分钟运行一次。命令接受您希望监视的数据库连接配置的名称,以及在调度事件之前应容忍的最大打开连接数量:
php artisan db:monitor --databases=mysql,pgsql --max=100
仅安排此命令还不足以触发一个提醒,以通知您打开连接的数量。当命令遇到拥有打开连接计数超过您的阈值的数据库时,将调度一个 DatabaseBusy
事件。您应在应用程序的 EventServiceProvider
中侦听此事件,以便向您或开发团队发送通知:
use App\Notifications\DatabaseApproachingMaxConnections;
use Illuminate\Database\Events\DatabaseBusy;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Notification;
/**
* Register any other events for your application.
*/
public function boot(): void
{
Event::listen(function (DatabaseBusy $event) {
Notification::route('mail', 'dev@example.com')
->notify(new DatabaseApproachingMaxConnections(
$event->connectionName,
$event->connections
));
});
}
无评论