August 10, 2024

Viiisit [Laravel] - MongoDB with Laravel

#laravel#mongodb

What is MongoDB?

MongoDB 是目前流行的 NoSQL 資料庫,是一個文檔導向(doc)的資料庫,不使用傳統的表格與行的模式,而是使用 JSON-like 的文檔(在 MongoDB 中稱為 BSON)來存儲資料。這使得數據結構可以非常靈活,能夠存儲更復雜的數據類型並輕鬆進行嵌套。MongoDB 廣泛用於需要處理大量數據並且數據頻繁變動的應用場景中。


要如何在 Laravel 專案中使用 MongoDB?

系統:安裝 MongoDB PHP Extension

1
sudo pecl install mongodb

並確認在 php.ini 裡有:

1
extension="mongodb.so"

Laravel 專案:安裝 mongodb/laravel-mongodb

1
composer require mongodb/laravel-mongodb

註:mongodb/laravel-mongodb 之前叫做 jenssegers/mongodb,因為所有權轉移給 MongoDB, Inc. 而更名為 mongodb/laravel-mongodb

config\database.php 設定 MongoDB 的連線
1
2
3
4
5
6
7
8
9
10
'default' => env('DB_CONNECTION', 'mongodb'),

...

'mongodb' => [
'driver' => 'mongodb',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', 27017),
'database' => env('DB_DATABASE', ''),
],
app.php 設定 providers
1
2
3
4
5
6
7
8
9
'providers' => [

/*
* Laravel Framework Service Providers...
*/

MongoDB\Laravel\MongoDBServiceProvider::class,

],

Laravel MongoDB CRUD example

以建立 Location Model 作為範例

建立一個 Location Model 並指定連接到 MongoDB:
1
php artisan make:model Location -mc

這指令會生成 App/Models/Location.phpApp/Http/Controllers/LocationController.php
預設上 artisan 指令會用 Illuminate\Database\Eloquent\Model

但要使用 MongoDB 我們需要用 MongoDB Eloquent model
也就是要將上面的預設修改為 MongoDB\Laravel\Eloquent\Model

建立 migration(optional)
1
php artisan make:migration create_locations_table

因爲 MongoDB 是一種 NoSQL 資料庫,不像關聯式資料庫 (MySQL) 使用較嚴謹的 migration,但在 MongoDB 使用 migration 可以知道資料的變更狀態,並保證不同開發環境間的一致性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use MongoDB\Laravel\Eloquent\Model;

class Location extends Model
{
protected $connection = 'mongodb';
protected $collection = 'locations';
protected $fillable = [
'country_code',
'party_id',
'id',
...
]

protected $casts = [
'publish' => 'boolean',
'charging_when_closed' => 'boolean',
'last_updated' => 'datetime',
];
}
筆記

Locationcoordinates 是一個包含經度和緯度的 array。為了方便處理,當時使用 Laravel 的 $casts 屬性將 coordinates 轉換成 array。

1
2
3
4
5
6
class Location extends Model
{
protected $casts = [
'coordinates' => 'array',
];
}

但,當開始使用 Laravel 的 API 資源(APIResource)封裝這些地理位置數據為 JSON 格式進行回應時,就遇到了困難。
雖然 coordinates 在 Model 內部正確地被處理為一個 PHP array,但當通過 APIResource 轉換為 JSON 時,這個 array 並沒有按照預期的方式顯示,因為 APIResource 基本上是將 Model 的數據直接轉換成 JSON,沒有對原始的數據類型做特別處理。

為了解決這個問題,而不是在 APIResource 中再次轉換為 JSON 字串,就是在 APIResource 中直接使用它的原生屬性,而不進行額外的處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
use Illuminate\Http\Resources\Json\JsonResource;

class LocationResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'coordinates' => $this->coordinates, // 直接使用,無需額外轉換
];
}
}

通過這種方式,確保了當 coordinates 被 APIResource 處理時,它保持為原生的格式,並且在最終的 JSON 回應中正確顯示。

CRUD

若 Model 內有 data 爲 id 和 MongoDB 的 _id 可能會衝突,推薦使用 where 方法來進行精確查詢:

1
2
$locations = Location::all(); // 獲取所有地點
$location = Location::where('id', $location_id)->first(); // 根據自定義 ID 查詢
1
2
3
$location = Location::where('id', $location_id)->first();
$location->name = '台北 101 大樓';
$location->save();

or

1
2
3
$location = Location::where('id', $location_id)->first();
$location->name = '台北 101 大樓';
$location->update();
1
2
$location = Location::where('id', $location_id)->first();
$location->delete();

參考資料:
mongodb/laravel-mongodb
MongoDB and Laravel Integration