credits: hz536n/George Thomas Bokeh Swingset Chains via photopin (license). posted on December 20th, 2016.

How to get the Categories linked to a Product in Magento 2

Using Service Contracts over Collections

This might seem like a no-brainer at first. I mean, come on! We all know we can just do $product->getCategoryIds() and move on from there right? You know, create a collection and stuff... But beware! This is not 'The Magento Way' on how to do this.

I've recently posted an interesting question on Stack Exchange, which more or less confirmed what I have been suspecting for a while now: That collections are going to be phased out in favour of Data Models and Service Contracts.

So what's the proper way to do this with Data Models and Service Contracts you might ask? Well, since the Product Data Model has no method getCategoryIds() we need to fetch it on a different way. Lucky for us, Data Models (or at least most of them) have a method called getCustomAttribute() which allows you to load attributes set by other sources.

Magento 2 utilizes this function to store the value of the attribute category_ids. So if we want to fetch category ID's, we can use:

$categoryIds() = $product->getCustomAttribute('category_ids')->getValue();

From here the rest can be easily done with repositories like most of you are already used to by now. The full example could look somewhat like this:

// Get a product Data Model from the repository:
$product = $this->productRepository->getById($productId);

// Fetch the 'category_ids' attribute from the Data Model.
if ($categoryIds = $product->getCustomAttribute('category_ids')) {
    foreach ($categoryIds->getValue() as $categoryId) {
        $category = $this->categoryRepository->get($categoryId);

So there you have it. No longer depend on collections, but always try to solve it with the functionality provided by the various Service Contracts.

Under the hood

'But wait!' you might say. 'Under water the repository still uses collections to fetch and filter it's data! So why can't we?'. Well the whole reason that it's done like this under the hood is because Magento is still in the middle of refactoring lots of it's code, but also figuring out what's the proper way to do it. Some might say that the upcoming Entity Manager will solve most of this, but even now this feature is still experimental and therefore discouraged to use in a production environment.

The best approach for now is to rely on Service Contracts, since their interfaces are abstractions of their implementations. Therefore it doesn't matter how it's fetched from the database or any other source. It could be bare SQL-queries or plain text files for as far as you're concerned (I sure hope not though).

Working with Service Contracts makes the underlying business logic hot-swappable with anything else, without modifying the interface nor the results. Remember that when you're writing your own modules or need functionality from other modules, but also when you're writing your own Service Contracts for other modules to use.

Categories Magento 2 Repositories Service Contracts