Working with MongoDB in PHP can be confusing, especially when it comes to the behavior of findOne()
and how the results can be accessed. This blog post aims to clarify the nuances and help you avoid common pitfalls.
findOne()
in PHPThe findOne()
method retrieves a single document from a MongoDB collection. By default, it returns a MongoDB\Model\BSONDocument
object. This object supports:
->
): Access document properties like $doc->key
.['key']
): Access properties like $doc['key']
, because BSONDocument
implements PHP's ArrayAccess
interface.Here's a simple example:
$doc = $collection->findOne([
'slug' => 'john-doe',
]);
if ($doc) {
// Object-style access
echo "Slug (object-style): " . $doc->slug . "\n";
// Array-style access
echo "Slug (array-style): " . $doc['slug'] . "\n";
} else {
echo "No document found with slug 'john-doe'.\n";
}
Both access styles work fine in this case. But what happens if you call jsonSerialize()
or use the typeMap
option?
jsonSerialize()
Calling jsonSerialize()
on a BSONDocument
converts it into a stdClass
object, which only supports object-style access (->
). Array-style access (['key']
) will fail.
Here’s an example:
$serializedDoc = $doc->jsonSerialize();
// Object-style access works
echo "Slug (object-style): " . $serializedDoc->slug . "\n";
// Array-style access will fail
try {
echo "Slug (array-style): " . $serializedDoc['slug'] . "\n";
} catch (Throwable $e) {
echo "Error: " . $e->getMessage() . "\n";
}
If the document exists:
Slug (object-style): john-doe
Error: Cannot use object of type stdClass as array
This demonstrates that jsonSerialize()
changes the access behavior.
typeMap
You can use the typeMap
option to control how findOne()
returns the document. For example:
$doc = $collection->findOne([
'slug' => 'john-doe',
], [
'typeMap' => ['root' => 'array'],
]);
if ($doc) {
// Array-style access works
echo "Slug (array-style): " . $doc['slug'] . "\n";
// Object-style access will fail
try {
echo "Slug (object-style): " . $doc->slug . "\n";
} catch (Throwable $e) {
echo "Error: " . $e->getMessage() . "\n";
}
}
$doc = $collection->findOne([
'slug' => 'john-doe',
], [
'typeMap' => ['root' => 'object'],
]);
if ($doc) {
// Object-style access works
echo "Slug (object-style): " . $doc->slug . "\n";
// Array-style access will fail
try {
echo "Slug (array-style): " . $doc['slug'] . "\n";
} catch (Throwable $e) {
echo "Error: " . $e->getMessage() . "\n";
}
}
findOne()
returns a BSONDocument
, supporting both access styles.jsonSerialize()
: Converts to stdClass
, breaking array-style access.typeMap
:['root' => 'array']
: Returns an associative array, breaking object-style access.['root' => 'object']
: Returns a PHP object, breaking array-style access.MongoDB’s PHP driver offers flexibility in accessing documents, but it can be confusing due to the interplay of BSONDocument
, jsonSerialize()
, and typeMap
. Here’s a quick summary:
Access Type | Default (BSONDocument ) | After jsonSerialize() | With typeMap => array | With typeMap => object |
---|---|---|---|---|
Object-style (-> ) | Works | Works | Fails | Works |
Array-style ([] ) | Works | Fails | Works | Fails |
When working with findOne()
, choose the method that best suits your needs and stick to it for consistency. Let me know if this post helps clarify the confusion!