前情提要
半年前,我在 Astrocamp 學習三個月的 Rails 開發,
結束後,誤打誤撞進入了以 Laravel 來開發的工作!
一開始的確有點擔心自己會不會吃不消,但慢慢發現這兩者在開發上有著很相似的味道,
慢慢記錄使用上的小小心得,或許之後都能用得上也說不定!
概念理解
在開發框架中,以下幾個名詞都是很重要的概念:
MVC(Model-View-Controller)
MVC 是一種常見的軟體架構模式,將應用程式分為三個核心部分:
模型(Model)、視圖(View)和控制器(Controller)在 Rails 和 Laravel 中,這種架構都被廣泛採用。
- 模型(Model)負責處理應用程式的資料邏輯,
- 視圖(View)負責呈現使用者介面,
- 控制器(Controller)則是連接模型和視圖,並處理用戶的輸入。
透過 MVC 架構,開發者可以更清晰地組織和管理程式碼,實現代碼的重用性和可維護性。
RESTful 設計
RESTful 是一種設計風格,用於建立有效的、可擴展的 Web API。
基於 REST(Representational State Transfer)原則,強調使用統一資源標識符(Uniform Resource Identifiers,URI)來操縱資源。
透過 RESTful 設計,開發者可以定義清晰的路由結構、合理的 HTTP 方法使用和良好的資源命名,以實現易於維護和擴展的 API。比方說,假設你在經營一家電子商務網站,希望用戶能夠通過 API 存取你的產品資訊。
使用 RESTful 設計,可以通過像/products
這樣的 URI 來獲取所有產品資訊,配上合適的 HTTP 方法(例如 GET、POST、PUT、DELETE)來執行獲取、新增、更新或刪除產品。Migration
Migration 是一種用於管理資料庫版本控制的機制,通過程式化的方式定義和更新資料庫結構。
在 Rails 和 Laravel 中,Migration 具有相似的概念和實現方式。開發者可以通過撰寫 Migration 檔案來定義資料庫結構的變化,然後使用框架提供的指令來執行 Migration,實現資料庫結構的異動。
Ruby on Rails
語言:Ruby / 作者: David Heinemeier Hansson
官方文件:Rails Guides
慣例優於設定(Convention over Configuration, CoC)
Rails 強調慣例優於設定原則,提供預設的設定和工作流程,使得開發更加快速和一致。Model 使用單數形式,例如
Article
代表一個文章模型;
Controller 使用複數形式,並以”Controller”結尾,例如ArticlesController
。不要做重複的事(Don’t Repeat Yourself, DRY)
把重複的部份抓出來,整理為一個方法、類別或模組。指令介面
使用 rails command line 來執行各種開發。
可以在終端機透過
rails --help
,就可以看見所有可以用的 rails 指令:
套件與套件管理
使用 Bundler 作為套件管理器,可以通過 Bundler 輕鬆管理 Gem 依賴關係。
Gem(是一個可以下載並安裝的 package。可以在開發上新增額外的功能。)
Ex: Devise 提供完整的用戶註冊、登錄、忘記密碼等功能,直接快速完成會員系統。bundle install
用於安裝所有必需的 gem 套件,包括指定版本的 gem 和其相依性。Bundler 會根據
Gemfile
文件中定義的依賴關係來安裝套件。
安裝的 gem 預設會放在vendor/bundle
目錄下,並生成Gemfile.lock
文件以確保後續安裝的 gem 版本與當前一致。bundle update
如果要新增套件或更新,只需在
Gemfile
中添加套件的名稱和版本約束,並使用bundle update
。Object–relational mapping (ORM) 物件關聯映射
在 Rails 名為 Active Record 提供方便的資料庫操作方法,
使用 Ruby 與資料庫交互更加簡單和直觀。
實際以 code 來看 Active Record
建立一個 User 的資料表,其中包含 id、name 和 email 欄位,
並使用 Active Record 對 User 資料進行操作:
在終端機使用 rails 指令,建立 User Model
rails generate model User name:string email:string
可以簡寫成:rails g model User name email
(*如果型別是string
可以省略不寫)id 是由 Rails 自動生成的欄位用於唯一識別每條記錄,也就是主鍵(primary key)。這個值是自動增長的,也就是每次新增記錄時都會自動增加。
指令輸入之後會生成與 User 相關的所有必要檔案,
包括 migration, model, test:
db/migrate/20240319034136_create_users.rb
1
2
3
4
5
6
7
8
9
10class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps
end
end
endapp/models/user.rb
1
2class User < ApplicationRecord
end
在繼續之前記得要跑一次,rails db:migrate
,
這會執行所有尚未運行的資料庫遷移,並確保資料庫結構是最新的。
如何確認是否有沒有運行的 migration?
rails db:migrate:status
1 | Status Migration ID Migration Name |
*狀態是 “up”,表示已執行,”down”,表示尚未被執行。
接下來,以 rails console
也就是進入 Rails 的控制台,
在裡頭我們可以直接操作資料庫並使用 Active Record 來進行一系列查詢、新增、修改、刪除。
rails console
正式進入 rails console 裡:
一但進入,就可以使用 Active Record 來與資料庫進行交互,以下為基本的操作方式:
建立一個新的 user
User.create(name: "Zinni Chang", email: "[email protected]")
1
2
3
4
5
6
7
8
9:001 > User.create(name: "Zinni Chang", email: "[email protected]")
TRANSACTION (0.0ms) begin transaction
User Create (0.5ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)
[["name", "Zinni Chang"], ["email", "[email protected]"],
["created_at", "2024-03-19 12:49:29.883029"], ["updated_at", "2024-03-19 12:49:29.883029"]]
TRANSACTION (0.8ms) commit transaction
=>
#<User:0x00000001097b2258
...
Rails 會啟動一個 TRANSACTION,並將我們的 create 動作放入這個 TRANSACTION 中,接著,Rails 執行 INSERT SQL 語句,將新的 user 插入到 users 資料表中,並將名稱、電子郵件地址以及建立時間和更新時間寫入資料庫。最後,TRANSACTION 成功!(也就是 commit transaction)。
查詢 user
在 users 資料表的所有 user
User.all
1
2
3
4
5
6
7
8
9:002 > User.all
User Load (16.8ms) SELECT "users".* FROM "users"
=>
[#<User:0x000000010a57b6a0
id: 1,
name: "Zinni Chang",
email: "[email protected]",
created_at: Fri, 19 Mar 2024 12:49:29.883029000 UTC +00:00,
updated_at: Fri, 19 Mar 2024 12:49:29.883029000 UTC +00:00>]查詢特定的 user
find(id)
根據主鍵 (id) 來查找單一記錄。
User.find(1)
1
2
3
4
5
6
7
8
9:003 > User.find(1)
User Load (4.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=>
#<User:0x000000010a5758e0
id: 1,
name: "Zinni Chang",
email: "[email protected]",
created_at: Fri, 19 Mar 2024 12:49:29.883029000 UTC +00:00,
updated_at: Fri, 19 Mar 2024 12:49:29.883029000 UTC +00:00>find_by()
根據指定的條件查找第一個匹配的記錄,這裡用欄位是 name 來尋找
User.find_by(name: "Zinni Chang")
1
2
3
4
5
6
7
8
9:004 > User.find_by(name: "Zinni Chang")
User Load (13.5ms) SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", "Zinni Chang"], ["LIMIT", 1]]
=>
#<User:0x000000010a3b7e40
id: 1,
name: "Zinni Chang",
email: "[email protected]",
created_at: Fri, 19 Mar 2024 12:49:29.883029000 UTC +00:00,
updated_at: Fri, 19 Mar 2024 12:49:29.883029000 UTC +00:00>
更新 user
User.find(id).update(email: "[email protected]")
1 | :005 > User.find(1).update(email: "[email protected]") |
刪除 user
User.destroy(id)
1 | :006 > User.destroy(1) |
Laravel
語言:PHP / 作者:Taylor Otwell
官方文件:Laravel
Laravel 相較 Rails 是一個較為年輕的框架,但他在短時間內迅速崛起,
成為 PHP 社群中最受歡迎的框架之一。
在看了一些文章與自己使用上,不難發現 Laravel 確實有 Rails 的感覺。
與 Rails 不同,Laravel 在開發上沒有強制性的命名規則或預設設置,
這使得我們可以更靈活地運用框架,根據自己的需求和喜好進行配置和開發。指令介面
使用 artisan command line 來執行各種開發。
可以在終端機透過
php artisan --help
,可以了解 Artisan 全面的說明與可以用的指令:
而 php artisan list 提供所有指令列表:
套件與套件管理
使用 Composer 作為套件管理器,可以通過 Composer 安裝、更新和管理依賴關係和第三方套件。
composer install
這指令使 Composer 將根據
composer.json
中定義的依賴關係,安裝所有必需的套件,包括指定版本的套件和其相依性;
Composer 會將所有的套件下載並安裝到vendor
目錄下,並生成composer.lock
文件以確保後續安裝的套件版本與當前一致。composer update
如果要新增套件或更新,只需在
composer.json
中添加套件的名稱和版本約束,並使用composer update
。Object–relational mapping (ORM) 物件關聯映射
在 Laravel 名為 Eloquent 提供方便的資料庫操作方法,
使用 PHP 與資料庫交互更加簡單和直觀。
實際以 code 來看 Eloquent
建立一個 User 的資料表,其中包含 id、name 和 email 欄位,
並使用 Eloquent 對 User 資料進行操作:
在終端機使用 artisan 指令,建立 User Model
php artisan make:model User -m
這指令會產生 Model 與 migrationapp/Models/User.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* 資料表名稱
*
* @var string
*/
protected $table = 'users';
/**
* 可以賦值的欄位
*
* @var array
*/
protected $fillable = [
'name', 'email',
];
}database/migrations/2024_03_19_163819_create_users_table.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('users');
}
}
接下來,以 php artisan tinker
,
在裡頭我們可以直接操作資料庫並使用 Eloquent 來進行一系列查詢、新增、修改、刪除。
在這之前記得要跑一次,php artisan migrate
,
這會執行所有尚未運行的資料庫遷移,並確保資料庫結構是最新的。
如何確認是否有沒有運行的 migration?
php artisan migrate:status
1
2
3
4
5
6
7 +------+-----------------------------------------------------------+-------+
| Ran? | Migration | Batch |
+------+-----------------------------------------------------------+-------+
| Yes | 2024_03_10_000000_create_users_table | 1 |
| Yes | 2024_03_12_000000_create_posts_table | 1 |
| No | 2024_03_19_000000_add_column_to_users_table | |
+------+-----------------------------------------------------------+-------+
*狀態是 “Yes”,表示已執行,”No”,表示尚未被執行。
php artisan tinker
正式進入 Tinker 裡:
一但進入,就可以使用 Eloquent 來與資料庫進行交互,以下為基本的操作方式:
建立一個新的 user
App\Models\User::create(['name' => 'Zinni Chang', 'email' => '[email protected]']);
也可以簡寫:
User::create(['name' => 'Zinni Chang', 'email' => '[email protected]']);
1
2
3
4
5
6
7
8> App\Models\User::create(['name' => 'Zinni Chang', 'email' => '[email protected]']);
= App\Models\User {#6960
name: "Zinni Chang",
email: "[email protected]",
updated_at: "2024-03-19 16:51:28",
created_at: "2024-03-19 16:51:28",
id: 1,
}
查詢 user
在 users 資料表的所有 user
User::all();
1
2
3
4
5
6
7
8
9
10
11
12
13> User::all();
= Illuminate\Database\Eloquent\Collection {#7288
all: [
App\Models\User {#7287
id: 1,
name: "Zinni Chang",
email: "[email protected]",
email_verified_at: null,
created_at: "2024-03-19 16:51:28",
updated_at: "2024-03-19 16:51:28",
},
],
}查詢特定的 user
find(id)
根據主鍵 (id) 來查找單一記錄。
User::find(1);
1
2
3
4
5
6
7
8
9> User::find(1);
= App\Models\User {#7216
id: 1,
name: "Zinni Chang",
email: "[email protected]",
email_verified_at: null,
created_at: "2024-03-19 16:51:28",
updated_at: "2024-03-19 16:51:28",
}where()
根據指定的條件查找第一個匹配的記錄,這裡用欄位是 name 來尋找
User::where('name', 'Zinni Chang')->first();
1
2
3
4
5
6
7
8
9> User::where('name', 'Zinni Chang')->first();
= App\Models\User {#7070
id: 1,
name: "Zinni Chang",
email: "[email protected]",
email_verified_at: null,
created_at: "2024-03-19 16:51:28",
updated_at: "2024-03-19 16:51:28",
}
更新 user
User::find(1)->update(['email' => '[email protected]'])
1 | > User::find(1)->update(['email' => '[email protected]']) |
刪除 user
User::find(3)->delete()
1 | > User::find(3)->delete() |
資料庫關聯
資料庫關聯是指資料庫中不同表格之間的關聯。常見的關聯包括一對一、一對多和多對多。
在 Rails 和 Laravel,都可以通過定義模型之間的關係來實現資料庫關聯,也就可以輕鬆地在應用程式中操作相關聯的資料,並利用框架提供的方法來簡化資料庫查詢和操作。
在 Rails 中,假設我們有兩個模型:
User
和Post
,
他們之間是一對多的關係,也就是一個使用者可以擁有多篇文章。可以這樣定義:
1
2
3
4
5
6
7
8
9# app/models/user.rb
class User < ApplicationRecord
has_many :posts
end
# app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
end在 Laravel 中,也可以用類似的方式來定義這個關係。
假設同樣有User
和Post
兩個模型,可以這樣寫:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// app/Models/User.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
這樣一來,在實際應用中,我們可以使用這些關係方法來輕鬆地查詢和操作相關聯的資料,
而不需要手動撰寫複雜的 SQL 查詢。
在 Rails 中:
1 | # 建立一個使用者 |
在 Laravel 中:
1 | // 建立一個使用者 |
Active Record 回調 與 Eloquent 事件
執行 Eloquent 事件(Laravel)和 Active Record 回調(Rails)都是在操作資料庫時執行額外邏輯的機制。
這兩種機制都允許在模型生命週期中的特定事件觸發時執行自定義的程式碼。
差異
細分程度:
- Rails 提供了更細緻的回調級別,例如驗證前後、保存前後、建立前後、更新前後、刪除前後,這使得可以在更多細節層面上處理邏輯。
- Laravel 的 Eloquent 事件相對較簡單,僅提供了模型被建立、更新或刪除的基本事件。
用法差異:
- Rails 的 Active Record 回調是在模型類中定義的方法,這些方法會在模型的特定生命周期事件發生時自動被調用。
例如,在創建模型、更新模型或刪除模型時,可以定義相應的回調方法。 - Laravel 的 Eloquent 事件需要通過註冊監聽器(Listener)來執行額外的邏輯,例如發送郵件。
例如,如果要在建立新的 User 時發送歡迎郵件,可以這樣做:
1
2
3
4
5
6
7
8
9class User < ApplicationRecord
after_create :send_welcome_email
private
def send_welcome_email
# 發送歡迎郵件的邏輯
end
endafter_create
回調會在建立新的 User 之後自動調用send_welcome_email
方法。相比之下,在 Laravel 的 Eloquent 中,需要註冊事件監聽器來執行額外的邏輯:
首先,建立一個 Eloquent 監聽器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace App\Observers;
use App\Models\User;
class UserObserver
{
public function created(User $user)
{
// 寄送歡迎郵件的邏輯
Mail::to($user->email)->send(new WelcomeEmail($user));
}
}然後,在
boot
方法中註冊該監聽器:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Observers\UserObserver;
class User extends Model
{
protected static function boot()
{
parent::boot();
static::observe(UserObserver::class);
}
}當建立新的 User 時,Eloquent 將會自動調用
created
方法,從而觸發相應的事件處理邏輯,這與 Rails 中 Active Record 回調的作用相似。- Rails 的 Active Record 回調是在模型類中定義的方法,這些方法會在模型的特定生命周期事件發生時自動被調用。
透過註冊回調方法,您可以在記錄的不同階段執行自定義的邏輯,從而擴展和定制模型的行為。
無論是在 Laravel 中的 Eloquent 事件還是在 Rails 中的 Active Record 回調,都提供了在模型生命週期中執行額外程式碼的方法。這些機制可以在資料庫操作的不同階段執行自定義邏輯,從而實現更高級別的業務邏輯或應用程式行為。
Summary
在 Ruby on Rails 中,許多功能和工作流程已經預先定義好了,例如路由、資料庫映射、文件結構等,開發者只需遵循這些約定就可以開發。
這種自動化配置和約定優於配置的方法有助於提高開發者的生產力,使得開發過程更加流暢。
相比之下,Laravel 並沒有像 Ruby on Rails 那樣嚴格遵循約定優於配置的原則。
雖然 Laravel 提供許多方便的功能和工具,開發者需要更多地進行手動配置來定義應用程序的行為和結構。
例如,Laravel 的路由、資料庫映射和文件結構沒有像 Rails 那樣嚴格的命名和結構約定,開發者可以自由地根據自己的偏好和需求進行設置和組織。
因此,可以說 Laravel 更加靈活。
Rails 和 Laravel 都是不錯的 Web 開發框架,具有各自的特點和優勢。
選擇使用哪個框架取決於項目需求、開發者技能和偏好等因素。