MongoDB 是文件導向的 NoSQL 資料庫,PHP 透過 mongodb 擴充套件(舊版 mongo 已棄用)進行操作。本文整理 連線參數、資料庫/集合選擇、CRUD 操作、查詢投影、索引建立、聚合管線、錯誤處理 等核心用法,並提供現代化的類別封裝範例。
# 安裝 PHP MongoDB 擴充套件
pecl install mongodb
# composer 專案相依
composer require mongodb/mongodb
| 連線字串格式 | 說明 |
|---|---|
mongodb://localhost:27017 | 本機預設連線 |
mongodb://user:pass@localhost:27017 | 帳密驗證 |
mongodb://user:pass@localhost:27017/dbname | 指定預設驗證資料庫 |
mongodb://host1:27017,host2:27017/?replicaSet=rs0 | 副本集連線 |
mongodb+srv://cluster.mongodb.net | Atlas SRV 記錄自動發現 |
<?php
require 'vendor/autoload.php';
use MongoDB\Client;
use MongoDB\Collection;
use MongoDB\Database;
// 1. 建立連線
$client = new Client('mongodb://localhost:27017');
// 2. 選擇資料庫與集合
$db = $client->selectDatabase('myapp'); // 明確選擇
$coll = $db->selectCollection('users'); // 明確選擇
// ---------- Create ----------
// 插入單筆
$insertOne = $coll->insertOne([
'name' => 'Alice',
'email' => 'alice@example.com',
'age' => 28,
'tags' => ['vip', 'beta'],
'created_at' => new MongoDB\BSON\UTCDateTime(), // 當前毫秒時間戳
]);
echo "Inserted ID: {$insertOne->getInsertedId()}\n";
// 批次插入
$insertMany = $coll->insertMany([
['name' => 'Bob', 'age' => 30],
['name' => 'Carol', 'age' => 25],
]);
echo "Inserted IDs: " . implode(', ', iterator_to_array($insertMany->getInsertedIds())) . "\n";
// ---------- Read ----------
// 查單筆(依 _id)
$user = $coll->findOne(['_id' => $insertOne->getInsertedId()]);
print_r($user);
// 條件查詢 + 投影 + 排序 + 分頁
$cursor = $coll->find(
['age' => ['$gte' => 25]], // filter
[
'projection' => ['name' => 1, 'email' => 1, '_id' => 0], // 只回傳需欄位
'sort' => ['age' => -1], // 年齡降序
'skip' => 0, // 分頁 offset
'limit' => 10, // 分頁 limit
]
);
foreach ($cursor as $doc) {
echo "{$doc['name']} ({$doc['email']})\n";
}
// 計算總數
$total = $coll->countDocuments(['age' => ['$gte' => 25]]);
// ---------- Update ----------
// 單筆更新($set 部分更新)
$updateOne = $coll->updateOne(
['email' => 'alice@example.com'], // filter
['$set' => ['age' => 29, 'last_login' => new MongoDB\BSON\UTCDateTime()]]
);
echo "Matched: {$updateOne->getMatchedCount()}, Modified: {$updateOne->getModifiedCount()}\n";
// 多筆更新
$updateMany = $coll->updateMany(
['tags' => 'beta'],
['$set' => ['status' => 'active'], '$unset' => ['temp' => '']]
);
// ---------- Delete ----------
$deleteOne = $coll->deleteOne(['email' => 'bob@example.com']);
$deleteMany = $coll->deleteMany(['status' => 'inactive']);
// ---------- Index ----------
// 建立單欄位索引
$coll->createIndex(['email' => 1], ['unique' => true]);
// 複合索引
$coll->createIndex(['age' => 1, 'status' => 1]);
// 文字索引(全文搜尋)
$coll->createIndex(['name' => 'text', 'email' => 'text']);
// ---------- Aggregation ----------
$pipeline = [
['$match' => ['age' => ['$gte' => 18]]],
['$group' => ['_id' => '$status', 'count' => ['$sum' => 1], 'avgAge' => ['$avg' => '$age']]],
['$sort' => ['count' => -1]],
];
$results = $coll->aggregate($pipeline);
foreach ($results as $row) {
print_r($row);
}
<?php
namespace App\Repository;
use MongoDB\Collection;
use MongoDB\Model\BSONDocument;
class UserRepository {
private Collection $coll;
public function __construct(Collection $coll) {
$this->coll = $coll;
}
public function findByEmail(string $email): ?array {
return $this->coll->findOne(['email' => $email]);
}
public function create(array $data): string {
$data['created_at'] = new \MongoDB\BSON\UTCDateTime();
$result = $this->coll->insertOne($data);
return (string)$result->getInsertedId();
}
public function updateById(string $id, array $data): bool {
$result = $this->coll->updateOne(
['_id' => new \MongoDB\BSON\ObjectId($id)],
['$set' => $data]
);
return $result->getModifiedCount() > 0;
}
public function paginate(array $filter, int $page, int $perPage): array {
$cursor = $this->coll->find($filter, [
'skip' => ($page - 1) * $perPage,
'limit' => $perPage,
'sort' => ['created_at' => -1],
]);
return [
'data' => iterator_to_array($cursor),
'total' => $this->coll->countDocuments($filter),
'page' => $page,
'per_page' => $perPage,
];
}
}
| 問題 | 原因 | 解法 |
|---|---|---|
MongoDB\BSON\ObjectId 無法直接 echo | 是物件非字串 | (string)$objectId 或 $objectId->__toString() |
| 日期儲存為字串而非 BSON Date | 直接存 date('Y-m-d') | 用 new MongoDB\BSON\UTCDateTime() 或 new DateTime() 物件 |
大量資料 find() 記憶體爆炸 | 游標預設在 PHP 端緩衝 | 使用 batchSize() 或分批 limit/skip |
聚合管線 $lookup 效能差 | 缺乏索引或資料量過大 | 建立外鍵欄位索引、考慮反正規化設計 |
PHP 操作 MongoDB 的關鍵在於:使用官方 mongodb/mongodb 套件、善用 BSON 類型(ObjectId, UTCDateTime)、建立適當索引、聚合管線替代複雜查詢。建議專案引入 Repository 模式封裝集合操作,並配合 PHPUnit 測試資料存取層,確保 NoSQL 彈性結構下的資料一致性。