Học Laravel – Bài 26: Database

Giới thiệu:

Laravel hỗ trợ tương tác db sử dụng raw SQL, fluent query builder và Eloquent ORM. Laravel hỗ trợ các db sau:

  • MariaDB
  • MySQL
  • PostgreSQL
  • SQLite
  • SQL Server

Cấu hình:

Cấu hình db ở file config/database.php. Mặc định cấu hình môi trường mẫu sử dụng Sail, 1 docker config cho Laravel.

Cấu hình sử dụng URL:

Một số service như AWS hoặc Heroku cung cấp db dưới dạng URL, ví dụ:

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

Để thuận tiện, Laravel hỗ trợ những URL kiểu này thông qua config url hoặc DATABASE_URL.

Đọc & Ghi connection:

Thi thoảng bạn cần sử dụng 1 db cho SELECT và db khác cho INSERT, UPDATE, DELETE:

'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' => '',
],

Tùy chọn sticky: là 1 giá trị tùy chọn cho phép ngay lập tức đọc record vừa được write vào db trong cùng request. Nếu option là true và 1 tác vụ write đang được thực hiện, mọi tác vụ read sẽ được sử dụng write connection. Điều này đảm bảo mọi dữ liệu được ghi trong suốt request sẽ được ngay lập tức read từ db.

Chạy SQL Query:

Sử dụng DB Facade với các method: select, update, insert, delete, statement.

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

Tham số thứ 2 của select là 1 tham số khi sử dụng prepare query. Thông thường đó là những giá trị của mệnh đề where. Parameter binding cung cấp sự bảo về chống lại SQL injection.

Method select trả về 1 array. Mỗi kết quả trong array là 1 PHP stdClass đại diện cho 1 bản ghi từ db:

use Illuminate\Support\Facades\DB;
 
$users = DB::select('select * from users');
 
foreach ($users as $user) {
    echo $user->name;
}

Select giá trị đơn:

$count_users = DB::scalar("select count(id) from users");

Select nhiều result set:

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

Binding bằng dấu ? hoặc sử dụng named binding đều được:

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

Insert:

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:

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

Chạy 1 lệnh thông thường:

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

Chạy 1 lệnh unprepare:

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

Unprepare không khuyến khích vì không bind tham số, dễ bị SQL injection.

Implicit Commits:

Khi sử dụng statementunprepared method (của Facade DB) trong transactions bạn phải cẩn thận để tránh những câu lệnh gây ra implicit commits. Những câu lệnh này sẽ khiến db engine gián tiếp commit toàn bộ transaction, khiến Laravel không kiểm soát được transaction level. Ví dụ 1 lệnh tạo table:

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

Tham khảo docs MySQL danh sách những lệnh có thể trigger implicit commits.

Sử dụng nhiều DB connections:

Nếu ứng dụng của bạn define nhiều connections trong config, bạn có thể tiếp cận 1 connection thông qua method connection cung cấp bởi DB facade. Tên connection trong method nên tương ứng 1 trong các connection đã được list trong file cấu hình:

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

Bạn có thể access PDO instance của connection sử dụng method getPdo:

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

Lắng nghe sự kiện Query:

Nếu bạn muốn chỉ định 1 closure sẽ được gọi cho mỗi SQL query được thực thi, sử dụng method listen. Method này hữu ích cho logging hoặc debug. Register query listener closure trong method boot của service provider:

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

Database transactions:

Bạn có thể sử dụng method transaction cung cấp bởi DB để chạy 1 tệp lệnh trong db transaction. Nếu có 1 ngoại lệ trong transaction closure, transaction sẽ tự động roll back.Nếu closure thực thi thành công, transaction tự động commit. Bạn không cần roll back bằng tay hoặc commit khi sử dụng method transaction:

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

Bạn có thể truyền tham số thứ 2 để define số lần 1 transaction có thể thực hiện lại nếu xảy ra deadlock. Khi hết số lần thử, 1 ngoại lệ sẽ được ném ra:

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

Sử dụng transaction bằng tay:

use Illuminate\Support\Facades\DB;
 
DB::beginTransaction();

Roll back:

DB::rollBack();

Commit transaction:

DB::commit();

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *