Laravel資料庫事務:如何有效地實現和使用它們

Laravel資料庫事務:如何有效地實現和使用它們

資料的準確性和一致性問題可能會導致從小的不便到大的企業關注。構建程式碼,安全地儲存,更改和刪除資料庫中的資料是至關重要的。

進入Laravel資料庫事務。

資料庫事務是確保資料完整性的一個有效方法。Laravel簡化了廣泛的資料庫的事務。

但它們到底是什麼? 你怎麼能在Laravel中解決它們?

在這個廣泛的教程結束時, 你會了解到所有關於Laravel的資料庫事務,以及如何在你的專案中有效地使用它們。

  1. 什麼是Laravel資料庫事務?
  2. Laravel資料庫事務的選項
  3. 如何與你的Laravel資料庫一起工作
  4. 在Laravel中使用原始資料庫查詢的另外5種方法

什麼是Laravel資料庫事務?

在我們進入技術層面之前, 讓我們首先了解什麼是Laravel資料庫事務,以及你如何從它們中受益.

一個資料庫事務是一組操作,你可以在你的應用程式的資料庫結構中安全地進行,如修改資料的SQL查詢(如更新,刪除和插入)。

在任何時候,你都可以決定回滾所有事務的查詢。此外,你所做的任何查詢將被資料庫視為一個單一的動作。

讓我們看一下這個例子。

假設我們有一個讓使用者建立賬戶的應用。自然地,每個賬戶可以有一個或許多使用者附屬。如果這個應用程式同時生成一個賬戶和第一個使用者,你就必須處理如果賬戶被正確生成,但使用者沒有被生成的情況。

請看一下這個示例程式碼:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Create Account
$newAcct = Account::create([
'accountname' => Input::get('accountname'),
]);
// Create User
$newUser = User::create([
'username' => Input::get('username'),
'account_id' => $newAcct->id,
]);
// Create Account $newAcct = Account::create([ 'accountname' => Input::get('accountname'), ]); // Create User $newUser = User::create([ 'username' => Input::get('username'), 'account_id' => $newAcct->id, ]);
// Create Account
$newAcct = Account::create([
'accountname' => Input::get('accountname'),
]);
// Create User
$newUser = User::create([
'username' => Input::get('username'),
'account_id' => $newAcct->id,
]);

這裡有兩種情況會導致不愉快的問題:

  1. 帳戶沒有生成
  2. 未能建立一個使用者

讓我們考慮後一種情況。

擁有一個沒有可用使用者的賬戶,會導致資料庫中的資料不一致。為了解決這個問題,你可以通過艱鉅的任務,圍繞它進行編碼,或者節省大量的程式碼,或者乾脆用事務來包裹它,以快速完成工作。

雖然資料庫事務存在於大多數SQL資料庫中,但它們主要在實施和效率上有所不同。流行的系統如MySQL、SQLite、PostgreSQL和Oracle都支援事務,所以你在部署你喜歡的SQL資料庫時不應該有困難。

遷移

遷移是Laravel的一個重要功能,它允許你在資料庫中建立一個表,進行修改,並分享應用程式的資料庫模式。你可以使用Laravel遷移來編輯表,新增新的列或刪除現有的列。

假如你正在和一個團隊討論想法,需要對錶進行調整。這個SQL檔案必須由團隊中的某個人分享和匯入。他們有可能忘記匯入SQL檔案,導致應用程式的執行出現問題。

這就是Laravel遷移的用武之地。你可以在資料庫中新增一個新的列,或者刪除條目,而不影響已經存在的列。

資料填充器

資料填充是Laravel為開發者提供的一個工具,以方便測試不同的資料型別,修復錯誤,並調整效能。你可以通過資料庫播種器在一個命令中自動新增多行假資料到你的資料庫表中。

因此,你可以用一個新的資料庫和樣本值重新開始,而不是每次恢復資料庫時都要手動輸入。

Laravel資料庫事務的選項

Laravel提供了不同的工具來管理你的資料,如Adminer。對於資料庫交易, 在資料庫端有三種方法可以手動啟動交易並完全控制交易管理.

