Skip to content

Commit d84a160

Browse files
committed
PHPORM-239 Convert raw results to eloquent models, only when _id or id is returned
1 parent 07a44cc commit d84a160

File tree

3 files changed

+50
-23
lines changed

3 files changed

+50
-23
lines changed

src/Eloquent/Builder.php

+16-8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace MongoDB\Laravel\Eloquent;
66

77
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
8+
use MongoDB\BSON\Document;
89
use MongoDB\Driver\CursorInterface;
910
use MongoDB\Driver\Exception\WriteException;
1011
use MongoDB\Laravel\Connection;
@@ -16,7 +17,9 @@
1617
use function array_merge;
1718
use function collect;
1819
use function is_array;
20+
use function is_object;
1921
use function iterator_to_array;
22+
use function property_exists;
2023

2124
/** @method \MongoDB\Laravel\Query\Builder toBase() */
2225
class Builder extends EloquentBuilder
@@ -178,21 +181,26 @@ public function raw($value = null)
178181

179182
// Convert MongoCursor results to a collection of models.
180183
if ($results instanceof CursorInterface) {
181-
$results = iterator_to_array($results, false);
184+
$results->setTypeMap(['root' => 'array', 'document' => 'array', 'array' => 'array']);
185+
$results = $this->query->aliasIdForResult(iterator_to_array($results));
182186

183187
return $this->model->hydrate($results);
184188
}
185189

186-
// Convert MongoDB BSONDocument to a single object.
187-
if ($results instanceof BSONDocument) {
188-
$results = $results->getArrayCopy();
189-
190-
return $this->model->newFromBuilder((array) $results);
190+
// Convert MongoDB Document to a single object.
191+
if (is_object($results) && (property_exists($results, '_id') || property_exists($results, 'id'))) {
192+
$results = (array) match (true) {
193+
$results instanceof BSONDocument => $results->getArrayCopy(),
194+
$results instanceof Document => $results->toPHP(['root' => 'array', 'document' => 'array', 'array' => 'array']),
195+
default => $results,
196+
};
191197
}
192198

193199
// The result is a single object.
194-
if (is_array($results) && array_key_exists('_id', $results)) {
195-
return $this->model->newFromBuilder((array) $results);
200+
if (is_array($results) && (array_key_exists('_id', $results) || array_key_exists('id', $results))) {
201+
$results = $this->query->aliasIdForResult($results);
202+
203+
return $this->model->newFromBuilder($results);
196204
}
197205

198206
return $results;

src/Query/Builder.php

+9-13
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020
use InvalidArgumentException;
2121
use LogicException;
2222
use MongoDB\BSON\Binary;
23+
use MongoDB\BSON\Document;
2324
use MongoDB\BSON\ObjectID;
2425
use MongoDB\BSON\Regex;
2526
use MongoDB\BSON\UTCDateTime;
2627
use MongoDB\Builder\Stage\FluentFactoryTrait;
2728
use MongoDB\Driver\Cursor;
28-
use MongoDB\Driver\CursorInterface;
2929
use Override;
3030
use RuntimeException;
3131
use stdClass;
@@ -935,17 +935,7 @@ public function raw($value = null)
935935
{
936936
// Execute the closure on the mongodb collection
937937
if ($value instanceof Closure) {
938-
$results = call_user_func($value, $this->collection);
939-
940-
if ($results instanceof CursorInterface) {
941-
$results = $results->toArray();
942-
}
943-
944-
if (is_array($results) || is_object($results)) {
945-
$results = $this->aliasIdForResult($results);
946-
}
947-
948-
return $results;
938+
return call_user_func($value, $this->collection);
949939
}
950940

951941
// Create an expression for the given value
@@ -1659,14 +1649,20 @@ private function aliasIdForQuery(array $values): array
16591649
}
16601650

16611651
/**
1652+
* @internal
1653+
*
16621654
* @psalm-param T $values
16631655
*
16641656
* @psalm-return T
16651657
*
16661658
* @template T of array|object
16671659
*/
1668-
private function aliasIdForResult(array|object $values): array|object
1660+
public function aliasIdForResult(array|object $values): array|object
16691661
{
1662+
if ($values instanceof Document) {
1663+
$values = $values->toPHP(['typeMap' => ['root' => 'object', 'document' => 'array', 'array' => 'array']]);
1664+
}
1665+
16701666
if (is_array($values)) {
16711667
if (array_key_exists('_id', $values) && ! array_key_exists('id', $values)) {
16721668
$values['id'] = $values['_id'];

tests/ModelTest.php

+25-2
Original file line numberDiff line numberDiff line change
@@ -908,10 +908,17 @@ public function testRaw(): void
908908
$this->assertInstanceOf(User::class, $users[0]);
909909

910910
$user = User::raw(function (Collection $collection) {
911-
return $collection->findOne(['age' => 35]);
911+
return $collection->findOne(
912+
['age' => 35],
913+
['projection' => ['_id' => 1, 'name' => 1, 'age' => 1, 'now' => '$$NOW']],
914+
);
912915
});
913916

914-
$this->assertTrue(Model::isDocumentModel($user));
917+
$this->assertInstanceOf(User::class, $user);
918+
$this->assertArrayNotHasKey('_id', $user->getAttributes());
919+
$this->assertArrayHasKey('id', $user->getAttributes());
920+
$this->assertNotEmpty($user->id);
921+
$this->assertInstanceOf(Carbon::class, $user->now);
915922

916923
$count = User::raw(function (Collection $collection) {
917924
return $collection->count();
@@ -922,6 +929,22 @@ public function testRaw(): void
922929
return $collection->insertOne(['name' => 'Yvonne Yoe', 'age' => 35]);
923930
});
924931
$this->assertNotNull($result);
932+
933+
$result = User::raw(function (Collection $collection) {
934+
return $collection->aggregate([
935+
['$set' => ['now' => '$$NOW']],
936+
['$limit' => 2],
937+
]);
938+
});
939+
940+
$this->assertInstanceOf(EloquentCollection::class, $result);
941+
$this->assertCount(2, $result);
942+
$user = $result->first();
943+
$this->assertInstanceOf(User::class, $user);
944+
$this->assertArrayNotHasKey('_id', $user->getAttributes());
945+
$this->assertArrayHasKey('id', $user->getAttributes());
946+
$this->assertNotEmpty($user->id);
947+
$this->assertInstanceOf(Carbon::class, $user->now);
925948
}
926949

927950
public function testDotNotation(): void

0 commit comments

Comments
 (0)