跳至内容

数据库:入门

介绍

几乎每个现代 Web 应用程序都会与数据库交互。Laravel 使用原始 SQL、流畅的查询构建器Eloquent ORM ,使与各种受支持的数据库的交互变得极其简单。目前,Laravel 为以下五个数据库提供第一方支持:

此外,MongoDB 还通过mongodb/laravel-mongodb由 MongoDB 官方维护的软件包获得支持。查看Laravel MongoDB文档了解更多信息。

配置

Laravel 数据库服务的配置位于应用程序的config/database.php配置文件中。在此文件中,您可以定义所有数据库连接,并指定默认使用的连接。此文件中的大多数配置选项由应用程序的环境变量值驱动。此文件中提供了 Laravel 支持的大多数数据库系统的示例。

默认情况下,Laravel 的示例环境配置已与Laravel Sail配合使用,Laravel Sail 是一个用于在本地计算机上开发 Laravel 应用程序的 Docker 配置。但是,您可以根据需要随意修改本地数据库的配置。

SQLite 配置

SQLite 数据库包含在文件系统上的单个文件中。您可以使用touch终端中的以下命令创建一个新的 SQLite 数据库:touch database/database.sqlite。创建数据库后,您可以通过在环境变量中放置数据库的绝对路径来轻松配置环境变量以指向该数据库DB_DATABASE

1DB_CONNECTION=sqlite
2DB_DATABASE=/absolute/path/to/database.sqlite

默认情况下,SQLite 连接启用外键约束。如果要禁用它们,应将DB_FOREIGN_KEYS环境变量设置为false

1DB_FOREIGN_KEYS=false

如果您使用Laravel 安装程序创建 Laravel 应用程序并选择 SQLite 作为数据库,Laravel 将自动创建一个文件并为您database/database.sqlite运行默认数据库迁移。

Microsoft SQL Server 配置

要使用 Microsoft SQL Server 数据库,您应该确保已安装sqlsrvpdo_sqlsrvPHP 扩展以及它们可能需要的任何依赖项,例如 Microsoft SQL ODBC 驱动程序。

使用 URL 进行配置

通常,数据库连接使用多个配置值进行配置,例如hostdatabaseusernamepassword等。每个配置值都有其对应的环境变量。这意味着在生产服务器上配置数据库连接信息时,您需要管理多个环境变量。

一些托管数据库提供商(例如 AWS 和 Heroku)提供单个数据库“URL”,该 URL 将数据库的所有连接信息包含在一个字符串中。数据库 URL 示例可能如下所示:

1mysql://root:password@127.0.0.1/forge?charset=UTF-8

这些 URL 通常遵循标准架构约定:

1driver://username:password@host:port/database?options

为了方便起见,Laravel 支持这些 URL,作为使用多个配置选项配置数据库的替代方案。如果存在url(或相应的DB_URL环境变量)配置选项,它将用于提取数据库连接和凭据信息。

读取和写入连接

有时您可能希望将一个数据库连接用于 SELECT 语句,将另一个数据库连接用于 INSERT、UPDATE 和 DELETE 语句。Laravel 让这一切变得轻而易举,无论您使用原始查询、查询构建器还是 Eloquent ORM,Laravel 都会始终使用正确的连接。

为了了解如何配置读/写连接,让我们看这个例子:

1'mysql' => [
2 'read' => [
3 'host' => [
4 '192.168.1.1',
5 '196.168.1.2',
6 ],
7 ],
8 'write' => [
9 'host' => [
10 '196.168.1.3',
11 ],
12 ],
13 'sticky' => true,
14 
15 'database' => env('DB_DATABASE', 'laravel'),
16 'username' => env('DB_USERNAME', 'root'),
17 'password' => env('DB_PASSWORD', ''),
18 'unix_socket' => env('DB_SOCKET', ''),
19 'charset' => env('DB_CHARSET', 'utf8mb4'),
20 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
21 'prefix' => '',
22 'prefix_indexes' => true,
23 'strict' => true,
24 'engine' => null,
25 'options' => extension_loaded('pdo_mysql') ? array_filter([
26 PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
27 ]) : [],
28],

请注意,已将三个键添加到配置数组中:readwritestickyreadwrite键的数组值包含单个键: 。和连接host的其余数据库选项将从主配置数组合并readwritemysql

如果您希望覆盖主数组中的值,则只需将项放入read和数组中。因此,在本例中,将用作“读取”连接的主机,而将用作“写入”连接的主机。主数组中的数据库凭据、前缀、字符集和所有其他选项将在两个连接之间共享。当配置数组中存在多个值时,将为每个请求随机选择一个数据库主机。writemysql192.168.1.1192.168.1.3mysqlhost

选择sticky

sticky选项是一个可选值,可用于允许立即读取在当前请求周期内已写入数据库的记录。如果sticky启用该选项,并且在当前请求周期内对数据库执行了“写入”操作,则任何后续的“读取”操作都将使用“写入”连接。这确保了在请求周期内写入的任何数据都可以在同一请求期间立即从数据库读回。您可以自行决定这是否是您的应用程序所需的行为。

