leroy-merlin-br / mongolid-laravel

Easy, powerful and ultrafast MongoDB ODM for Laravel.
MIT License
240 stars 41 forks source link

AbstractModel::first returns generic object? #130

Closed bradleygreen closed 5 years ago

bradleygreen commented 5 years ago

Our implementation has new classes for each kind of mongo document. For example, I have a collection called "products", and we have a class called "Product" that extends Mongolid. (we create an alias in config/app.php: 'MongoLid' => Mongolid\Laravel\Model::class,)

When I request a document from the collection App::make("Product")->first() The object that is returned has no class methods. It appears to be a standard object, with seemingly all the data from the document, but without the Product class methods, nor the AbstractModel methods.

Is that the expected nature of the first() function? The Mongolid-laravel we have been using (zizaco), returns an instance of the Product model, complete with data and methods.

I admittedly don't understand the flow of the abstract model, is this a bug or my ignorance that is giving me grief?

ravanscafi commented 5 years ago

@bradleygreen, a lot of things has changed. In the past, we use to have a Document Assembler that would take an array from the database and then build an model. We took other directions to allow direct object unserialization from database. To fix your collections, you now need to have a __pclass key on each document telling which class it belongs to.

Note: try this with a test database, not directly on production or some sensitive data!

// make sure to replace both occurrences of `\My\Namespace\Product::class` with your Product class

app(\My\Namespace\Product::class)->getCollection()->updateMany(
    [],
    [
        '$set' => [
            '__pclass' => new \MongoDB\BSON\Binary(\My\Namespace\Product::class, \MongoDB\BSON\Binary::TYPE_USER_DEFINED)
        ]
    ]
);

now, if you do:

\My\Namespace\Product::first();

it should work.

Basically you will need to do this to all of your collections.

You can leverage migrations to do this on production/etc:

php artisan make:mongolid-migration change_product_pclass

and create the logic, then run

php artisan mongolid-migrate

Refer to this documentation to learn more about __pclass and mongodb serialization/unserialization.

I hope that it helps!

bradleygreen commented 5 years ago

Thank you for your response. We have been able to successfully create a test collection and update it to include __pclass. That test class did then work the way we are accustomed to using our Mongolid models.

We are concerned about implementation in one area. Will you please address it? How should we implement a class type for classes that sometimes (but not always) extend other classes (inheritance)?

As an example, we created a collection called 'products'. Its namespace is MyApp\Catalog\Product. We then created a new website, using the same products collection, but that has a different getPrice() method so it could use different rules for the different site. So we created MyApp\Template\Product, which 'extends' MyApp\Catalog\Product and has only one method in it, but benefits from the methods on its parent class. We add the Template directory after the other directories in our auto-loader, so those classes get loaded instead of the base classes. We can then call either class, based on whether the Template namespace exists, by calling App::make("Product").

Can we add an alias instead of a namespace? How can we address class inheritance?

ravanscafi commented 5 years ago

Awesome! I was wondering if you managed to use it :) There is a little friction migrating from v2, but it is worth it.

About "inheritance", we do have a concept that I think might work for you. It's what we call PolymorphableModel. Please take a look at PolymorphableModelInterface.

Basically, you make your base Product implement it, creating a polymorph model which will tell which class mongolid must use for your model. There are some tests that might help understanding the concept.

We really need to update the documentation :wink:

Please tell me about your progress!