Description
When a customer with the same email address has an account on different multi stores in the same Magento installation, changes to the newsletter subscription in one account affect the other.
Preconditions
- Magento 2.1.5
- PHP 7.0.16
- MySQL 5.7
- Apache 2.4
- At least two stores setup with nothing shared
- A customer exists in both stores with the same email address
Steps to reproduce
- Login with customer 1 in store A, go to My Account -> Newsletter Subscriptions, and make sure he's subscribed to the general newsletter
- Login with customer 2 in store B, go to My Account -> Newsletter Subscriptions, and make sure he's subscribed to the general newsletter
- Unsubscribe customer 1 from the newsletter
- Refresh the page for customer 2
Expected result
- Customer 1 is unsubscribed
- Customer 2 is subscribed
Actual Result
- Customer 1 is unsubscribed
- Customer 2 is unsubscribed
Extra info
When looking in the newsletter_subscriber table, instead of there being one record for each customer per store, there's one global record. When customer 1 in store A updates, the store_id in newsletter subscriber is updated to store A's ID. When customer 2 in store B updates, the store_id in newsletter_subscriber is updated to store B's ID.
Tracing the code out, I think the problem is in Magento_Newsletter\Model\ResourceModel\Subscriber.php, specifically the loadByCustomerData function.
Here's the function:
public function loadByCustomerData(\Magento\Customer\Api\Data\CustomerInterface $customer){
$select = $this->connection->select()->from($this->getMainTable())->where('customer_id=:customer_id');
$result = $this->connection->fetchRow($select, ['customer_id' => $customer->getId()]);
if ($result) {
return $result;
}
$select = $this->connection->select()->from($this->getMainTable())->where('subscriber_email=:subscriber_email');
$result = $this->connection->fetchRow($select, ['subscriber_email' => $customer->getEmail()]);
if ($result) {
return $result;
}
return [];
}
It basically uses the customerId as the lookup key but then falls back to the customerEmail if nothing is found. This creates the unintended consequence where updates in one store show in the other. A suggested fix would be to either only use the customerId or add an additional field to the select that matches the store_id of the customer, something like
select * from newsletter_subscriber where subscriber_email = $customer->getEmail() and store_id = $customer->getStoreId()