运行 SQL 查询

配置数据库连接后,即可使用DBFacade 运行查询。FacadeDB为每种查询类型提供了方法:selectupdateinsertdeletestatement

运行选择查询

要运行基本的 SELECT 查询,您可以使用外观select上的方法DB

1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Support\Facades\DB;
6use Illuminate\View\View;
7 
8class UserController extends Controller
9{
10 /**
11 * Show a list of all of the application's users.
12 */
13 public function index(): View
14 {
15 $users = DB::select('select * from users where active = ?', [1]);
16 
17 return view('user.index', ['users' => $users]);
18 }
19}

传递给该方法的第一个参数select是 SQL 查询,第二个参数是需要绑定到查询的任何参数绑定。通常,这些是where子句约束的值。参数绑定可以防止 SQL 注入。

select方法总是返回一个array结果集。数组中的每个结果都是一个 PHPstdClass对象,代表数据库中的一条记录:

1use Illuminate\Support\Facades\DB;
2 
3$users = DB::select('select * from users');
4 
5foreach ($users as $user) {
6 echo $user->name;
7}

选择标量值

有时,数据库查询可能会返回单个标量值。Laravel 无需从记录对象中检索查询的标量结果,而是允许你使用该scalar方法直接检索此值:

1$burgers = DB::scalar(
2 "select count(case when food = 'burger' then 1 end) as burgers from menu"
3);

选择多个结果集

如果您的应用程序调用返回多个结果集的存储过程,则可以使用该selectResultSets方法检索存储过程返回的所有结果集:

1[$options, $notifications] = DB::selectResultSets(
2 "CALL get_user_options_and_notifications(?)", $request->user()->id
3);

使用命名绑定

?您可以使用命名绑定来执行查询,而不是使用来表示参数绑定:

1$results = DB::select('select * from users where id = :id', ['id' => 1]);

运行插入语句

要执行insert语句,可以使用外观层insert上的 方法DB。与 类似select,此方法接受 SQL 查询作为其第一个参数,并将绑定作为其第二个参数:

1use Illuminate\Support\Facades\DB;
2 
3DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);

运行更新语句

update方法应用于更新数据库中的现有记录。该方法返回受该语句影响的行数:

1use Illuminate\Support\Facades\DB;
2 
3$affected = DB::update(
4 'update users set votes = 100 where name = ?',
5 ['Anita']
6);

运行删除语句

delete方法用于从数据库中删除记录。例如update,该方法将返回受影响的行数:

1use Illuminate\Support\Facades\DB;
2 
3$deleted = DB::delete('delete from users');

运行一般性声明

某些数据库语句不返回任何值。对于这些类型的操作,你可以statementDB外观上使用以下方法:

1DB::statement('drop table users');

运行未准备好的语句

有时你可能希望执行一条不绑定任何值的 SQL 语句。你可以使用DBFacade 的unprepared方法来实现:

1DB::unprepared('update users set votes = 100 where name = "Dries"');

由于未准备好的语句不绑定参数,因此可能存在 SQL 注入漏洞。切勿在未准备好的语句中允许用户控制值。

隐式提交

在事务中使用DBFacadestatementunprepared方法时,必须小心避免使用导致隐式提交的语句。这些语句会导致数据库引擎间接提交整个事务,从而使 Laravel 无法感知数据库的事务级别。此类语句的一个例子是创建数据库表:

1DB::unprepared('create table a (col varchar(1) null)');

请参阅 MySQL 手册以获取触发隐式提交的所有语句的列表

使用多个数据库连接

如果您的应用程序在配置文件中定义了多个连接,您可以通过外观层提供的方法config/database.php访问每个连接。传递给该方法的连接名称应该对应于配置文件中列出的连接之一,或在运行时使用Helpers程序配置的连接之一connectionDBconnectionconfig/database.phpconfig

1use Illuminate\Support\Facades\DB;
2 
3$users = DB::connection('sqlite')->select(/* ... */);

getPdo您可以使用连接实例上的方法访问连接的原始底层 PDO 实例:

1$pdo = DB::connection()->getPdo();

监听查询事件

如果您想指定一个闭包,用于在应用程序执行每个 SQL 查询时调用,可以使用DBFacade 的方法。此方法对于记录查询或调试非常有用。您可以在服务提供者的方法listen中注册查询监听器闭包boot

1<?php
2 
3namespace App\Providers;
4 
5use Illuminate\Database\Events\QueryExecuted;
6use Illuminate\Support\Facades\DB;
7use Illuminate\Support\ServiceProvider;
8 
9class AppServiceProvider extends ServiceProvider
10{
11 /**
12 * Register any application services.
13 */
14 public function register(): void
15 {
16 // ...
17 }
18 
19 /**
20 * Bootstrap any application services.
21 */
22 public function boot(): void
23 {
24 DB::listen(function (QueryExecuted $query) {
25 // $query->sql;
26 // $query->bindings;
27 // $query->time;
28 // $query->toRawSql();
29 });
30 }
31}

