How to update embedded document in MongoDB with Doctrine ODM - mongodb

I'm unable to find how to update embedded documents with Doctrine Mongo ODM in Symfony2. I have a class called Page with many embedded documents "Comments" and I want to use createQueryBuilder to update specific comment. Here is a simple class that I have:
class Page
{
protected $id;
/** #MongoDB\EmbedMany */
private $pageComment = array();
}
I searched the whole internet, but I don't see to find any info on how to update subdocuments of a document with Doctrine ODM query builder. I will be thankful for any information as I'm new to both Doctrine and Mongo. In simple words I want to update specific comment in a page after searching for it by id.
Thanks in advance for your help!

You can update only one field at time (instead of pageComment.$):
$this->createQueryBuilder('page')
->update()
->field('id')->equals($pageId)
->field('pageComment.id')->equals($pageCommentId)
->field("pageComment.$.field1")->set($field1)
->getQuery()
->execute();

If you wan to use queryBuilder use this
$dm->createQueryBuilder('Page')
->update()
->field('page.pageComment')->set( <$newupdatePageCommentObj> )
->field('id')->equals('<matchedId>')
->getQuery()
->execute();
Or When you generate setters and getters for a EmbedMany member variable it will generate add and remove member functions inside your class. so in your case these will be member functions:
public function addPageComment(type_hint_with_your_pageComment_document $pageComment )
{
$this->pageComment[] = $pageComment;
}
public function removePageComment( type_hint_with_your_pageComment_document $pageComment )
{
$this->items->removeElement( $pageComment );
}
So you can use addPageComment() function which will add it if does not exists and will update it will its already there.

$yourArrayPageComment = array(
"id" => new \MongoId($pageCommentId),
"field1" => $field1,
...
)
$this->createQueryBuilder('page')
->update()
->field('id')->equals($pageId)
->field('pageComment.id')->equals($pageCommentId)
->field("pageComment.$")->set($yourArrayPageComment)
->getQuery()
->execute();

Related

Retrieve 'username' from Articles table

I have two tables, 'users' and 'articles'. Articles have a column 'user_id' which is a foreign key that references the user_id in 'users'.
I have in the Articles model this function which should return the user data:
public function user()
{
return $this->belongsTo('App\User');
}
And this works fine when I pass my articles to my viewer and call it in blade template:
#foreach($articles as $article)
<p>{{$article->user->name}}</p>
#endforeach
But I am trying to use the RESTful approach, so I am rather retrieving my data from JS (VueJS)
axios.get('/api/articles')
that should fire my Controller's function:
public function index()
{
$books = bookpost::all();
return $books;
}
So I was wondering if there's a way to append the user names to the JSON array of articles before returning it because in JS I couldn't get to find a way to get the username.
You can use "eager loading" in your query to help:
$books = bookpost::with('user')->get();
You may even eager load nested relationships:
$books = bookpost::with('user.friends')->get();
Have a look at the documentation for further help.

Symfony2 & DoctrineMongoDB ODM: Custom Mapping Types

My problem is the following: I have a mongo database which stores such objects
{
"name":"Accord Neo",
"number_of_photos":"3",
"id":"accord_neo",
"description":"Very comfortable sofa.",
"details": {
"chair_is": false,
"Sofa_bed": "delfin",
"Structure_configuration": "corner"
},
"properties":[
{
"property":"2-У-1",
"value":"2150 X 1550 X 880"
},
{
"property":"Sleeping place",
"value":"2150 X 1550 X 880"
}
]
}​
I need to retrieve these objects in Symfony2 using Doctrine Mapping Types and the problem is that I don't understand how to create my Custom Classes for Mapping for the field "details" and the field "properties". I tried to make analogy from the official website of doctrine
http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/basic-mapping.html#custom-mapping-types
but I still don't undertand how it works and how it should be implemented in my case.
Create a new Document for "details" like this:
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\EmbeddedDocument
*/
class Details
{
}
Note that it's an embeddeddocument so it will be stored as part of the one record in mongodb. Do exactly the same thing for Properties
Then in your parent object, you embed a set of them and create the collection in your constructor:
/**
* #MongoDB\EmbedOne(targetDocument="Details")
*/
protected $details;
/**
* #MongoDB\EmbedMany(targetDocument="Properties")
*/
protected $propertieslist;
public function __construct()
{
$this->propertieslist = new ArrayCollection();
}
You can generate your setters and getters using the tools that come with symfony.
Assuming you're using Forms to manage them, you need to create a FormType for each embedded document.
For the details, you just go "new DetailsType" as your type (as opposed to "text" or "choice").
For the properties, you'll need to add a 'collection' instead, and then pass an array where 'type' => new PropertiesType. To add and remove dynamically is going to need a bit of javascript. Details are here: http://symfony.com/doc/current/cookbook/form/form_collections.html

