Depending how a product was loaded in Magento 2, not all of the product attributes are available on the object. This can be quite frustrating and lead to a development road block – the value of the attribute is required but it’s not there. This can occur for various reasons, the most common being that the product was loaded from an object like quite_address_item
which is a more simplified product object. If the product attribute comes from a custom module / feature, there is a chance the attribute will not be on this product object.
There are ways to overcome this – some more efficient than others. It seems the biggest go-to for developers is to just load the entire product object from the catalog. This is handy as it does have all of the data that may be needed. However, this is a costly practice – as is outlined below – as it takes more time to load. In ecommerce a fast page load time is critical for customer experience, customer confidence, and SEO among other reasons. Read on to identify the best solution for each scenario.
NOTES:
- The examples below track the performance of each method by measuring how many microseconds it took to complete the operation.
- The examples do use the
objectManager
for ease of testing and to make them more universial for others to apply this for their own testing. It is a Magento 2 best practice to avoid usingobjectManager
in code and thus any neccessary objects should be properly loaded via the dependency injection__construct
method. - The loading of the
objectManager
is excluded the processing time calculations.
Contents
Magento 2 Methods for Fetching Product Attribute Data
There are several methods to fetch a products attribute data. Keep in mind this is only needed in those off-situations where the product attribute needed isn’t already part of the product object. Read on to learn about the best methods for various situations and the potential cost (time to process) that each solution may incur.
Load Product via Model
A product can be loaded by its product_id
using the Catalog Product model. This should load all the product attributes to the product object. The desired product attribute can be fetched from that object.
1 2 3 4 5 6 7 8 9 | $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $start_time = microtime(true); // DEBUG $productFromOM = $objectManager->get('Magento\Catalog\Model\Product')->load($product->getId()); $billingFrequency = $productFromOM->getBillingFrequency(); $end_time = microtime(true); // DEBUG error_log( "execution_time Product Model: " . ($end_time - $start_time) . PHP_EOL, 3, "/path/to/debug.log" ); |
Load Product via Product Repository
Using the Magento Catalog Product Repository a product can be loaded at a specific store ID. In a multi-store implementation of Magento 2 this would be required to ensure that the store-specific value of the product attribute is used. Depending on the scope set on the product attribute desired however, this might not matter. If the product attribute’s scope is Global
it doesn’t matter what store ID you pull – it’s the same value for all store ID’s. That said it could be confusing for future development if a developer is expecting all of the store-specific data to be available and it isn’t…
1 2 3 4 5 6 7 8 9 | $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $start_time = microtime(true); // DEBUG $productFromOM2 = $objectManager->get('\Magento\Catalog\Model\ProductRepository')->getById($product->getId(),false,$product->getStoreId()); $billingFrequency = $productFromOM2->getBillingFrequency(); $end_time = microtime(true); // DEBUG error_log( "execution_time Product Repository: " . ($end_time - $start_time) . PHP_EOL, 3, "/path/to/debug.log" ); |
Load Product Attribute Value Directly
A product attribute can be loaded directly with the getAttributeRawValue
method which does not load the full product. Loading less data results in a quicker processing time. If only a few product attributes values are needed, this is a very efficient way to approach the problem. This method loads the attribute data for a specific store ID which makes this a suitable solution for a multi-store Magento 2 installation.
This method does require a resource (database) connection object. The resource connection object from the product object can be used – requiring less code to be loaded and processed. If only a product ID is available and there is no resource connection, then it would need to be loaded manually.
1 2 3 4 5 6 | $start_time = microtime(true); // DEBUG $billingFrequency = $product->getResource()->getAttributeRawValue($product->getId(),'billing_frequency',$product->getStoreId()); $end_time = microtime(true); // DEBUG error_log( "execution_time Attribute: " . ($end_time - $start_time) . PHP_EOL, 3, "/path/to/debug.log" ); |
If a product object isn’t available and thus the resource connection isn’t already loaded, then that needs to be loaded first. Here is an example of that using product and store IDs.
1 2 3 4 5 6 7 8 9 10 11 12 13 | $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $productId = 12345; $storeId = 2; $start_time = microtime(true); // DEBUG $resourceConnection = $objectManager->get('Magento\Catalog\Model\ResourceModel\Product\Interceptor'); $billingFrequency = $resourceConnection->getAttributeRawValue($productId,'billing_frequency',$storeId); $end_time = microtime(true); // DEBUG error_log( "execution_time Product Model: " . ($end_time - $start_time) . PHP_EOL, 3, "/path/to/debug.log" ); |
Time Trial Comparison of Methods
Magento\Catalog\Model\Product | \Magento\Catalog\Model\ProductRepository | getAttributeRawValue | getAttributeRawValue with Loading Resource | |
---|---|---|---|---|
Trial 1 | 0.029865980148315 | 0.054730892181396 | 0.00081586837768555 | 0.0010240077972412 |
Trial 2 | 0.022938013076782 | 0.02375602722168 | 0.0010089874267578 | 0.0011839866638184 |
Trial 1 | 0.022112846374512 | 0.022986888885498 | 0.00074100494384766 | 0.00078296661376953 |
Trial 4 | 0.022199869155884 | 0.022958993911743 | 0.00098681449890137 | 0.0011329650878906 |
Average | 0.024279177188873 | 0.031108200550079 | 0.000888168811798 | 0.0010309815406799 |
The time to process each method all turn out to be fractions of a second. While this might not feel like it’ll have much impact either way, keep in mind how this compounds on top of it self with all of the queries that Magento has to run to build one page. And these tests were done on a development server with no load/traffic. On a server with high traffic under load this will compound and impact the customer experience. Shaving fractions of seconds at every step of code does matter!
This table shows how much slower the other methods compared to the getAttributeRawValue
method.
getAttributeRawValue | getAttributeRawValue with Loading Resource | Magento\Catalog\Model\Product | \Magento\Catalog\Model\ProductRepository | |
---|---|---|---|---|
getAttributeRawValue | -- | -13.85% | -96.34% | -97.14% |
getAttributeRawValue with Loading Resource | 16.08% | -- | -95.75% | -96.69% |
Magento\Catalog\Model\Product | 2,633.62% | 2,254.96% | -- | 28% |
\Magento\Catalog\Model\ProductRepository | 3,402.51% | 2,917.34% | 28.13% | -- |
getAttributeRawValue
methodMath Equation: 100 * ( ( final -
initial
) / initial
)
Summary
To summarize, typically the getAttributeRawValue
method – with our without loading the resource connection – is the most efficient method to load product attribute data. If multiple product attributes are needed, it could maybe be argued that loading the product object directly would be more efficient, but there is a balancing point where that is true or false depending on how many product attributes are needed. Use the information of this article to choose the best method for the development project.