Skip to content

Commit 5581470

Browse files
committed
MAGETWO-56538: [GitHub] Minicart does not Synchronise Across Multiple Devices #5524
1 parent c17c5df commit 5581470

File tree

7 files changed

+164
-43
lines changed

7 files changed

+164
-43
lines changed

app/code/Magento/Customer/Block/CustomerData.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,23 @@
1010
*/
1111
class CustomerData extends \Magento\Framework\View\Element\Template
1212
{
13+
/**
14+
* @var array
15+
*/
16+
private $expirableSectionNames;
17+
1318
/**
1419
* @param \Magento\Framework\View\Element\Template\Context $context
20+
* @param array $expirableSectionNames
1521
* @param array $data
1622
*/
1723
public function __construct(
1824
\Magento\Framework\View\Element\Template\Context $context,
19-
array $data = []
25+
array $data = [],
26+
array $expirableSectionNames = []
2027
) {
2128
parent::__construct($context, $data);
29+
$this->expirableSectionNames = $expirableSectionNames;
2230
}
2331

2432
/**
@@ -43,4 +51,26 @@ public function getCustomerDataUrl($route)
4351
{
4452
return $this->getUrl($route, ['_secure' => $this->getRequest()->isSecure()]);
4553
}
54+
55+
/**
56+
* Retrieve lifetime period (in minutes) of the frontend section configuration.
57+
*
58+
* Once this period has expired the corresponding section must be invalidated and reloaded.
59+
*
60+
* @return int section lifetime in minutes
61+
*/
62+
public function getExpirableSectionLifetime()
63+
{
64+
return (int)$this->_scopeConfig->getValue('customer/online_customers/section_data_lifetime');
65+
}
66+
67+
/**
68+
* Retrieve the list of sections that can expire.
69+
*
70+
* @return array
71+
*/
72+
public function getExpirableSectionNames()
73+
{
74+
return array_values($this->expirableSectionNames);
75+
}
4676
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Customer\Test\Unit\Block;
7+
8+
use Magento\Customer\Block\CustomerData;
9+
use Magento\Framework\App\Config\ScopeConfigInterface;
10+
use Magento\Framework\View\Element\Template\Context;
11+
12+
class CustomerDataTest extends \PHPUnit_Framework_TestCase
13+
{
14+
/**
15+
* @var Context|\PHPUnit_Framework_MockObject_MockObject
16+
*/
17+
private $contextMock;
18+
19+
/**
20+
* @var ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject
21+
*/
22+
private $scopeConfigMock;
23+
24+
protected function setUp()
25+
{
26+
$this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class)->getMock();
27+
$this->contextMock = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock();
28+
$this->contextMock->expects($this->once())->method('getScopeConfig')->willReturn($this->scopeConfigMock);
29+
}
30+
31+
public function testGetExpirableSectionLifetimeReturnsConfigurationValue()
32+
{
33+
$block = new CustomerData(
34+
$this->contextMock,
35+
[],
36+
[]
37+
);
38+
39+
$this->scopeConfigMock->expects($this->once())
40+
->method('getValue')
41+
->with('customer/online_customers/section_data_lifetime', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null)
42+
->willReturn('10');
43+
44+
$actualResult = $block->getExpirableSectionLifetime();
45+
$this->assertSame(10, $actualResult);
46+
}
47+
48+
public function testGetExpirableSectionNames()
49+
{
50+
$expectedResult = ['cart'];
51+
$block = new CustomerData(
52+
$this->contextMock,
53+
[],
54+
$expectedResult
55+
);
56+
57+
$this->assertEquals($expectedResult, $block->getExpirableSectionNames());
58+
}
59+
}

app/code/Magento/Customer/etc/adminhtml/system.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,10 @@
290290
<label>Online Minutes Interval</label>
291291
<comment>Leave empty for default (15 minutes).</comment>
292292
</field>
293+
<field id="section_data_lifetime" translate="label comment" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
294+
<label>Customer Data Lifetime</label>
295+
<comment>Please specify value in minutes.</comment>
296+
</field>
293297
</group>
294298
</section>
295299
<section id="general">

app/code/Magento/Customer/etc/config.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@
9595
{{depend fax}}F: {{var fax}}|{{/depend}}|
9696
{{depend vat_id}}VAT: {{var vat_id}}{{/depend}}|]]></pdf>
9797
</address_templates>
98+
<online_customers>
99+
<section_data_lifetime>60</section_data_lifetime>
100+
</online_customers>
98101
</customer>
99102
</default>
100103
</config>

app/code/Magento/Customer/etc/frontend/di.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,11 @@
7070
</argument>
7171
</arguments>
7272
</type>
73+
<type name="Magento\Customer\Block\CustomerData">
74+
<arguments>
75+
<argument name="expirableSectionNames" xsi:type="array">
76+
<item name="cart" xsi:type="string">cart</item>
77+
</argument>
78+
</arguments>
79+
</type>
7380
</config>

