Files
server/core/lib/Db/Collection.php
2025-12-21 10:09:54 -05:00

296 lines
8.2 KiB
PHP

<?php
namespace KTXC\Db;
use MongoDB\Collection as MongoCollection;
use MongoDB\InsertOneResult;
use MongoDB\UpdateResult;
use MongoDB\DeleteResult;
/**
* Wrapper for MongoDB\Collection
* Provides abstraction layer for MongoDB collection operations
*/
class Collection
{
private MongoCollection $collection;
public function __construct(MongoCollection $collection)
{
$this->collection = $collection;
// Set type map to return plain arrays instead of objects
// This converts BSON types to PHP native types
$this->collection = $collection->withOptions([
'typeMap' => [
'root' => 'array',
'document' => 'array',
'array' => 'array'
]
]);
}
/**
* Find documents in the collection
*
* @param array $filter Query filter
* @param array $options Query options
* @return Cursor
*/
public function find(array $filter = [], array $options = []): Cursor
{
$filter = $this->convertFilter($filter);
/** @var \Iterator $cursor */
$cursor = $this->collection->find($filter, $options);
return new Cursor($cursor);
}
/**
* Find a single document
*
* @param array $filter Query filter
* @param array $options Query options
* @return array|null Returns array with _id as string
*/
public function findOne(array $filter = [], array $options = []): ?array
{
$filter = $this->convertFilter($filter);
$result = $this->collection->findOne($filter, $options);
if ($result === null) {
return null;
}
// Convert to array if it's an object
if (is_object($result)) {
$result = (array) $result;
}
return $this->convertBsonToNative($result);
}
/**
* Insert a single document
*
* @param array|object $document Document to insert
* @param array $options Insert options
* @return InsertOneResult
*/
public function insertOne(array|object $document, array $options = []): InsertOneResult
{
$document = $this->convertDocument($document);
return $this->collection->insertOne($document, $options);
}
/**
* Insert multiple documents
*
* @param array $documents Documents to insert
* @param array $options Insert options
*/
public function insertMany(array $documents, array $options = []): mixed
{
$documents = array_map(fn($doc) => $this->convertDocument($doc), $documents);
return $this->collection->insertMany($documents, $options);
}
/**
* Update a single document
*
* @param array $filter Query filter
* @param array $update Update operations
* @param array $options Update options
* @return UpdateResult
*/
public function updateOne(array $filter, array $update, array $options = []): UpdateResult
{
$filter = $this->convertFilter($filter);
$update = $this->convertDocument($update);
return $this->collection->updateOne($filter, $update, $options);
}
/**
* Update multiple documents
*
* @param array $filter Query filter
* @param array $update Update operations
* @param array $options Update options
* @return UpdateResult
*/
public function updateMany(array $filter, array $update, array $options = []): UpdateResult
{
$filter = $this->convertFilter($filter);
$update = $this->convertDocument($update);
return $this->collection->updateMany($filter, $update, $options);
}
/**
* Delete a single document
*
* @param array $filter Query filter
* @param array $options Delete options
* @return DeleteResult
*/
public function deleteOne(array $filter, array $options = []): DeleteResult
{
$filter = $this->convertFilter($filter);
return $this->collection->deleteOne($filter, $options);
}
/**
* Delete multiple documents
*
* @param array $filter Query filter
* @param array $options Delete options
* @return DeleteResult
*/
public function deleteMany(array $filter, array $options = []): DeleteResult
{
$filter = $this->convertFilter($filter);
return $this->collection->deleteMany($filter, $options);
}
/**
* Count documents matching filter
*
* @param array $filter Query filter
* @param array $options Count options
* @return int
*/
public function countDocuments(array $filter = [], array $options = []): int
{
$filter = $this->convertFilter($filter);
return $this->collection->countDocuments($filter, $options);
}
/**
* Execute aggregation pipeline
*
* @param array $pipeline Aggregation pipeline
* @param array $options Aggregation options
* @return Cursor
*/
public function aggregate(array $pipeline, array $options = []): Cursor
{
/** @var \Iterator $cursor */
$cursor = $this->collection->aggregate($pipeline, $options);
return new Cursor($cursor);
}
/**
* Create an index
*
* @param array $key Index specification
* @param array $options Index options
* @return string Index name
*/
public function createIndex(array $key, array $options = []): string
{
return $this->collection->createIndex($key, $options);
}
/**
* Drop the collection
*/
public function drop(): array|object|null
{
return $this->collection->drop();
}
/**
* Get collection name
*/
public function getCollectionName(): string
{
return $this->collection->getCollectionName();
}
/**
* Get database name
*/
public function getDatabaseName(): string
{
return $this->collection->getDatabaseName();
}
/**
* Convert ObjectId instances in filter to MongoDB ObjectId
*/
private function convertFilter(array $filter): array
{
return $this->convertArray($filter);
}
/**
* Convert ObjectId instances in document to MongoDB ObjectId
*/
private function convertDocument(array|object $document): array|object
{
if (is_array($document)) {
return $this->convertArray($document);
}
return $document;
}
/**
* Recursively convert ObjectId and UTCDateTime instances
*/
private function convertArray(array $data): array
{
foreach ($data as $key => $value) {
if ($value instanceof ObjectId) {
$data[$key] = $value->toBSON();
} elseif ($value instanceof UTCDateTime) {
$data[$key] = $value->toBSON();
} elseif (is_array($value)) {
$data[$key] = $this->convertArray($value);
}
}
return $data;
}
/**
* Get the underlying MongoDB Collection
* Use sparingly - prefer using wrapper methods
*/
public function getMongoCollection(): MongoCollection
{
return $this->collection;
}
/**
* Convert BSON objects to native PHP types
* Handles ObjectId, UTCDateTime, and other BSON types
*/
private function convertBsonToNative(mixed $data): mixed
{
if (is_array($data)) {
foreach ($data as $key => $value) {
$data[$key] = $this->convertBsonToNative($value);
}
return $data;
}
if (is_object($data)) {
// Convert MongoDB BSON ObjectId to string
if ($data instanceof \MongoDB\BSON\ObjectId) {
return (string) $data;
}
// Convert MongoDB BSON UTCDateTime to string or DateTime
if ($data instanceof \MongoDB\BSON\UTCDateTime) {
return (string) $data->toDateTime()->format('c');
}
// Convert other objects to arrays recursively
if (method_exists($data, 'bsonSerialize')) {
return $this->convertBsonToNative($data->bsonSerialize());
}
return (array) $data;
}
return $data;
}
}