許多使用者發現這些選項更靈活,可以準確地定義一個事務應該何時提交或回滾:

  • 建立一個事務:使用DB::beginTransaction(); 命令來啟動一個事務。
  • 回滾一個事務:使用DB::rollBack(); 命令,如果你想進行修改或撤消操作。
  • 提交一個事務:如果一切按計劃進行,使用DB::commit(); 命令。

一定要記住用提交或回滾動作來結束每個開放的事務,特別是迴圈。否則, 這個手動方法將失去同步性,你的記錄將不會被更新。

如何使用你的Laravel資料庫

遷移和播種機, 如前所述,是為Laravel開發人員設計的複雜的解決方案,通過減少差異,快速部署,刪除和恢復一個應用程式的資料庫。它是很方便的, 特別是當多個開發人員在同一個應用程式上工作時.

本節將向你展示如何使用artisan命令在你的Laravel資料庫中輕鬆使用遷移和資料填充。

前提條件

以下是你需要開始使用的東西:

  1. 一個擁有sudo許可權的非root使用者在Ubuntu 18.04本地電腦或開發伺服器上。如果你使用的是遠端伺服器,最好能設定一個有效的防火牆。
  2. 在你的機器上安裝LEMP。你可以選擇安裝Docker和Docker Compose來執行你的應用程式,如果你覺得用它們工作更舒服的話。

還有其他的網路開發工具,你可以根據你的技能和編碼需求來使用。

Laravel遷移

在遷移類中有兩種方法:向上和向下。向上的方法是用來在你的資料庫中建立新的表,索引,或列。向下的方法應該是撤銷向上方法的效果。

