Update embedded subdocument in array with doctrine and mongodb

doctrine, document, mongodb

Solution

This was a bug in ODM and should be fixed in PR #661. Please take a look and ensure that the revised test satisfies your use case.

Until the next beta of ODM is tagged, this will only reside in the master branch; however, that should match your `1.0.*@dev` version requirement.

Problem

I want to update (replace) a subdocument within an array of all the documents, that have this specific embedded subdocument. My sample content object is: ``` { "_id" : ObjectId("51f289e5345f9d10090022ef"), "title" : "This is a content", "descriptors" : [ { "_id" : ObjectId("51f289e5345f9d10090022f4"), "name" : "This is a descriptor", "type" : "This is a property" }, { "_id" : ObjectId("51f289e5345f9d10090022f0"), "name" : "This is another descriptor", "type" : "This is another property" } ] } ``` I want to find the object with a specific `descriptor._id`. I want to replace the one subdocument with another one (respectivley with an updated version of the old object, but not necessarily with the same properties). While this works well in RoboMongo / shell... ``` db.Content.update( { 'descriptors._id': ObjectId("51f289e5345f9d10090022f4") }, { $set: { 'descriptors.$': { "_id" : ObjectId("51f289e5345f9d10090022f4"), "name" : "This is the updated descriptor", "category" : "This is a new property" } } }, { 'multi': true }) ``` ...and with the plain php methods... ``` $descriptor = array(); $descriptor['_id'] = new \MongoID('51f289e5345f9d10090022f4'); $descriptor['name'] = 'This is the updated descriptor'; $descriptor['category'] = 'This is a new property'; $mongo = new \Mongo('mongodb://localhost:27017'); $database = $mongo->selectDB('MyDatabase'); $output = $database->selectCollection('Content')->update( array('descriptors._id' => $descriptor['_id']), array('$set' => array('descriptors.$' => $descriptor)), array("multiple" => true) ); ``` ...it doesn't work with Doctrine MongoDB ODM... ``` $descriptor = array(); $descriptor['_id'] = new \MongoID('51f289e5345f9d10090022f4'); $descriptor['name'] = 'This is the updated descriptor'; $descriptor['category'] = 'This is a new property'; $query = $dm->createQueryBuilder('Content') ->update()->multiple(true) ->field('descriptors._id')->equals($descriptor['_id']) ->field('descriptors.$')->set($descriptor) ->getQuery()->execute(); ``` ...because it fails with the following error: Notice: Undefined offset: 2 in C:\MyProject\vendor\doctrine\mongodb-odm\lib\Doctrine\ODM\MongoDB\Persisters\DocumentPersister.php line 998 So I assume, that Doctrine MongoDB ODM needs three parts in the dot-notation. The not so nice solution would be to iterate over the properties of the subdocument and set them manually. ``` $descriptor = array(); $descriptor['_id'] = new \MongoID('51f289e5345f9d10090022f4'); $descriptor['name'] = 'This is the updated descriptor'; $descriptor['category'] = 'This is a new property'; $query = $dm->createQueryBuilder('Content') ->update()->multiple(true) ->field('descriptors._id')->equals($descriptor['_id']); foreach ($descriptor as $key => $value) { $query->field('descriptors.$.'.$key)->set($value); } $query->getQuery()->execute(); ``` But this will only update existing and add new properties, but won't remove old/unnecessary properties from the subdocument. Any ideas how to solve the problem: - with a simple query - while using Doctrine MongoDB ODM - without looping over the subdocument-array in php I'm using: - Mongo-Server: 2.4.5 - PHP: 5.4.16 - PHP-Mongo-Driver: 1.4.1 Composer: ``` "php": ">=5.3.3", "symfony/symfony": "2.3.*", "doctrine/orm": ">=2.2.3,<2.4-dev", "doctrine/doctrine-bundle": "1.2.*", "doctrine/mongodb-odm": "1.0.*@dev", "doctrine/mongodb-odm-bundle": "3.0.*@dev" ```

Original source