Is there ever a reason to prefer $model->load() over service contracts?

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.


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.


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.