Skip to content

Commit

Permalink
PHPORM-239 Convert raw results to eloquent models, only when _id or i…
Browse files Browse the repository at this point in the history
…d is returned
  • Loading branch information
GromNaN committed Sep 17, 2024
1 parent 07a44cc commit d84a160
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 23 deletions.
24 changes: 16 additions & 8 deletions src/Eloquent/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace MongoDB\Laravel\Eloquent;

use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use MongoDB\BSON\Document;
use MongoDB\Driver\CursorInterface;
use MongoDB\Driver\Exception\WriteException;
use MongoDB\Laravel\Connection;
Expand All @@ -16,7 +17,9 @@
use function array_merge;
use function collect;
use function is_array;
use function is_object;
use function iterator_to_array;
use function property_exists;

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

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

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

// Convert MongoDB BSONDocument to a single object.
if ($results instanceof BSONDocument) {
$results = $results->getArrayCopy();

return $this->model->newFromBuilder((array) $results);
// Convert MongoDB Document to a single object.
if (is_object($results) && (property_exists($results, '_id') || property_exists($results, 'id'))) {
$results = (array) match (true) {
$results instanceof BSONDocument => $results->getArrayCopy(),
$results instanceof Document => $results->toPHP(['root' => 'array', 'document' => 'array', 'array' => 'array']),
default => $results,
};
}

// The result is a single object.
if (is_array($results) && array_key_exists('_id', $results)) {
return $this->model->newFromBuilder((array) $results);
if (is_array($results) && (array_key_exists('_id', $results) || array_key_exists('id', $results))) {
$results = $this->query->aliasIdForResult($results);

return $this->model->newFromBuilder($results);
}

return $results;
Expand Down
22 changes: 9 additions & 13 deletions src/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
use InvalidArgumentException;
use LogicException;
use MongoDB\BSON\Binary;
use MongoDB\BSON\Document;
use MongoDB\BSON\ObjectID;
use MongoDB\BSON\Regex;
use MongoDB\BSON\UTCDateTime;
use MongoDB\Builder\Stage\FluentFactoryTrait;
use MongoDB\Driver\Cursor;
use MongoDB\Driver\CursorInterface;
use Override;
use RuntimeException;
use stdClass;
Expand Down Expand Up @@ -935,17 +935,7 @@ public function raw($value = null)
{
// Execute the closure on the mongodb collection
if ($value instanceof Closure) {
$results = call_user_func($value, $this->collection);

if ($results instanceof CursorInterface) {
$results = $results->toArray();
}

if (is_array($results) || is_object($results)) {
$results = $this->aliasIdForResult($results);
}

return $results;
return call_user_func($value, $this->collection);
}

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

/**
* @internal
*
* @psalm-param T $values
*
* @psalm-return T
*
* @template T of array|object
*/
private function aliasIdForResult(array|object $values): array|object
public function aliasIdForResult(array|object $values): array|object
{
if ($values instanceof Document) {
$values = $values->toPHP(['typeMap' => ['root' => 'object', 'document' => 'array', 'array' => 'array']]);
}

if (is_array($values)) {
if (array_key_exists('_id', $values) && ! array_key_exists('id', $values)) {
$values['id'] = $values['_id'];
Expand Down
27 changes: 25 additions & 2 deletions tests/ModelTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -908,10 +908,17 @@ public function testRaw(): void
$this->assertInstanceOf(User::class, $users[0]);

$user = User::raw(function (Collection $collection) {
return $collection->findOne(['age' => 35]);
return $collection->findOne(
['age' => 35],
['projection' => ['_id' => 1, 'name' => 1, 'age' => 1, 'now' => '$$NOW']],
);
});

$this->assertTrue(Model::isDocumentModel($user));
$this->assertInstanceOf(User::class, $user);
$this->assertArrayNotHasKey('_id', $user->getAttributes());
$this->assertArrayHasKey('id', $user->getAttributes());
$this->assertNotEmpty($user->id);
$this->assertInstanceOf(Carbon::class, $user->now);

$count = User::raw(function (Collection $collection) {
return $collection->count();
Expand All @@ -922,6 +929,22 @@ public function testRaw(): void
return $collection->insertOne(['name' => 'Yvonne Yoe', 'age' => 35]);
});
$this->assertNotNull($result);

$result = User::raw(function (Collection $collection) {
return $collection->aggregate([
['$set' => ['now' => '$$NOW']],
['$limit' => 2],
]);
});

$this->assertInstanceOf(EloquentCollection::class, $result);
$this->assertCount(2, $result);
$user = $result->first();
$this->assertInstanceOf(User::class, $user);
$this->assertArrayNotHasKey('_id', $user->getAttributes());
$this->assertArrayHasKey('id', $user->getAttributes());
$this->assertNotEmpty($user->id);
$this->assertInstanceOf(Carbon::class, $user->now);
}

public function testDotNotation(): void
Expand Down

0 comments on commit d84a160

Please sign in to comment.