LaravelDocs(中文)

資料庫基礎(Getting Started)

簡介 (Introduction)

幾乎每個現代 Web 應用程式都會與資料庫互動。Laravel 讓使用原始 SQL、流暢查詢建構器Eloquent ORM 與各種支援的資料庫互動變得非常簡單。目前,Laravel 提供對五種資料庫的第一方支援:

此外,MongoDB 透過 mongodb/laravel-mongodb 套件獲得支援,該套件由 MongoDB 官方維護。請查看 Laravel MongoDB 文件以了解更多資訊。

設定 (Configuration)

Laravel 資料庫服務的設定位於你應用程式的 config/database.php 設定檔中。在此檔案中,你可以定義所有資料庫連線,以及指定預設應使用哪個連線。此檔案中的大多數設定選項由你應用程式的環境變數值驅動。此檔案中提供了 Laravel 支援的大多數資料庫系統的範例。

預設情況下,Laravel 的範例環境設定已準備好與 Laravel Sail 一起使用,這是一個用於在本機開發 Laravel 應用程式的 Docker 設定。不過,你可以根據本機資料庫的需要自由修改資料庫設定。

SQLite 設定 (Sqlite Configuration)

SQLite 資料庫包含在檔案系統上的單一檔案中。你可以使用終端機中的 touch 指令建立新的 SQLite 資料庫:touch database/database.sqlite。建立資料庫後,你可以透過將資料庫的絕對路徑放入 DB_DATABASE 環境變數中,輕鬆設定環境變數以指向此資料庫:

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

預設情況下,SQLite 連線會啟用外鍵約束。如果你想停用它們,應該將 DB_FOREIGN_KEYS 環境變數設為 false

DB_FOREIGN_KEYS=false

[!NOTE] 如果你使用 Laravel 安裝器建立 Laravel 應用程式並選擇 SQLite 作為你的資料庫,Laravel 將自動建立 database/database.sqlite 檔案並為你執行預設的資料庫遷移

Microsoft SQL Server 設定 (Mssql Configuration)

要使用 Microsoft SQL Server 資料庫,你應該確保已安裝 sqlsrvpdo_sqlsrv PHP 擴充套件以及它們可能需要的任何相依性,例如 Microsoft SQL ODBC 驅動程式。

使用 URL 設定 (Configuration Using Urls)

通常,資料庫連線使用多個設定值進行設定,例如 hostdatabaseusernamepassword 等。每個設定值都有其對應的環境變數。這意味著在生產伺服器上設定資料庫連線資訊時,你需要管理多個環境變數。

某些託管資料庫提供者(如 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(或對應的 DB_URL 環境變數)設定選項,它將用於提取資料庫連線和憑證資訊。

讀寫連線 (Read And Write Connections)

有時你可能希望使用一個資料庫連線來執行 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,

    'database' => env('DB_DATABASE', 'laravel'),
    'username' => env('DB_USERNAME', 'root'),
    'password' => env('DB_PASSWORD', ''),
    'unix_socket' => env('DB_SOCKET', ''),
    'charset' => env('DB_CHARSET', 'utf8mb4'),
    'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
    'prefix' => '',
    'prefix_indexes' => true,
    'strict' => true,
    'engine' => null,
    'options' => extension_loaded('pdo_mysql') ? array_filter([
        PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
    ]) : [],
],

請注意,設定陣列中已新增三個鍵:readwritestickyreadwrite 鍵的陣列值包含一個鍵:hostreadwrite 連線的其餘資料庫選項將從主 mysql 設定陣列中合併。

只有在你希望覆寫主 mysql 陣列中的值時,才需要將項目放入 readwrite 陣列中。因此,在此範例中,192.168.1.1 將用作「讀取」連線的主機,而 192.168.1.3 將用於「寫入」連線。主 mysql 陣列中的資料庫憑證、前綴、字元集和所有其他選項將在兩個連線之間共享。當 host 設定陣列中存在多個值時,將為每個請求隨機選擇一個資料庫主機。

sticky 選項 (The Sticky Option)

sticky 選項是一個選用值,可用於允許立即讀取在當前請求週期內寫入資料庫的記錄。如果啟用了 sticky 選項,並且在當前請求週期內對資料庫執行了「寫入」操作,則任何進一步的「讀取」操作都將使用「寫入」連線。這確保了在請求週期內寫入的任何資料都可以在同一請求期間立即從資料庫讀取。由你決定這是否是應用程式所需的行為。

