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 uasort to sort product data based on the added_at timestamp 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:

  1. As a duration for the JavaScript storage layer (expiry time).
  2. 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.

Start with the tools

Explore migration tools

See options, compare methods, and pick the path that fits your store.

Explore migration tools