23

I understand that the preferred way to work in between modules in Magento 2 is using the service contracts.

So if I want to load a product, I use the product repository:

$product = $productRepository->getById($id);

which is by contract returning an instance of Magento\Catalog\Api\Data\ProductInterface.

But I could also use the old way instead, calling the domain layer directly:

$product = $productFactory->create()->load($id);

Is there any case where this would be necessary or useful?

The devdocs say (highlight added):

A module can directly call into another module. This tightly coupled solution is not recommended for most situations, but is sometimes unavoidable.

[...]

Your strategy for calling another module’s domain-layer code is highly dependent upon the unique configuration and needs of your system.

Source: http://devdocs.magento.com/guides/v2.0/architecture/archi_perspectives/domain_layer.html

And a comment on a related question stated:

using the Repository will give you a Product data model (Api/Data/Product), which is a Product model converted into a dumbed-down DTO. Something to consider, as they're quite different

But as far as I can see the objects are the same under normal conditions, just the return types per phpDoc differ (Magento\Catalog\Api\Data\ProductInterface / Magento\Catalog\Model\Product)

21

The reason to use ProductRepository's get/getById instead of a ProductFatory's load() method, is because the former is of a higher level than the latter.

A ProductRepository - just like a ProductFactory - might return a Product model, but that is not what M2 wants you to consider. That's not what \Magento\Catalog\Api\ProductRepositoryInterface::getById()'s doc block says. It says @return \Magento\Catalog\Api\Data\ProductInterface, which is an interface a Product model is implementing.

SO, you should use the API layer whenever possible, because:

  • Api/Data layer is used in the Web Api, as well
  • models can - and probably will - be refactored at some point; Api/Data/Product will not.
  • To get a product in your classes, you need to inject either a concrete factory (ProductFactory) or an interface (ProductRepository). I don't think you want your module to rely on anything but an interface. Hence I disagree with this type of injection.

I consider it to be just another tiny abstraction layer above models, to cater for the Web API (REST, SOAP etc.).

Quoting this answer:

Hopefully, you will love service contracts when your custom module will not be broken after the next releases of Magento 2 (of course if you do not bypass service contracts and use models/collections/resource models directly). And when you start consuming/exposing Magento 2 web API, which is now based on the same service contracts. So you have to make changes only in one place (e.g. via plugin) and they will be applied everywhere. This was impossible in Magento 1.

  • Not exactly what I was asking for but that's what I thought as well, thanks for the confirmation! – Fabian Schmengler Apr 18 '16 at 11:57
  • 1
    But I could also use the old way instead, calling the domain layer directly: (use factory). Is there any case where this would be necessary or useful?. Yes: when you need to call a model's method and not an Api/Data/Product's one. Is this better? :) – nevvermind Apr 18 '16 at 12:59
  • Yes, that makes sense :) – Fabian Schmengler Apr 18 '16 at 13:03
14

To me, there is no reason to use the load method over the getById / get method.

I don't say that I'm right but here's how I see things.

Ok so here's the getById method (get method is similar but uses the sku instead of the id):

public function getById($productId, $editMode = false, $storeId = null, $forceReload = false)
{
    $cacheKey = $this->getCacheKey(func_get_args());
    if (!isset($this->instancesById[$productId][$cacheKey]) || $forceReload) {
        $product = $this->productFactory->create();
        if ($editMode) {
            $product->setData('_edit_mode', true);
        }
        if ($storeId !== null) {
            $product->setData('store_id', $storeId);
        }
        $product->load($productId);
        if (!$product->getId()) {
            throw new NoSuchEntityException(__('Requested product doesn\'t exist'));
        }
        $this->instancesById[$productId][$cacheKey] = $product;
        $this->instances[$product->getSku()][$cacheKey] = $product;
    }
    return $this->instancesById[$productId][$cacheKey];
}

As you can notice the code you pasted:

$productFactory->create()->load($id);

Is part of this function.

However, the extra condition uses cached instances to avoid an extra reload in case you have previously used the getById or the get method for the same id (or sku in case of the get method).

You may think that a good reason to use load could be to avoid using those cached instances (in which case could that be a good reason ? that I don't know) but the getById and the get methods have a $forceReload parameter that can be set to true to avoid using those cache instances.

That's why to me, there's no good reason to use load method over getById or get methods.

2

Please understand the difference between repositories and collections.

In your example, if using repositories, you will get an array of Magento\Catalog\Api\Data\ProductInterface that is different from getting a collection of Magento\Catalog\Model\Product.

Repositories and data interface give you an high interface level that should be guaranteed as compatible in future relases. This is why it is the suggested approach.

Hope it helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.