執行 SQL 查詢 (Running Queries)

設定資料庫連線後,你可以使用 DB facade 執行查詢。DB facade 為每種類型的查詢提供了方法:selectupdateinsertdeletestatement

執行 Select 查詢 (Running A Select Query)

要執行基本的 SELECT 查詢,你可以在 DB facade 上使用 select 方法:

<?php

namespace App\Http\Controllers;

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 方法將始終回傳結果的 array。陣列中的每個結果都是一個 PHP stdClass 物件,代表資料庫中的一筆記錄:

use Illuminate\Support\Facades\DB;

$users = DB::select('select * from users');

foreach ($users as $user) {
    echo $user->name;
}

選擇純量值 (Selecting Scalar Values)

有時你的資料庫查詢可能會產生單一的純量值。Laravel 允許你使用 scalar 方法直接檢索此值,而不需要從記錄物件中檢索查詢的純量結果:

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

選擇多個結果集 (Selecting Multiple Result Sets)

如果你的應用程式呼叫回傳多個結果集的預存程序,你可以使用 selectResultSets 方法來檢索預存程序回傳的所有結果集:

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

使用命名綁定 (Using Named Bindings)

你可以使用命名綁定執行查詢,而不是使用 ? 來表示參數綁定:

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

執行 Insert 語句 (Running An Insert Statement)

要執行 insert 語句,你可以在 DB facade 上使用 insert 方法。與 select 一樣,此方法接受 SQL 查詢作為第一個參數,綁定作為第二個參數:

use Illuminate\Support\Facades\DB;

DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);

執行 Update 語句 (Running An Update Statement)

update 方法應用於更新資料庫中的現有記錄。該方法回傳受語句影響的列數:

use Illuminate\Support\Facades\DB;

$affected = DB::update(
    'update users set votes = 100 where name = ?',
    ['Anita']
);

執行 Delete 語句 (Running A Delete Statement)

delete 方法應用於從資料庫中刪除記錄。與 update 一樣,該方法將回傳受影響的列數:

use Illuminate\Support\Facades\DB;

$deleted = DB::delete('delete from users');

執行一般語句 (Running A General Statement)

有些資料庫語句不回傳任何值。對於這些類型的操作,你可以在 DB facade 上使用 statement 方法:

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

執行未準備的語句 (Running An Unprepared Statement)

有時你可能想要執行 SQL 語句而不綁定任何值。你可以使用 DB facade 的 unprepared 方法來完成此操作:

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

[!WARNING] 由於未準備的語句不綁定參數,它們可能容易受到 SQL 注入攻擊。你絕不應該允許使用者控制的值出現在未準備的語句中。

交易中的隱式提交 (Implicit Commits In Transactions)

在交易中使用 DB facade 的 statementunprepared 方法時,你必須小心避免導致隱式提交的語句。這些語句將導致資料庫引擎間接提交整個交易,使 Laravel 不知道資料庫的交易層級。建立資料庫表就是這種語句的範例:

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

請參閱 MySQL 手冊以了解所有觸發隱式提交的語句清單

使用多個資料庫連線 (Using Multiple Database Connections)

如果你的應用程式在 config/database.php 設定檔中定義了多個連線,你可以透過 DB facade 提供的 connection 方法存取每個連線。傳遞給 connection 方法的連線名稱應對應於 config/database.php 設定檔中列出的連線之一,或在執行階段使用 config 輔助函式設定的連線:

use Illuminate\Support\Facades\DB;

$users = DB::connection('sqlite')->select(/* ... */);

你可以使用連線實例上的 getPdo 方法存取連線的原始底層 PDO 實例:

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

監聽查詢事件 (Listening For Query Events)

如果你想指定一個閉包,在應用程式執行的每個 SQL 查詢時被呼叫,你可以使用 DB facade 的 listen 方法。此方法對於記錄查詢或除錯很有用。你可以在 service providerboot 方法中註冊查詢監聽器閉包:

<?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;
            // $query->toRawSql();
        });
    }
}

監控累積查詢時間 (Monitoring Cumulative Query Time)