监控累计查询时间

现代 Web 应用程序的一个常见性能瓶颈是查询数据库所花费的时间。值得庆幸的是,当 Laravel 在单个请求中花费过多时间查询数据库时,它可以调用您选择的闭包或回调。首先,为该方法提供一个查询时间阈值(以毫秒为单位)和闭包。您可以在服务提供者的方法whenQueryingForLongerThan中调用此方法boot

1<?php
2 
3namespace App\Providers;
4 
5use Illuminate\Database\Connection;
6use Illuminate\Support\Facades\DB;
7use Illuminate\Support\ServiceProvider;
8use Illuminate\Database\Events\QueryExecuted;
9 
10class AppServiceProvider extends ServiceProvider
11{
12 /**
13 * Register any application services.
14 */
15 public function register(): void
16 {
17 // ...
18 }
19 
20 /**
21 * Bootstrap any application services.
22 */
23 public function boot(): void
24 {
25 DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) {
26 // Notify development team...
27 });
28 }
29}

数据库事务

您可以使用外观transaction提供的方法DB在数据库事务中运行一组操作。如果事务闭包中抛出异常,事务将自动回滚并重新抛出异常。如果闭包成功执行,事务将自动提交。使用该方法时,您无需担心手动回滚或提交transaction

1use Illuminate\Support\Facades\DB;
2 
3DB::transaction(function () {
4 DB::update('update users set votes = 1');
5 
6 DB::delete('delete from posts');
7});

处理死锁

transaction方法接受一个可选的第二个参数,该参数定义了发生死锁时事务应重试的次数。一旦尝试次数耗尽,将抛出异常:

1use Illuminate\Support\Facades\DB;
2 
3DB::transaction(function () {
4 DB::update('update users set votes = 1');
5 
6 DB::delete('delete from posts');
7}, 5);

手动使用事务

如果您希望手动开始事务并完全控制回滚和提交,则可以使用外观beginTransaction提供的方法DB

1use Illuminate\Support\Facades\DB;
2 
3DB::beginTransaction();

您可以通过下列方法回滚事务rollBack

1DB::rollBack();

最后,您可以通过该方法提交交易commit

1DB::commit();

外观的事务方法控制查询构建器Eloquent ORM 的DB事务

连接到数据库 CLI

如果您想连接到数据库的 CLI,您可以使用dbArtisan 命令:

1php artisan db

如果需要,您可以指定数据库连接名称来连接到非默认连接的数据库连接:

1php artisan db mysql

检查数据库

使用db:showArtisandb:table命令,您可以深入了解数据库及其关联表。要查看数据库的概览,包括其大小、类型、打开的连接数以及表的摘要,可以使用以下db:show命令:

1php artisan db:show

您可以通过以下选项向命令提供数据库连接名称来指定应检查哪个数据库连接--database

1php artisan db:show --database=pgsql

如果您希望在命令输出中包含表行数和数据库视图详细信息,可以分别提供--counts--views选项。在大型数据库中,检索行数和视图详细信息可能会很慢:

1php artisan db:show --counts --views

此外,您可以使用以下Schema方法来检查您的数据库:

1use Illuminate\Support\Facades\Schema;
2 
3$tables = Schema::getTables();
4$views = Schema::getViews();
5$columns = Schema::getColumns('users');
6$indexes = Schema::getIndexes('users');
7$foreignKeys = Schema::getForeignKeys('users');

如果您想检查不是应用程序默认连接的数据库连接,您可以使用该connection方法:

1$columns = Schema::connection('sqlite')->getColumns('users');

表格概览

如果您想概览数据库中的单个表,可以执行db:tableArtisan 命令。此命令提供数据库表的总体概览,包括其列、类型、属性、键和索引:

1php artisan db:table users

监控数据库

使用Artisan 命令,如果您的数据库管理的打开连接数超过指定数量,db:monitor您可以指示 Laravel 分派事件。Illuminate\Database\Events\DatabaseBusy

首先,您应该将db:monitor命令设置为每分钟运行一次。该命令接受您希望监视的数据库连接配置的名称,以及在调度事件之前应允许的最大打开连接数:

1php artisan db:monitor --databases=mysql,pgsql --max=100

仅调度此命令不足以触发通知来提醒您打开的连接数。当该命令遇到打开连接数超过阈值的数据库时,DatabaseBusy将会调度一个事件。您应该在应用程序中监听此事件,AppServiceProvider以便向您或您的开发团队发送通知:

1use App\Notifications\DatabaseApproachingMaxConnections;
2use Illuminate\Database\Events\DatabaseBusy;
3use Illuminate\Support\Facades\Event;
4use Illuminate\Support\Facades\Notification;
5 
6/**
7 * Bootstrap any application services.
8 */
9public function boot(): void
10{
11 Event::listen(function (DatabaseBusy $event) {
12 Notification::route('mail', 'dev@example.com')
13 ->notify(new DatabaseApproachingMaxConnections(
14 $event->connectionName,
15 $event->connections
16 ));
17 });
18}