你可以使用Laravel模式構建器在這些方法中自由構建和編輯表。例如, 這個遷移生成了一個航班表:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateFlightsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up() {
Schema::create('flights', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('airline');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down() {
Schema::drop('flights');
}
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateFlightsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('flights', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('name'); $table->string('airline'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('flights'); }
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateFlightsTable extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up() {
Schema::create('flights', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('airline');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down() {
Schema::drop('flights');
}

請記住, make:migration 命令需要明確表的名稱。所以要確保 table_name 與你想要的一致。

你可以使用 --table--create 選項來指定表的名稱以及遷移是否會建立一個新的表,如下所示:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
php artisan make:migration create_users_table --create=users
php artisan make:migration create_users_table --create=users
php artisan make:migration create_users_table --create=users
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
php artisan make:migration add_votes_to_users_table --table=users
php artisan make:migration add_votes_to_users_table --table=users
php artisan make:migration add_votes_to_users_table --table=users

你的database/migrations目錄現在將包括新的遷移。每個遷移檔名都包括一個時間戳, Laravel用它來確定遷移的順序.

你也可以選擇定義一個 --path, 它應該與你的安裝的根目錄有關。使用下面的命令:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
php artisan migrate:make foo --path=app/migrations
php artisan migrate:make foo --path=app/migrations
php artisan migrate:make foo --path=app/migrations

執行遷移

在執行遷移時,你可以利用一些有用的命令。讓我們來看看其中的幾個:

  • php artisan migrate:這個命令將你所有的模式釋出到資料庫中。它還會在資料庫中生成一個表。
  • php artisan migrate --path=app/foo/migrations:這條命令在一個目錄下執行所有的遷移程式。如果你收到錯誤資訊 “Nothing to migrate”,請執行 php artisan migrate --path=database/migrations/foo 命令,去掉app目錄。
  • php artisan migrate --package=vendor/package:如果你想為一個包執行遷移,請使用此命令。

有時你可能會在執行遷移的時候遇到 “Class not found” 的錯誤。如果你遇到這種情況,請執行 composer dump-autoload 命令。

有些遷移可能是危險的,可能會導致你的資料丟失。因此, Laravel會提示你確認執行命令以保護你的資料。

如果你不希望被提示,可以使用 --force flag 來強制執行命令,如下所示:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
php artisan migrate --force
php artisan migrate --force
php artisan migrate --force

回滾遷移

當你需要逆轉上一個遷移批次時,請使用回滾命令,如下所示:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
php artisan migrate:rollback
php artisan migrate:rollback
php artisan migrate:rollback

這裡還有一些你可以使用的回滾命令:

  • php artisan migrate:reset:這個命令可以回滾所有的遷移操作,而不僅僅是最後一次的操作。
  • php artisan migrate:fresh:當你想重新安裝你的資料庫時,使用這個命令。它將刪除所有現有的表並執行 migration 命令。
  • php artisan migrate:refresh:這是一條二合一的命令,同時執行 :rollback and migrate 命令。
  • php artisan migrate:fresh --seed:這條命令在向資料庫播種之前執行 migrate:fresh 命令。當你在一個新的主機上安裝應用程式時,你可以利用這個命令來播種(即上傳資料到)資料庫。

Laravel資料填充

資料填充器是一個建立和放置資料樣本(seeds)到資料庫的類。Laravel提供了一個簡單的技術,在database/seeds目錄下使用seed類來填充你的資料庫中的測試資料。

你可以自由選擇你的seed類的名字. 但我們建議你遵循一個明確的命名模式,如UsersTableSeeder。之後,一個 DatabaseSeeder 類會預設為你建立。

下面是一個Laravel中的資料庫seed類的例子:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class DatabaseSeeder extends Seeder {
public function run() {
$this->call('UserTableSeeder');
$this->command->info('User table seeded!');
}
}
class UserTableSeeder extends Seeder {
public function run() {
DB::table('users')->delete();
User::create(array('email' => 'foo@bar.com'));
}
}
class DatabaseSeeder extends Seeder { public function run() { $this->call('UserTableSeeder'); $this->command->info('User table seeded!'); } } class UserTableSeeder extends Seeder { public function run() { DB::table('users')->delete(); User::create(array('email' => 'foo@bar.com')); } }
class DatabaseSeeder extends Seeder {
public function run() {
$this->call('UserTableSeeder');
$this->command->info('User table seeded!');
}
}
class UserTableSeeder extends Seeder {
public function run() {
DB::table('users')->delete();
User::create(array('email' => 'foo@bar.com'));
}
}

建立資料填充器

產生資料填充器就像派一樣簡單。你可以閉著眼睛做這件事(但請不要這樣做)。

執行 make:seeder artisan命令來建立一個資料填充器。現在database/seeds將包括所有由框架產生的資料填充器:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
php artisan make:seeder UsersTableSeeder
php artisan make:seeder UsersTableSeeder
php artisan make:seeder UsersTableSeeder

資料填充器類的預設方法是執行。這個過程是在你應用 db:seed artisan命令時發生的。你可以使用執行函式以任何你喜歡的方式將資料放入你的資料庫。此外,你完全可以使用Eloquent模型工廠或Query Builder來手動插入資料。

無論如何,你應該記住,在資料庫填充期間,大規模分配保護會自動停用。

在這裡,我們將對基本的 DatabaseSeeder 類進行修改,並在執行方法中新增一個資料庫插入語句:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<?php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class DatabaseSeeder extends Seeder {
/**
* Run the database seeds.
*
* @return void
*/
public function run() {
DB::table('users')->insert([
'name' => Str::random(10),
'email' => Str::random(10).'@gmail.com',
'password' => Hash::make('password'),
]);
}
}
<?php use Illuminate\Database\Seeder; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { DB::table('users')->insert([ 'name' => Str::random(10), 'email' => Str::random(10).'@gmail.com', 'password' => Hash::make('password'), ]); } }
<?php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class DatabaseSeeder extends Seeder {
/**
* Run the database seeds.
*
* @return void
*/
public function run() {
DB::table('users')->insert([
'name' => Str::random(10),
'email' => Str::random(10).'@gmail.com',
'password' => Hash::make('password'),
]);
}
}

如果你想在執行方法的程式碼中輸入提示任何依賴性,Laravel服務容器將自動解決它們。

此外, 你可以使用 call 函式從這個類中執行不同的seed類, 允許你自定義播種順序. 你可以將你的資料庫資料填充分割到不同的檔案中, 確保沒有一個seeder類會過度膨脹.

輸入你要使用的seeder類的名稱,如下圖所示:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
/**
* Run the database seeds.
*
* @return void
*/
public function run() {
$this->call([
UsersTableSeeder::class,
PostsTableSeeder::class,
CommentsTableSeeder::class,
]);
}
/** * Run the database seeds. * * @return void */ public function run() { $this->call([ UsersTableSeeder::class, PostsTableSeeder::class, CommentsTableSeeder::class, ]); }
/**
* Run the database seeds.
*
* @return void
*/
public function run() {
$this->call([
UsersTableSeeder::class,
PostsTableSeeder::class,
CommentsTableSeeder::class,
]);
}

執行資料填充器

生成資料填充器後,你可能需要使用 dump-autoload 命令來重新建立Composer的自動載入器:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
composer dump-autoload
composer dump-autoload
composer dump-autoload

接下來,你需要執行 db:seed artisan 命令,為你的資料庫播種:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
php artisan db:seed
php artisan db:seed
php artisan db:seed

這個命令通過代理來執行 DatabaseSeeder 類,它可以用來執行其他seed類。然而,你可以使用 --class 引數來單獨執行一個特定的seed機類,如下所示:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
php artisan db:seed --class=UserTableSeeder
php artisan db:seed --class=UserTableSeeder
php artisan db:seed --class=UserTableSeeder

如果你想從頭開始重建你的資料庫,包括刪除所有的表並重新執行所有的遷移,該怎麼辦?在這種情況下,請使用 migrate:fresh 命令為你的資料庫填充資料。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
php artisan migrate:fresh --seed
php artisan migrate:fresh --seed
php artisan migrate:fresh --seed

與遷移的情況一樣,一些播種過程可能會導致資料丟失或不必要的改變。出於這個原因,在執行資料填充器之前,會提示你批准,以保護你不在主資料庫上執行資料填充命令。

如果你有足夠的信心,並且不希望被這個安全步驟打斷,可以使用下面的 --force 標誌:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
php artisan db:seed --force
php artisan db:seed --force
php artisan db:seed --force

在Laravel中使用原始資料庫查詢的另外5種方法

雖然Laravel提供了方便的工具,如Eloquent和Query Builder,你仍然可以使用SQL進行原始查詢。我們總結了五種不同的方法來實現。

但是在你開始之前, 你應該知道原始查詢並不是自動安全的, 這使得它們成為一種危險的方法. 因此,如果你要給查詢提供任何引數,請確保它們是正確的格式,並且有正確的值,例如數字而不是文字。

平均值/總和/計數的計算

如果你想建立 GROUP BY() ,你可以使用原始查詢,然後利用MySQL的聚合函式,如 Count()SUM()AVG()MIN()MAX() ,如下例所示:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$users = DB::table('users')
->selectRaw('count(*) as user_count, status')
->where('status', '<>', 1)
->groupBy('status')
->get();
$users = DB::table('users') ->selectRaw('count(*) as user_count, status') ->where('status', '<>', 1) ->groupBy('status') ->get();
$users = DB::table('users')
->selectRaw('count(*) as user_count, status')
->where('status', '<>', 1)
->groupBy('status')
->get();

甚至可以在同一個SQL查詢中同時進行 count()avg()

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$salaries = DB::table('salaries')
->selectRaw('companies.name as company_name, avg(salary) as avg_salary, count(*) as people_count')
->join('companies', 'salaries.company_id', '=', 'companies.id')
->groupBy('companies.id')
->orderByDesc('avg_salary')
->get();
$salaries = DB::table('salaries') ->selectRaw('companies.name as company_name, avg(salary) as avg_salary, count(*) as people_count') ->join('companies', 'salaries.company_id', '=', 'companies.id') ->groupBy('companies.id') ->orderByDesc('avg_salary') ->get();
$salaries = DB::table('salaries')
->selectRaw('companies.name as company_name, avg(salary) as avg_salary, count(*) as people_count')
->join('companies', 'salaries.company_id', '=', 'companies.id')
->groupBy('companies.id')
->orderByDesc('avg_salary')
->get();

篩選年份

如果你需要在 GROUP BYORDER BY 中進行SQL計算,你可以使用 groupByRaw()orderByRaw() 查詢。分組後,你也可以通過使用 having 帶有 havingRaw() 的SQL查詢來利用 where 語句。

例如,下面這個命令顯示瞭如何按年份對一個日期/時間欄位進行分組:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$results = User::selectRaw('YEAR(birth_date) as year, COUNT(id) as amount')
->groupByRaw('YEAR(birth_date)')
->havingRaw('YEAR(birth_date) > 2000')
->orderByRaw('YEAR(birth_date)')
->get();
$results = User::selectRaw('YEAR(birth_date) as year, COUNT(id) as amount') ->groupByRaw('YEAR(birth_date)') ->havingRaw('YEAR(birth_date) > 2000') ->orderByRaw('YEAR(birth_date)') ->get();
$results = User::selectRaw('YEAR(birth_date) as year, COUNT(id) as amount')
->groupByRaw('YEAR(birth_date)')
->havingRaw('YEAR(birth_date) > 2000')
->orderByRaw('YEAR(birth_date)')
->get();

計算單個欄位(子查詢)

假設你想從另一列計算出一個列,並在SQL查詢中返回結果。你怎樣才能做到這一點呢?

讓我們來看看:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$products = Product::select('id', 'name')
->selectRaw('price - discount_price AS discount')
->get();
$products = Product::select('id', 'name') ->selectRaw('price - discount_price AS discount') ->get();
$products = Product::select('id', 'name')
->selectRaw('price - discount_price AS discount')
->get();

下面是另一個SQL CASE 語句的例子:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$users = DB::table('users')
->select('name', 'surname')
->selectRaw("(CASE WHEN (gender = 1) THEN 'M' ELSE 'F' END) as gender_text")
->get();
$users = DB::table('users') ->select('name', 'surname') ->selectRaw("(CASE WHEN (gender = 1) THEN 'M' ELSE 'F' END) as gender_text") ->get();
$users = DB::table('users')
->select('name', 'surname')
->selectRaw("(CASE WHEN (gender = 1) THEN 'M' ELSE 'F' END) as gender_text")
->get();

轉換舊的SQL

有一個SQL語句需要轉換為Eloquent或Query Builder,這是一種常見的情況,特別是來自你所做的一個老專案。

那麼,你真的不需要這麼做。相反,你可以簡單地使用 DB::select() 語句,如圖所示:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$results = DB::select('select * from users where id=?', [1]);
$results = DB::select('select * from users where id=?', [1]);
$results = DB::select('select * from users where id=?', [1]);

執行沒有結果的查詢

DB::statement 可以執行一個SQL查詢,得到沒有變數的 INSERTUPDATE 等結果。

這在資料庫遷移中經常使用,當一個表的結構發生變化,舊的資料必須用新的資料來改變:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
DB::statement('UPDATE users SET role_id = 1 WHERE role_id IS NULL AND YEAR(created_at) > 2020');
DB::statement('UPDATE users SET role_id = 1 WHERE role_id IS NULL AND YEAR(created_at) > 2020');
DB::statement('UPDATE users SET role_id = 1 WHERE role_id IS NULL AND YEAR(created_at) > 2020');

此外, DB::statement() 可以執行任何帶有模式的SQL查詢,不限於數值或列。這裡有一個例子:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
DB::statement('DROP TABLE users');
DB::statement('ALTER TABLE projects AUTO_INCREMENT=123');
DB::statement('DROP TABLE users'); DB::statement('ALTER TABLE projects AUTO_INCREMENT=123');
DB::statement('DROP TABLE users');
DB::statement('ALTER TABLE projects AUTO_INCREMENT=123');

小結

到現在,你應該對Laravel中的資料庫事務有了深刻的理解,以及如何實現它們。它們不僅有助於資料的完整性,而且還有助於優化Laravel的效能,使你的開發過程更容易。

評論留言