現代 Web 應用程式的一個常見效能瓶頸是它們花費在查詢資料庫上的時間。幸運的是,當 Laravel 在單一請求期間花費太多時間查詢資料庫時,它可以呼叫你選擇的閉包或回呼。要開始使用,請向 whenQueryingForLongerThan 方法提供查詢時間閾值(以毫秒為單位)和閉包。你可以在 service providerboot 方法中呼叫此方法:

<?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...
        });
    }
}

資料庫交易 (Database Transactions)

你可以使用 DB facade 提供的 transaction 方法在資料庫交易中執行一組操作。如果在交易閉包中拋出例外,交易將自動回滾並重新拋出例外。如果閉包成功執行,交易將自動提交。使用 transaction 方法時,你無需擔心手動回滾或提交:

use Illuminate\Support\Facades\DB;

DB::transaction(function () {
    DB::update('update users set votes = 1');

    DB::delete('delete from posts');
});

處理死鎖 (Handling Deadlocks)

transaction 方法接受一個可選的第二個參數,該參數定義發生死鎖時應重試交易的次數。一旦用盡這些嘗試,將拋出例外:

use Illuminate\Support\Facades\DB;

DB::transaction(function () {
    DB::update('update users set votes = 1');

    DB::delete('delete from posts');
}, attempts: 5);

手動使用交易 (Manually Using Transactions)

如果你想手動開始交易並完全控制回滾和提交,你可以使用 DB facade 提供的 beginTransaction 方法:

use Illuminate\Support\Facades\DB;

DB::beginTransaction();

你可以透過 rollBack 方法回滾交易:

DB::rollBack();

最後,你可以透過 commit 方法提交交易:

DB::commit();

[!NOTE] > DB facade 的交易方法控制查詢建構器Eloquent ORM 的交易。

連線到資料庫 CLI (Connecting To The Database Cli)

如果你想連線到資料庫的 CLI,你可以使用 db Artisan 指令:

php artisan db

如果需要,你可以指定資料庫連線名稱以連線到非預設連線的資料庫連線:

php artisan db mysql

檢查你的資料庫 (Inspecting Your Databases)

使用 db:showdb:table Artisan 指令,你可以獲得有關資料庫及其相關表的寶貴見解。要查看資料庫的概覽,包括其大小、類型、開啟連線數和表的摘要,你可以使用 db:show 指令:

php artisan db:show

你可以透過 --database 選項向指令提供資料庫連線名稱來指定應檢查哪個資料庫連線:

php artisan db:show --database=pgsql

如果你想在指令的輸出中包含表列數和資料庫檢視詳細資訊,你可以分別提供 --counts--views 選項。在大型資料庫上,檢索列數和檢視詳細資訊可能會很慢:

php artisan db:show --counts --views

此外,你可以使用以下 Schema 方法來檢查你的資料庫:

use Illuminate\Support\Facades\Schema;

$tables = Schema::getTables();
$views = Schema::getViews();
$columns = Schema::getColumns('users');
$indexes = Schema::getIndexes('users');
$foreignKeys = Schema::getForeignKeys('users');

如果你想檢查不是應用程式預設連線的資料庫連線,你可以使用 connection 方法:

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

表概覽 (Table Overview)

如果你想獲得資料庫中單個表的概覽,你可以執行 db:table Artisan 指令。此指令提供資料庫表的一般概覽,包括其欄位、類型、屬性、鍵和索引:

php artisan db:table users

監控你的資料庫 (Monitoring Your Databases)

使用 db:monitor Artisan 指令,如果你的資料庫管理的開啟連線數超過指定數量,你可以指示 Laravel 分派 Illuminate\Database\Events\DatabaseBusy 事件。

要開始使用,你應該排程 db:monitor 指令每分鐘執行一次。該指令接受你希望監控的資料庫連線設定名稱,以及在分派事件之前應容忍的最大開啟連線數:

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

僅排程此指令不足以觸發通知,提醒你開啟連線的數量。當指令遇到開啟連線數超過閾值的資料庫時,將分派 DatabaseBusy 事件。你應該在應用程式的 AppServiceProvider 中監聽此事件,以便向你或你的開發團隊發送通知:

use App\Notifications\DatabaseApproachingMaxConnections;
use Illuminate\Database\Events\DatabaseBusy;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Notification;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Event::listen(function (DatabaseBusy $event) {
        Notification::route('mail', 'dev@example.com')
            ->notify(new DatabaseApproachingMaxConnections(
                $event->connectionName,
                $event->connections
            ));
    });
}