coen-hyde / Shanty-Mongo

Shanty Mongo is a mongodb library for the Zend Framework. Its intention is to make working with mongodb documents as natural and as simple as possible. In particular allowing embedded documents to also have custom document classes.
Other
200 stars 52 forks source link

Returning the correct class from a DBRef #73

Closed tonymillion closed 4 years ago

tonymillion commented 12 years ago

I have a notification system where numerous objects could create a notification as such:

protected static $_requirements = array(
    'object'        => array('AsReference'),
);

further in the code $this->object is set to the object creating the notification (could be one of 4 types (so far)).

in the database it looks like:

'object' => 
  array (
    '$ref' => 'users',
    '$id' => new MongoId("4f721182304fbef21c000001"),
  ),

however when I come to dereference the object using $notification->object its always returned as a Shanty_Mongo_Document and not as a user object. Is there a way to get Shanty to return the right type based on the _type in the user object?

coen-hyde commented 12 years ago

Ah interesting. Yes maybe we can create a custom db ref with a _type.

gwagner commented 12 years ago

I have committed this in a new patch (2c44e606b0aea834a3e3f7aa917580204ddb511d) but here is the code in the mean time to fix your problem:

In getProperty() in Shanty_Mongo_Document, add this code:

<?php
/* try and grab the class name from the data first */
if(!$className && !empty($data) && isset($data['_type']) && count($data['_type']) >= 1 && class_exists($data['_type'][0]))
    $className = $data['_type'][0];

to this block:

<?php
// Find out the class name of the document or document set we are loaded
if ($className = $this->hasRequirement($property, 'DocumentSet')) {
    $docType = 'Shanty_Mongo_DocumentSet';
}
else {
    $className = $this->hasRequirement($property, 'Document');

    /* try and grab the class name from the data first */
    if(!$className && !empty($data) && isset($data['_type']) && count($data['_type']) >= 1 && class_exists($data['_type'][0]))
        $className = $data['_type'][0];

    // Load a document anyway so long as $data is not empty
    if (!$className && !empty($data)) {
        $className = 'Shanty_Mongo_Document';
    }

    if ($className) $docType = 'Shanty_Mongo_Document';
}

Normally what you could do is set a requirement with

<?php
protected static $_requirements = array(
    'object'        => array('Document:ObjectType', 'AsReference'),
);

But in your case with your mutating document type, my patch above will detect the document type out of the result rather than from a config file

gwagner commented 12 years ago

Also, digging through the code, it seems that $docType is old and currently unused. Do you remember what the original purpose of $docType was for?

coen-hyde commented 12 years ago

Thanks gwagner, I forgot how Shanty Mongo worked. What you are supposed to do is:

<?php
protected static $_requirements = array(
  'object'        => array('Document:User', 'AsReference'),
);

There is a problem with this though. You wouldn't be able to store references to documents of different types.

gwagner commented 12 years ago

That is where my patch comes in, we can get the object type off of the data being returned before we instantiate a generic object

tonymillion commented 12 years ago

I use things like:

<?php
protected static $_requirements = array( 'object' => array('Document:User', 'AsReference'));

Quite heavily in my code to enforce type, but in this case needed the type to mutate, thus the assumption on using just AsReference, I'm about to test the fix @gwagner pasted here, and will let you know.

gwagner commented 12 years ago

Tony (if that is your real name) how did the code snippet work?

tonymillion commented 12 years ago

It is indeed my real name! And, yes, it worked perfectly, never got a chance to reply again last night. Thanks for the quick fix!

gwagner commented 12 years ago

Wonderful. I have the code pushed to my branch (with corrected test) waiting to be integrated into the mainline.