MongoDB 提供了包括 PHP 在內的各種語言的驅動程式。為了簡化在 PHP 中建立聚合管道的過程,我們需要將所有階段和運算子建模為可以組合的函數。
聚合管道是「階段」文件的清單。我們將舉一個使用 進行查詢$match和連接的範例$lookup:
db.orders.aggregate([ { $match: { $or: [ { status: "shipped" }, { created_at: { $gte: ISODate("2023-01-01T00:00:00Z") } } ] } }, { $lookup: { from: "inventory", localField: "product_id", foreignField: "product_id", as: "inventory_docs" } } ])
每個帶有美元前綴的鍵都是我們要為其提供工廠方法的運算符。
命名空間函數
最明顯的解決方案是建立命名空間函數,例如:MongoDBOperatoreqof$eq運算子。
namespace MongoDB\Operator; function eq(mixed $value): array { return ['$eq' => $value]; } function lookup(string $from, string $localField, string $foreignField, string $as): array { return ['$lookup' => [ 'from' => $from, 'localField' => $localField, 'foreignField' => $foreignField, 'as' => $as, ]]; }
使用帶有命名參數的函數,管道將用 PHP 編寫:
pipeline( match( or( query(status: eq('shipped')), query(date: gte(new UTCDateTime())), ), ), lookup(from: 'inventory', localField: 'product_id', foreignField: 'product_id', as: 'inventory_docs'), );
但是,某些運算符名稱與PHP 中的保留關鍵字衝突。我們無法建立具有以下名稱的函數(全域或命名空間):
and,
or,
match,
unset,
set,
為函數名稱加上字尾
為了避免保留名稱的問題,我們可以在函數名稱中加上前綴或後綴。
以運算符類型作為後綴:
function andQuery(...) { /* ... */ } function matchStage(...) { /* ... */ }
帶下劃線:
function _and(...) { /* ... */ } function _match(...) { /* ... */ }
或使用表情符號。漂亮,但不實用:
function ?and(...) { /* ... */ } function ?match(...) { /* ... */ }
靜態類別方法
碰巧的是,方法名稱的保留關鍵字清單較短。我們可以在類別上建立靜態方法。
final class Stage { public static function lookup(...) { /* ... */ } public static function match(...) { /* ... */ } } final class Query { public static function and(...) { /* ... */ } public static function eq(...) { /* ... */ } }
字寫得有點長了,不過還是可讀的。
new Pipeline( Stage::match( Query::or( Query::query(status: Query::eq('shipped')), Query::query(date: Query::gte(new UTCDateTime())), ), ), Stage::lookup(from: 'inventory', localField: 'product_id', foreignField: 'product_id', as: 'inventory_docs'), );
為了防止任何人建立此類別的實例,我們可以將建構函式設為私有。
final class Operator { // ... private function __construct() {} // This constructor cannot be called }
我們也可以使用enum不含外殼的。 Enum 接受靜態方法且不能實例化。
enum Query { public static function and() { /* ... */ } public static function eq() { /* ... */ } }
類別和枚舉靜態方法都可以以相同的方式呼叫。
變數中的閉包
由於找不到理想的解決方案,我們開始熱衷於不太可能的解決方案。
如果我們想要一個看起來與 MongoDB 語法非常相似且沒有名稱限制的簡短語法,那麼我們就會想到使用變數來儲存閉包。請注意,這(...)是PHP 8.1 中建立閉包的新語法。
$eq = Operator::eq(...); $and = Operator::and(...);
$PHP 使用美元符號作為變數前綴,MongoDB 使用相同的運算子作為前綴。
pipeline( $match( $or( $query(status: $eq('shipped')), $query(date: $gte(new UTCDateTime())), ), ), $lookup(from: 'inventory', localField: 'product_id', foreignField: 'product_id', as: 'inventory_docs'), );
函式庫可以將這些閉包作為陣列提供。
enum Query { public static function and(array ...$queries) { /* ... */ } public static function eq(mixed $value) { /* ... */ } public static function query(mixed ...$query) { /* ... */ } /** @return array{and:callable,eq:callable,query:callable} */ public static function functions(): array { return [ 'and' => self::and(...), 'eq' => self::eq(...), 'query' => self::query(...), ]; } }
取得所有變數的語法有點冗長,但仍可讀。
['and' => $and, 'eq' => $eq, 'query' => $query] = Query::functions();
extract我們可以使用 Laravel 中經常使用但 PHPStorm 和靜態分析工具非常討厭的神奇功能將所有變數導入到當前作用域中。
extract(Query::functions()); var_dump($and( $query(foo: $eq(5)), $query(bar: $eq(10)) )); // INFO: MixedFunctionCall - Cannot call function on mixed
結論
正如您所看到的,在使用保留關鍵字時,PHP 中的函數命名並不那麼簡單。
以上是如何克服PHP的命名限制來建模MongoDB運算符的詳細內容。更多資訊請關注PHP中文網其他相關文章!