Magento 2's Hidden Flaw: Why 'Recently Viewed Products' Might Be Showing the Oldest Items
Unmasking a Hidden Flaw in Magento 2's Recently Viewed Products Logic
The 'Recently Viewed Products' feature is a cornerstone of e-commerce user experience, guiding shoppers back to items of interest and often contributing to conversion rates. However, a recent discovery within the Magento 2.4.8 core code revealed a subtle yet significant bug that could be undermining this functionality, causing stores to display the oldest viewed items rather than the newest. This insight delves into GitHub issue #41010, detailing how a core synchronizer method was inadvertently preserving historical data over recent interactions.
The Core Problem: Oldest Actions Persist, Not Newest
The issue, reported by Amadeco, centers on the Synchronizer::filterNewestActions() method within Magento\Catalog\Model\Product\ProductFrontendAction\Synchronizer. Despite its name, this method was found to be retaining the oldest product actions rather than the newest. The root cause lies in its sorting and slicing logic:
- The method uses
uasortto sort product data based on theadded_attimestamp in ascending order. This means the oldest entries come first. - Subsequently,
array_slice($productsData, 0, $actionsNumber, true)is used to extract a subset of the sorted array, taking elements from the start.
The combination of an ascending sort and slicing from the beginning inevitably results in the oldest actions being preserved, directly contradicting the method's intended purpose and the concept of 'recently viewed'.
Here's a simplified reproduction of the core logic demonstrating the bug:
$data = [
'old' => ['added_at' => 1000, 'product_id' => 1],
'middle' => ['added_at' => 2000, 'product_id' => 2],
'newest' => ['added_at' => 3000, 'product_id' => 3],
];
uasort($data, fn(array $a, array $b) => $a['added_at'] <=> $b['added_at']);
$kept = array_slice($data, 0, 1, true);
var_dump(array_keys($kept)); // Actual result: ["old"]
Impact: Latent Until Configuration Changes
Under default Magento 2 configurations, this bug remains largely latent. The $actionsNumber variable, derived from catalog/recently_products/recently_viewed_lifetime (defaulting to 1000) and TIME_TO_DO_ONE_ACTION (1), typically results in a large enough number that the slice operation truncates nothing. Thus, the incorrect order isn't immediately visible.
However, the problem becomes critical for any merchant who lowers the recently_viewed_lifetime setting. For instance, if this value is set to 5, a shopper viewing 10 products will have the first 5 products they looked at persisted in the catalog_product_frontend_action table, rather than the 5 most recent. This directly breaks the user experience and the expected behavior of a 'recently viewed' block.
Secondary Observation: Dual-Purpose Configuration Value
The issue also highlights an interesting secondary observation: the recently_viewed_lifetime configuration value serves two distinct purposes:
- As a duration for the JavaScript storage layer (expiry time).
- As a cardinality (row count) for the
filterNewestActions()method.
This dual usage means that lowering the lifetime to tune client-side storage expiry silently also tightens the number of actions persisted server-side, which is unlikely to be the intended behavior for merchants.
The Proposed Solution
The fix for the primary issue is straightforward and elegant: simply reverse the sorting order within the uasort callback. By sorting in descending order, the newest actions will appear at the beginning of the array, allowing the subsequent slice operation to correctly retain them.
return ($secondProduct['added_at'] <=> $firstProduct['added_at']); // descending
This change alone resolves the naming/behavior mismatch. Decoupling the row count from the lifetime would be a larger, separate architectural change, but the immediate bug can be fixed with this targeted adjustment.
Community Insight and Conclusion
This issue serves as a prime example of a well-documented bug report within the Magento community. While direct discussions from comments were not provided in the source, the clarity of the issue description, detailed reproduction steps, and a precise suggested fix demonstrate the proactive nature of developers in identifying and addressing core platform inconsistencies. Such contributions are invaluable for maintaining the stability and reliability of Magento 2, ensuring that features like 'Recently Viewed Products' function as expected for both merchants and their customers. For developers, understanding such nuances in core modules is crucial for debugging, custom development, and ensuring seamless migrations and upgrades.