Helo,
firstly, please excuse my English, is not very good.
I am migrating the data container of a Symfony2 application to MongoDB, before that it run with MySQL.
I added the DoctrineMongoDBBundle and "almost everything" works perfectly.
I have some references between documents in which I would like to keep the "lazy load" pattern offered by the Doctrine ORM. I have read the official documentation for Doctrine ODM,
and some examples that explain how to create relations and define documents to get "lazy load" behavior,
but I can not make it work.
In my case, I have two documents, "travel" and "note" with a 1:N relationship I want to keep, something like this:
<?php
namespace MyApp\TravelBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* Travel
*
* @ODM\Document(collection="travel")
*/
class Travel {
/**
* @var \MyApp\NoteBundle\Document\Note
*
* @ODM\ReferenceMany(targetDocument="\MyApp\NoteBundle\Document\Note", mappedBy="travel", sort={"createdAt"="asc"} )
*/
private $notes;
// more properties ...
public function __construct() {
$this->notes = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add notes
*
* @param \MyApp\NoteBundle\Document\Note $notes
*/
public function addNote(\MyApp\NoteBundle\Document\Note $notes) {
$this->notes[] = $notes;
}
/**
* Remove notes
*
* @param \MyApp\NoteBundle\Document\Note $notes
*/
public function removeNote(\MyApp\NoteBundle\Document\Note $notes) {
$this->notes->removeElement($notes);
}
/**
* Get notes
*
* @return Doctrine\Common\Collections\Collection $notes
*/
public function getNotes() {
return $this->notes;
}
// more methods ...
}
?>
<?php
namespace MyApp\NoteBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Symfony\Component\Validator\Constraints as Assert;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* Note
*
* @ODM\Document(collection="note")
*/
class Note
{
/**
* @var \MyApp\TravelBundle\Document\Travel
*
* @ODM\ReferenceOne(targetDocument="MyApp\TravelBundle\Document\Travel", inversedBy="notes")
*/
private $travel;
// more properties ...
/**
* Set travel
*
* @param \MyApp\TravelBundle\Document\Travel $travel
* @return Note
*/
public function setTravel(\MyApp\TravelBundle\Document\Travel $travel) {
$this->travel = $travel;
$travel->addNote($this);
return $this;
}
// more methods ...
}
?>
When I add a note to a travel I understand that the result for the travel document should be:
{ "_id" : ObjectId( "5183aa63095a1a3921000000" ),
"name" : "First travel",
"isActive" : true,
"createdAt" : Date( 1367583331000 ),
"updatedAt" : Date( 1367583331000 ),
"notes" : [{ "$ref" : "note",
"$id" : ObjectId( "5183aa63095a1a3955000000" ),
"$db" : "mydb" }]
}
and for the note document should be:
{ "_id" : ObjectId( "5183aa63095a1a3955000000" ),
"travel" : { "$ref" : "travel",
"$id" : ObjectId( "5183aa63095a1a3921000000" ),
"$db" : "mydb" },
"note" : "First note",
"createdAt" : Date( 1367583331000 ),
"updatedAt" : Date( 1367583331000 ) }
but for now I only get a reference in the note document, whilst no reference appears in the travel document, and when I do a query in the travel document Doctrine do not load the related note documents:
<?php
.
.
$travel = $dm->getRepository('TravelBundle:Travel')->findCurrentTravel($user->getId());
$travel->getNotes(); // IS EMPTY :(
.
.
?>
The process I follow to add a note to a travel is as follows:
<?php
namespace MyApp\TravelBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class TravelController extends Controller {
public function createNoteAction(Request $request) {
$dm = $this->get('doctrine.odm.mongodb.document_manager');
$travel = $dm->getRepository('TravelBundle:Travel')->findCurrentTravel($user->getId());
$entity = new Note();
$form = $this->createForm(newNoteType(), $entity);
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
$entity->setTravel($travel);
$dm>persist($travel);
$dm>persist($entity);
$dm>flush();
}
}
}
}
?>
Any ideas or suggestions to get the method $travel->getNotes() can automatically retrieve referenced notes through the "lazy load".
Thank you very much beforehand for the contributions,
Zacarías
What you want to achieve is done by simply removing the
mappedBy
attribute in theReferenceOne
of the$travel
property:In this way doctrine will store the Notes IDs in the
$nodes
array.With "mappedBy" instead, Doctrine doesn't store the IDs of the Notes in the
$notes
array, but instead it will do a query like this to fetch the actual notes:Note that IMHO this one is the preferred approach, since in this way when you add/remove a note, you don't have to update the Travel doc. (however you'll have to add an index on the $travel field)
Also notice that with
ReferenceMany
, using themappedBy
is lazy: only when you try to cycle the$notes
array it will actually execute the query, so it is also lightweight.See the doc for more info.