mongodb / laravel-mongodb

A MongoDB based Eloquent model and Query builder for Laravel (Moloquent)
https://www.mongodb.com/compatibility/mongodb-laravel-integration
MIT License
6.99k stars 1.42k forks source link

_id returned by raw() #3151

Closed Torquin closed 1 day ago

Torquin commented 5 days ago

Description:

Following the latest update and id management, raw requests do not seem to return 'id' but still return '_id'

Steps to reproduce

  1. $tmp = \App\Models\Container::first()->toArray(); => this returns id
  2. $tmp = \App\Models\Container::raw(function($collection) { return $collection->findOne(); }); => this returns _id

Expected behaviour

Should receive 'id'

Actual behaviour

Receive '_id'

GromNaN commented 5 days ago

Exact, the raw() method gives access to the MongoDB features directly. I didn't want to apply the transformation to the results of this function because it seems to me that it's designed for advanced use and that it's not compatible with Laravel and SQL databases.

What you can do is add the id field and remove _id from the projection.

\App\Models\Container::raw(function($collection) {
    return $collection->findOne(
        [], // filter
        // Options
        [
            'projection' => [
                // Remove the "_id" field
                '_id' => -1,
                // Add the "id" field, with the value if "_id"
                'id' => '$_id',
                // You must specify the other fields you want to retrieve
                'name' => 1,
            ],
        ]
    );
});
Torquin commented 5 days ago

@GromNaN Thanks for your answer.

We tried to test your pending pull-request but we can't go further as it looks like raw() requests return BSON documents instead of eloquent models.

We also tested the same thing in mongodb 4.8.x & 5.0.0 and both (->where & ->raw) returned eloquent models.

GromNaN commented 5 days ago

Why do you want to use the raw() method here?

If you have a specific filter that you can't or don't want to express with the Query Builder, you can pass it directly to where.

$filter = [/* Complex query expression */];

\App\Models\Container::where($filter)->first()
Torquin commented 5 days ago

This was just a simplified example of a raw request. We use complex aggregation built dynamically by our app (facets, projects, lookup, unwind etc). These requests were returning eloquent models that we used to call their methods.

Another thing is that this update would force dealing with 2 different data formats depending on whether using raw or classic query builder.

Torquin commented 2 days ago

Hi @GromNaN ,

Another issue we've found is that the function "aliasIdForResult" works well (transforms all _id to id, nested or not) for class query builder. But when using with raw(), as raw() returns BSON documents, $values is neither an array nor a stdClass and so nothing is applied.

Thanks for your help

GromNaN commented 2 days ago

You need to change the typeMap to get data in array.

$collection->aggregate($pipeline, ['typeMap' => ['root' => 'object', 'document' => 'object', 'array' => 'array']);
Torquin commented 2 days ago

Great one ! Ok we've resolved to fetching data & forceFilling our model. The typeMap did the job and all '_id' (even nested) are changed to 'id'. Thanks for bearing with us :)

GromNaN commented 1 day ago

@Torquin Can you check branch 5.x, it should handle the conversion automatically (for Eloquent Models only)

Torquin commented 4 hours ago

@GromNaN Thanks so much, this is perfect !