Named collection keys in ArrayCollection

I'm looking for a way to have an associative array keys for a ODM ArrayCollection.
The entity has the following mapping:
/**
* $fields
*
* The entities fields
*
* #ODM\ReferenceMany(targetDocument="JobboardEntity\Entity\EntityField", sort={"name"="asc"}, cascade={"all"})
* #var \Doctrine\Common\Collections\ArrayCollection
*/
protected $fields;
/**
* addField
*
* Add a single field to the field collection
*
* #param EntityField $field The field to add
*/
public function addField(EntityField $field)
{
$this->fields[$field->getFieldName()] = $field;
}
Notice in the addField method I am giving the item a index of $field->getFieldName().
Unfortunately, Doctrine forgets this key and returns an ArrayCollection with numeric indexes rather than the previously set strings.
This means that in order to correctly implement $this->hasField($fieldName) or $this->getField($fieldName) I would need to loop over the collection and test the fieldName value
For example
public function hasField($fieldName)
{
foreach($this->fields as $field) {
if ($fieldName === $field->getFieldName()) {
return true;
}
}
return false;
}
This, in my option, is a poor solution as I would need to load the entire collection to just check the key!
Having looked into the issue I can see that ORM has this implemented with IndexBy mapping.
Is there any similar functionality for Doctrine ODM? What is the correct way to accomplish this?
Have a look at the Collection Strategies.
If you use #ODM\ReferenceMany(strategy="set") (works with EmbedMany as well) the collection will be stored as as BSON Object with the respective keys being set on load:
I'm not entirely sure about performance impact in the DB itself. I guess storing the collection as BSON Object is a bit slower but as you said, it's not a good thing to load the entire collection (and the entities you're checking against) and it helps to keep the code a bit cleaner.

Querying by nested references values in mongodb / doctrine2 odm

Hello ive got the following code:
$primer = function($dm, $className, $fieldName, $ids, $hints) {
$repository = $dm->getRepository($className);
$qb = $repository->createQueryBuilder()
->field('id')->in($ids)
->field('images')->prime(true);
$query = $qb->getQuery();
$query->execute()->toArray();
};
$qb = $followRepo
->createQueryBuilder()
->field('isActive')->equals(true)
->field('target')->prime($primer)
->field('follower')->references($return['user'])
->field('target.$ref')->equals('boards')
->field('target.createdBy.type')->equals('user') // here i dont know how to handle this
->sort('created', 'desc')
Is it even possible in mongo to query via target.createdBy.type?
target.createdBy is also ref.
Yes, it is possible to query on sub document properties using the dot notation, as per
the official documentation. I am pretty sure it wont work with a referencing in 1 step though.

Doctrine 2 + Zend Form - Populate Dynamic Select Menus

I'm building a Zend form that has a dropdown/select menu populated with data from a Doctrine 2 query.
In my repository class, I have the following query in a method named selectUser():
$query = $em->createQuery('SELECT u.id, u.name FROM XX\Entity\Users u ORDER BY u.name ASC');
$users = $query->getResult();
This returns a multidimensional array, which I'm trying to loop through like this (within the same method):
$options = array();
foreach ($users as $key => $value) {
$options[$value['id']] = $value['name'];
}
return $options;
Then in my Zend form class, I try to populate the Select element like this:
$id = new Zend_Form_Element_Select('id');
$options = $this->usersRepository->selectUser();
$id->AddMultiOptions($options);
The result is an error for each user row that states "Undefined index: [name] in ...UsersRepository.php..." where [name] is the value of the 'name' column in each row.
Does anyone see what I'm doing wrong or how to populate a dynamic select menu using Doctrine 2 and Zend Framework?
(By the way, in order to run the repository method, the form class has protected properties representing the Doctrine container, entity manager, and Users repository. If this isn't considered best practice, I'd welcome any suggestions on improving my technique.)
I think your problem is here
$options[$value['id'] = $value['name']];
this would be better
$options[$value['id']] = $value['name'];