Magento 2 Customer Export Nightmare: When 'Admin' Customers Break Your CSVs

At Shopping Mover, we understand the critical importance of reliable data exports for any e-commerce operation, especially when considering or performing a Magento migration. A recent Magento 2 GitHub issue (magento/magento2#41289) has shed light on a significant bug that could halt your customer data exports, specifically impacting customers associated with the 'admin' website (website_id 0).

The Silent Killer: How Admin Customers Broke Your CSV Exports

Imagine trying to export your entire customer list, only for the process to crash with an obscure Undefined array key 0 error. This wasn't a rare occurrence but a systemic issue for Magento 2 stores with customers created via the admin panel or migrated from older installations, which often carry a website_id of 0.

The root of the problem lay deep within Magento's UI component data provider for customers. Specifically, the Magento\Customer\Ui\Component\DataProvider\Document::setWebsiteValue() method was tasked with resolving a customer's website name. It would retrieve a list of websites using StoreManager::getWebsites(). The critical flaw? By default, getWebsites() is called with $withDefault = false, which explicitly excludes the admin website (id = 0) from the returned list. Consequently, when a customer with website_id 0 was processed, attempting to access $list[0] resulted in an 'Undefined array key' error, which, thanks to Magento's error handling in app/bootstrap.php, escalated to an exception, aborting the entire CSV export.

What made this particularly frustrating was that the customer grid itself rendered normally. The issue only manifested during the export, affecting all rows even if only one customer had website_id 0.

The Two-Fold Solution: Restoring Export Integrity

The proposed fix addresses this issue in two crucial steps, demonstrating a deep understanding of Magento's core architecture and PHP's nuances:

Part 1: Including the Admin Website in the List

The primary fix involves modifying how the website list is retrieved and handled. Instead of relying on the default behavior, StoreManager::getWebsites() is now explicitly called with true to include the admin website:

$list = $this->storeManager->getWebsites(true);
$this->setCustomAttribute(
self::$websiteAttributeCode,
$value !== null && isset($list[$value]) ? $list[$value]->getName() : ''
);

This change ensures that website_id 0 is present in the $list array. Furthermore, a robust isset($list[$value]) check is added. This not only prevents issues with website_id 0 but also gracefully handles scenarios where a website might have been removed after a customer was created, or if website_id is null (a potential PHP 8.5 deprecation trap).

Part 2: Correctly Resolving Confirmation Scope

A secondary, more subtle bug emerged once the first was fixed. The setConfirmationValue() method, which determines the 'Confirmed email' status, was incorrectly resolving the website scope. Due to the column order in customer_listing.xml, the website_id column was processed first. After the first fix, for customers with website_id 0, the _data['website_id'] would hold the *label* 'Admin' instead of the ID 0.

The original code used the falsy-aware ?: operator:

$websiteId = $this->getData(self::$websiteIdAttributeCode) ?: $this->getData(self::$websiteAttributeCode);

Since original_website_id was 0 (falsy), ?: would fall through and use the 'Admin' label. This label was then incorrectly passed to Magento's scope resolver, leading to configuration values being read from an invalid scope (websites/Admin instead of website 0).

The fix involved switching to the null coalescing operator ??:

$websiteId = $this->getData(self::$websiteIdAttributeCode) ?? $this->getData(self::$websiteAttributeCode);

The ?? operator only falls back if the left-hand operand is null, not just falsy. This ensures that a valid 0 (for website_id 0) is correctly retained as the scope, resolving the 'Confirmed email' status accurately.

Impact for Merchants and Developers

This fix is invaluable. For merchants, it ensures the reliability of customer data exports, a fundamental operation for marketing, analytics, and CRM integrations. For developers, it highlights the intricate dependencies within Magento's core and the importance of thorough testing, especially when dealing with default values, scope resolution, and PHP version compatibility. It also serves as a reminder that website_id 0 is a legitimate state for customers and must be handled correctly.

Understanding such core fixes is crucial for maintaining a healthy Magento installation and for seamless migrations to newer versions or platforms. At Shopping Mover, we emphasize such details to ensure data integrity throughout your e-commerce journey.

Start with the tools

Explore migration tools

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

Explore migration tools