[Feature Request] Ability to disable root-level _id and id mapping
Currently, mongodb/laravel-mongodb automatically maps MongoDB’s _id field to Laravel’s id attribute on Eloquent models. This makes it difficult to use a custom id field alongside MongoDB’s native _id.
Example use case:
I want to store documents like this:
{ "_id": ObjectId("6518e9c4c6a3d1f9b3a2f1a7"), "id": "lyqxei40yhyw", "title": "Some custom HTML block" }
But right now, if I set id, it overrides the default _id mapping or causes conflicts.
Proposed solution: Add a configuration option or model-level property to disable the automatic mapping of _id to id, so developers can:
- Keep _id as the real MongoDB ObjectId
- Define their own id (or any other field like customId) without collisions
Migration issue: I am migrating from the package jenssegers/mongodb, where having both _id and id fields was possible. Since I already have large amounts of existing data with both fields present, the current forced mapping causes serious migration issues and makes it impossible to work with my existing dataset.
Already possible, add 'rename_embedded_id_field' => false in your database.php config, like so:
'mongodb' => [
..
'host' => "HOST",
'rename_embedded_id_field' => false
]
Already possible, add
'rename_embedded_id_field' => falsein your database.php config, like so:'mongodb' => [ .. 'host' => "HOST", 'rename_embedded_id_field' => false ]
That option only affects embedded documents. I need to disable the root id field mapping, not the embedded one.
Weirdly, it does work for me for root id field mappings.
No, it doesn’t actually work.
rename_embedded_id_field only affects embedded documents, not the root document. You can also see that clearly from its naming — it doesn’t apply to the root id field.
When you try to insert the document like this:
$model->id = 'lyqxei40yhyw';
$model->title = 'Some custom HTML block';
$model->save();
you’ll get the error:
Cannot have both "id" and "_id" fields.
Also, if you try to query using id, it won’t return any results — because the query will look for _id, not id.
I don't particularly use models, but this works:
$dbCon = DB::connection('mongodb');
$dbCon->table('collection_name_here')
->updateOrInsert(['id' => $item['id']], $item);
MongoDB imposes the primary key to be the _id field. There is no restriction in the type of this field.
Having 2 id isn't practical.
For the migration, update your database collections by an aggregation like this:
$collection->insertMany([
['_id' => 1, 'id' => 11, 'name' => 'Alice'],
['_id' => 2, 'id' => 22, 'name' => 'Bob'],
]);
$collection->aggregate([
['$match' => [ 'id' => ['$exists' => true] ]],
['$set' => [ '_id' => '$id' ]],
['$unset' => ['id']],
[
'$merge' => [
'into' => $collectionName,
'whenMatched' => 'fail',
'whenNotMatched' => 'insert',
]
]
]);
$collection->deleteMany(['id' => ['$exists' => true]]);
You are missing the point. The second id is not meant to be a primary key, it is just a field called id.
Now, when you get the entities from mongo, the mongodb _id is renamed to id which replaces your own id field.
You are missing the point. The second id is not meant to be a primary key, it is just a field called
id.Now, when you get the entities from mongo, the mongodb
_idis renamed toidwhich replaces your ownidfield.
That's something that can't be supported while also ensuring a good compatibility with Laravel packages.
@SMFloris unfortunately, the name id is hardcoded as an identifier in various places, despite model classes having a dedicated field to indicate the primary key name. We have created pull requests to Laravel to fix this and avoid this scenario, but the pull requests were closed without merging. Now, you may say that id is just a field called id, but we all know that it's the default name for a primary key and shorthand for "identifier", so I'm not sure what field you would conceivably be calling id except for an identifier. Having both _id and id fields for identifiers is inefficient (because of duplicate storage), confusing (because they will have different values unless explicitly set), or plain wrong (because the primary key is always _id in MongoDB). I'm disappointed that Laravel still hardcodes this name for an identifier when it's available as model property, but there's nothing we can do about this.
I understand that the current situation is frustrating, especially as you can't change the value of _id once the document has been written. In an ideal world, we could've avoided this whole thing by Laravel respecting the primary key name defined in your models, in which case it would use _id by default, but you could tell Laravel to use id if necessary for backwards compatibility. It may be inefficient and wrong, but at least it won't break your app. Unfortunately that wasn't an option for the Laravel team, so we had to work around this.
If you can elaborate your use case for using an id field that isn't an identifier, I'm genuinely curious what it is -- just because I can't imagine one doesn't mean there isn't one. Unfortunately, the only workaround I can recommend is to rename that field to something else, along with lobbying the Laravel team to respect their own convention and stop hardcoding identifier names.