app/code/Magento/Customer/view/frontend/templates/js/customer-data.phtml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
<?= /* @noEscape */ $this->helper(\Magento\Framework\Json\Helper\Data::class)->jsonEncode([
1111
'*' => ['Magento_Customer/js/customer-data' => [
1212
'sectionLoadUrl' => $block->getCustomerDataUrl('customer/section/load'),
13+
'expirableSectionLifetime' => $block->getExpirableSectionLifetime(),
14+
'expirableSectionNames' => $block->getExpirableSectionNames(),
1315
'cookieLifeTime' => $block->getCookieLifeTime(),
1416
'updateSessionUrl' => $block->getCustomerDataUrl('customer/account/updateSession'),
1517
]],

app/code/Magento/Customer/view/frontend/web/js/customer-data.js

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -194,17 +194,24 @@ define([
194194
*/
195195
init: function () {
196196
var countryData,
197-
privateContent = $.cookieStorage.get('private_content_version');
197+
privateContent = $.cookieStorage.get('private_content_version'),
198+
expiredSectionNames;
198199

199200
if (_.isEmpty(storage.keys())) {
200201
if (!_.isEmpty(privateContent)) {
201202
this.reload([], false);
202203
}
203-
} else if (this.needReload()) {
204+
205+
return;
206+
}
207+
208+
expiredSectionNames = this.getExpiredSectionNames();
209+
210+
if (expiredSectionNames.length > 0) {
204211
_.each(dataProvider.getFromStorage(storage.keys()), function (sectionData, sectionName) {
205212
buffer.notify(sectionName, sectionData);
206213
});
207-
this.reload(this.getExpiredKeys(), false);
214+
this.reload(expiredSectionNames, false);
208215
} else {
209216
_.each(dataProvider.getFromStorage(storage.keys()), function (sectionData, sectionName) {
210217
buffer.notify(sectionName, sectionData);
@@ -225,57 +232,66 @@ define([
225232
},
226233

227234
/**
228-
* @return {Boolean}
235+
* Retrieve the list of sections that has expired since last page reload.
236+
*
237+
* Sections can expire due to lifetime constraints or due to inconsistent storage information
238+
* (validated by cookie data).
239+
*
240+
* @return {Array}
229241
*/
230-
needReload: function () {
231-
var cookieSections = $.cookieStorage.get('section_data_ids'),
232-
storageVal,
233-
name;
234-
235-
if (typeof cookieSections != 'object') {
236-
return true;
237-
}
242+
getExpiredSectionNames: function () {
243+
var expiredSectionNames = [],
244+
cookieSectionTimestamps = $.cookieStorage.get('section_data_ids') || {},
245+
sectionLifetime = options.expirableSectionLifetime * 60,
246+
currentTimestamp = Math.floor(Date.now() / 1000),
247+
sectionData;
248+
249+
// process sections that can expire due to lifetime constraints
250+
_.each(options.expirableSectionNames, function (sectionName) {
251+
sectionData = storage.get(sectionName);
252+
253+
if (typeof sectionData === 'object' && sectionData['data_id'] + sectionLifetime <= currentTimestamp) {
254+
expiredSectionNames.push(sectionName);
255+
}
256+
});
238257

239-
for (name in cookieSections) {
240-
if (name !== undefined) {
241-
storageVal = storage.get(name);
258+
// process sections that can expire due to storage information inconsistency
259+
_.each(cookieSectionTimestamps, function (cookieSectionTimestamp, sectionName) {
260+
sectionData = storage.get(sectionName);
242261

243-
if (typeof storageVal === 'undefined' || //eslint-disable-line max-depth
244-
typeof storageVal == 'object' && cookieSections[name] > storageVal['data_id']
245-
) {
246-
return true;
247-
}
262+
if (typeof sectionData === 'undefined' ||
263+
typeof sectionData === 'object' &&
264+
cookieSectionTimestamp != sectionData['data_id'] //eslint-disable-line
265+
) {
266+
expiredSectionNames.push(sectionName);
248267
}
249-
}
268+
});
250269

251-
return false;
270+
return _.uniq(expiredSectionNames);
252271
},
253272

254273
/**
274+
* Check if some sections have to be reloaded.
255275
*
256-
* @return {Array}
276+
* @deprecated Use getExpiredSectionNames instead.
277+
*
278+
* @return {Boolean}
257279
*/
258-
getExpiredKeys: function () {
259-
var cookieSections = $.cookieStorage.get('section_data_ids'),
260-
storageVal,
261-
name,
262-
expiredKeys = [];
263-
264-
if (typeof cookieSections != 'object') {
265-
return [];
266-
}
267-
268-
for (name in cookieSections) { //eslint-disable-line guard-for-in
269-
storageVal = storage.get(name);
280+
needReload: function () {
281+
var expiredSectionNames = this.getExpiredSectionNames();
270282

271-
if (typeof storageVal === 'undefined' ||
272-
typeof storageVal == 'object' && cookieSections[name] != storage.get(name)['data_id'] //eslint-disable-line
273-
) {
274-
expiredKeys.push(name);
275-
}
276-
}
283+
return expiredSectionNames.length > 0;
284+
},
277285

278-
return expiredKeys;
286+
/**
287+
* Retrieve the list of expired keys.
288+
*
289+
* @deprecated Use getExpiredSectionNames instead.
290+
*
291+
* @return {Array}
292+
*/
293+
getExpiredKeys: function () {
294+
return this.getExpiredSectionNames();
279295
},
280296

281297
/**

0 commit comments

Comments
 (0)