本文分析一個基於 Zend Framework 風格的 MongoDB PHP 封裝類別:Hrs_Mongo_Config 管理連線與資料庫選擇,Hrs_Mongo_Table 提供 CRUD 與三種查詢介面 (find/look/getCursor),詳細說明遊標物件、陣列轉換、連線狀態檢查機制。
<?php
class Hrs_Mongo_Config {
const DEFAULT_HOST = 'localhost';
const DEFAULT_PORT = 27017;
private static $host = self::DEFAULT_HOST;
private static $port = self::DEFAULT_PORT;
private static $options = [
'connect' => true,
'timeout' => 30,
// 'replicaSet' => ''
];
public static $conn = null;
public static $defaultDb = null;
public static $linkStatus = '';
public static function set($server = 'mongodb://localhost:27017', $options = ['connect' => true]) {
// 解析 $server (字串或陣列),建構連線 URI
// 支援認證:mongodb://user:pass@host:port
$url = ...;
if (is_array($options)) {
foreach (self::$options as $k => $v) {
if (isset($options[$k])) self::$options[$k] = $options[$k];
}
}
try {
self::$conn = new Mongo($url, self::$options); // 舊版 Mongo 驅動
self::$linkStatus = 'success';
} catch (Exception $e) {
self::$linkStatus = 'failed';
}
if (isset($server['database'])) {
self::selectDB($server['database']);
}
}
public static function selectDB($database) {
if (!$database) throw new Zend_Exception('Database name required');
if (self::$linkStatus !== 'success') throw new Zend_Exception('Connection failed');
self::$defaultDb = self::$conn->selectDB($database);
return self::$defaultDb;
}
}
<?php
abstract class Hrs_Mongo_Table {
protected $_db = ''; // MongoCollection 物件
protected $_name = ''; // 集合名稱
protected $_data = []; // find() 結果暫存
protected $c_options = ['fsync'=>true, 'safe'=>true];
protected $u_options = ['multiple'=>true, 'fsync'=>true, 'safe'=>true];
protected $d_options = ['fsync'=>true, 'justOne'=>false, 'safe'=>true];
public function __construct() {
if (Hrs_Mongo_Config::$conn instanceof Mongo) {
$this->_db = Hrs_Mongo_Config::$defaultDb->{$this->_name};
} else {
throw new Zend_Exception('Mongo connection failed');
}
}
// 基本 CRUD
public function insert($data) { return $this->_db->insert($data, $this->c_options); }
public function update($data, $where) { return $this->_db->update($where, $data, $this->u_options); }
public function delete($where) { return $this->_db->remove($where, $this->d_options); }
public function dropMe() { return $this->_db->drop(); }
// 查詢模式一:find() 回傳 $this,結果存 $_data,稍後 toArray()
public function find($where = [], $limit = 0) {
if ($this->testLink()) {
$cursor = $where ? $this->_db->find($where) : $this->_db->find();
if ($limit > 0) $cursor->limit($limit);
$cursor->snapshot(); // 防止重複/遺漏
$this->_data = $cursor;
}
return $this; // 鏈式呼叫
}
// 查詢模式二:look() 直接回傳 MongoCursor 遊標物件
public function look($where = [], $fields = []) {
if (!$this->testLink()) return false;
if ($fields) {
return $where
? $this->_db->find($where, $fields)
: $this->_db->find()->fields($fields);
}
return $where ? $this->_db->find($where) : $this->_db->find();
}
// 取得 find() 存儲的遊標
public function getCursor() {
return $this->_data;
}
// 結果轉換
public function toArray() {
$result = [];
foreach ($this->_data as $id => $row) {
$row['_id'] = $id;
$result[] = $row;
}
return $result;
}
public function __toString() {
return (string)$this->_data;
}
protected function testLink() {
return Hrs_Mongo_Config::$linkStatus === 'success';
}
}
| 方法 | 回傳值 | 結果儲存 | 適用場景 | 記憶體 |
|---|---|---|---|---|
find() | $this (鏈式) | 存入 $_data (MongoCursor) | 後續呼叫 toArray() 取得完整陣列 | 一次性載入全部 |
look() | MongoCursor | 不儲存,直接回傳 | 需自行迭代、大量資料分批處理 | 逐筆迭代,省記憶體 |
getCursor() | MongoCursor | 從 $_data 取出 | find() 後需直接操作遊標 | 同上 |
<?php
// 定義具體 Table
class UserTable extends Hrs_Mongo_Table {
protected $_name = 'users';
}
// 初始化連線
Hrs_Mongo_Config::set([
'host' => 'localhost',
'port' => 27017,
'database' => 'myapp',
'user' => 'admin',
'pass' => 'secret'
]);
$userTable = new UserTable();
// 模式一:find() + toArray() (小資料集)
$users = $userTable->find(['status' => 1], 20)->toArray();
foreach ($users as $u) { echo $u['name'] . "\n"; }
// 模式二:look() 直接迭代遊標 (大資料集)
$cursor = $userTable->look(['status' => 1], ['name' => 1, 'email' => 1]);
foreach ($cursor as $doc) {
// 逐筆處理,不佔用大量記憶體
process_user($doc);
}
// 模式三:find() 後 getCursor() 取得遊標
$userTable->find(['status' => 1]);
$cursor = $userTable->getCursor();
$cursor->sort(['created_at' => -1]);
foreach ($cursor as $doc) { ... }
Mongo 類別 (PECL mongo),現代專案請改用 MongoDB\Driver\Manager (mongodb 擴充套件) 或官方 mongodb/mongodb 程式庫。look($where, ['name'=>1]) 僅取回需要欄位,大幅減少網路傳輸。toArray() 會將 _id 併入陣列,方便後續操作。Hrs_Mongo_Config::$conn 為靜態單例,全程複用同一連線。<?php
require 'vendor/autoload.php';
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->myapp->users;
// 等同 find() + toArray()
$users = $collection->find(['status' => 1], ['limit' => 20])->toArray();
// 等同 look() 迭代
$cursor = $collection->find(['status' => 1], ['projection' => ['name' => 1, 'email' => 1]]);
foreach ($cursor as $doc) { ... }
// 遊標方法豐富:sort, limit, skip, batchSize, hint, explain 等