1

I have a Company class that references users:

/**
 * @MongoDB\Document()
 */
class Company {

    /* ... */

    /**
     * @MongoDB\ReferenceMany(targetDocument="Topboard\UserBundle\Document\User", inversedBy="companies")
     */
    protected $users;
}

In my controller I need to check if the reference to the user exists in the company and keep only the reference to that user, not other references. I also want to avoid multiple DB requests for users. I just want to check if the id of the references matches $currentUserId.

public function getCompanyAction($companyId, $currentUserId) {
    $dm = $this->get('doctrine_mongodb')->getManager();
    $company = $dm->getRepository( 'TopboardAppBundle:Company' )->findOneById( $companyId );

    foreach ($company->getUsers() as $user) {
        // Foreach will query each user separetly. This is not what I need.
        // I need somehow access array of references' ids
        // and compare them with the $currentUserId
        if($user->getId() !== $currentUserId){
            // Remove reference
        }        
    }

    return $company;

}
Websirnik
  • 1,372
  • 3
  • 21
  • 35

1 Answers1

1

Upon investigation it turned out that the query is fired when collection is initialized to avoid one query per document later (for reasoning why we can't do better yet see this comment on GH). The case is not lost though, solution is not anywhere near being beautiful, but sometimes OD/RMs requires these when performance needs to come first:

$users = $company->getUsers();
// condition below assumes $company is loaded from db so $users is in fact instance of PersistentCollection(Interface)
if ($users->isInitialized()) {
   $ids = $users->map(function($user) { 
       return $user->getId(); 
   })->toArray();
} else {
   $ids = array_map(function($dbRef) { 
        /* this depends on reference type */ 
        return (string) $dbRef['$id']; 
   }, $users->getMongoData());
}

You could also just place your logic in places where I'm mapping collections to get list of ids of referenced users.

Original answer that is relevant for single references or unitinitailzed proxies

If object is not loaded yet (i.e. it is still uninitialized Proxy) then asking for that document's identifier will not trigger additional queries, here is snippet of a Proxy class generated by ODM in my project:

public function getId()
{
    if ($this->__isInitialized__ === false) {
        return  parent::getId();
    }


    $this->__initializer__ && $this->__initializer__->__invoke($this, 'getId', []);

    return parent::getId();
}

Also to expand answer a bit, you can prevent n+1 problem with priming, this way ODM will fetch all referenced documents in one query.

malarzm
  • 2,831
  • 2
  • 15
  • 25
  • Proxy sounds about right. So you suggest to change the id getter in the User class? I think the issue is that when I do `foreach`, it already triggers a DB request. – Websirnik Jun 29 '16 at 13:23
  • No, your getter should remain intact, you can check autogenerated Proxy class (they're usually in `app/cache/lcl/doctrine/odm/mongodb/Proxies/`). As for `foreach`, it shouldn't trigger query on its own since your reference is the owning side thus already available in the document after initial fetch. – malarzm Jun 29 '16 at 13:39
  • What I mean is, your code looks correct and shouldn't trigger any additional queries at this point – malarzm Jun 29 '16 at 13:43
  • So `$company->getUsers()` should not trigger the db query? It does for me atm. – Websirnik Jun 29 '16 at 13:52
  • Sounds like a bug in ODM then, would you mind creating a failing test case and submitting it as a pull request? – malarzm Jun 29 '16 at 13:56