Skip to content

Commit 1a70516

Browse files
author
valdislav
committed
MAGETWO-35632: Refactor queries for Magento\Reports\Model\Resource\Quote\Collection
1 parent b20ef1c commit 1a70516

File tree

3 files changed

+241
-63
lines changed

3 files changed

+241
-63
lines changed

app/code/Magento/Reports/Block/Adminhtml/Shopcart/Product/Grid.php

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,7 @@ protected function _construct()
5757
protected function _prepareCollection()
5858
{
5959
$collection = $this->_quotesFactory->create();
60-
if ($this->queryResolver->isSingleQuery()) {
61-
$collection->prepareForProductsInCarts();
62-
$collection->setSelectCountSqlType(
63-
\Magento\Reports\Model\Resource\Quote\Collection::SELECT_COUNT_SQL_TYPE_CART
64-
);
65-
} else {
66-
$collection->prepareActiveCartItems();
67-
}
60+
$collection->prepareActiveCartItems();
6861
$this->setCollection($collection);
6962
return parent::_prepareCollection();
7063
}
@@ -80,6 +73,7 @@ protected function _prepareColumns()
8073
'header' => __('ID'),
8174
'align' => 'right',
8275
'index' => 'entity_id',
76+
'sortable' => false,
8377
'header_css_class' => 'col-id',
8478
'column_css_class' => 'col-id'
8579
]
@@ -90,6 +84,7 @@ protected function _prepareColumns()
9084
[
9185
'header' => __('Product'),
9286
'index' => 'name',
87+
'sortable' => false,
9388
'header_css_class' => 'col-product',
9489
'column_css_class' => 'col-product'
9590
]
@@ -104,6 +99,7 @@ protected function _prepareColumns()
10499
'type' => 'currency',
105100
'currency_code' => $currencyCode,
106101
'index' => 'price',
102+
'sortable' => false,
107103
'renderer' => 'Magento\Reports\Block\Adminhtml\Grid\Column\Renderer\Currency',
108104
'rate' => $this->getRate($currencyCode),
109105
'header_css_class' => 'col-price',
@@ -117,6 +113,7 @@ protected function _prepareColumns()
117113
'header' => __('Carts'),
118114
'align' => 'right',
119115
'index' => 'carts',
116+
'sortable' => false,
120117
'header_css_class' => 'col-carts',
121118
'column_css_class' => 'col-carts'
122119
]
@@ -128,6 +125,7 @@ protected function _prepareColumns()
128125
'header' => __('Orders'),
129126
'align' => 'right',
130127
'index' => 'orders',
128+
'sortable' => false,
131129
'header_css_class' => 'col-qty',
132130
'column_css_class' => 'col-qty'
133131
]

app/code/Magento/Reports/Model/Resource/Quote/Collection.php

Lines changed: 82 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -120,57 +120,28 @@ public function prepareForAbandonedReport($storeIds, $filter = null)
120120
/**
121121
* Prepare select query for products in carts report
122122
*
123-
* @return $this
123+
* @return \Magento\Framework\DB\Select
124124
*/
125-
public function prepareForProductsInCarts()
125+
public function prepareActiveCartItems()
126126
{
127-
$productAttrName = $this->_productResource->getAttribute('name');
128-
$productAttrNameId = (int)$productAttrName->getAttributeId();
129-
$productAttrNameTable = $productAttrName->getBackend()->getTable();
130-
$productAttrPrice = $this->_productResource->getAttribute('price');
131-
$productAttrPriceId = (int)$productAttrPrice->getAttributeId();
132-
$productAttrPriceTable = $productAttrPrice->getBackend()->getTable();
133-
134-
$this->getSelect()->useStraightJoin(
135-
true
136-
)->reset(
137-
\Zend_Db_Select::COLUMNS
138-
)->joinInner(
139-
['quote_items' => $this->getTable('quote_item')],
140-
'quote_items.quote_id = main_table.entity_id',
141-
null
142-
)->joinInner(
143-
['e' => $this->getTable('catalog_product_entity')],
144-
'e.entity_id = quote_items.product_id',
145-
null
146-
)->joinInner(
147-
['product_name' => $productAttrNameTable],
148-
'product_name.entity_id = e.entity_id'
149-
. ' AND product_name.attribute_id = ' . $productAttrNameId
150-
. ' AND product_name.store_id = ' . \Magento\Store\Model\Store::DEFAULT_STORE_ID,
151-
['name' => 'product_name.value']
152-
)->joinInner(
153-
['product_price' => $productAttrPriceTable],
154-
"product_price.entity_id = e.entity_id AND product_price.attribute_id = {$productAttrPriceId}",
155-
['price' => new \Zend_Db_Expr('product_price.value * main_table.base_to_global_rate')]
156-
)->joinLeft(
157-
['order_items' => new \Zend_Db_Expr(sprintf('(%s)', $this->getOrdersSubSelect()))],
158-
'order_items.product_id = e.entity_id',
159-
[]
160-
)->columns(
161-
'e.*'
162-
)->columns(
163-
['carts' => new \Zend_Db_Expr('COUNT(quote_items.item_id)')]
164-
)->columns(
165-
'order_items.orders'
166-
)->where(
167-
'main_table.is_active = ?',
168-
1
169-
)->group(
170-
'quote_items.product_id'
171-
);
127+
$quoteItemsSelect = $this->getSelect();
128+
$quoteItemsSelect->reset()
129+
->from(['main_table' => $this->getTable('quote')], '')
130+
->columns('quote_items.product_id')
131+
->columns(['carts' => new \Zend_Db_Expr('COUNT(quote_items.item_id)')])
132+
->columns('main_table.base_to_global_rate')
133+
->joinInner(
134+
['quote_items' => $this->getTable('quote_item')],
135+
'quote_items.quote_id = main_table.entity_id',
136+
null
137+
)->where(
138+
'main_table.is_active = ?',
139+
1
140+
)->group(
141+
'quote_items.product_id'
142+
);
172143

173-
return $this;
144+
return $quoteItemsSelect;
174145
}
175146

176147
/**
@@ -281,13 +252,7 @@ public function getSelectCountSql()
281252
$countSelect->reset(\Zend_Db_Select::COLUMNS);
282253
$countSelect->reset(\Zend_Db_Select::GROUP);
283254
$countSelect->resetJoinLeft();
284-
285-
if ($this->_selectCountSqlType == self::SELECT_COUNT_SQL_TYPE_CART) {
286-
$countSelect->columns("COUNT(DISTINCT e.entity_id)");
287-
} else {
288-
$countSelect->columns("COUNT(DISTINCT main_table.entity_id)");
289-
}
290-
255+
$countSelect->columns("COUNT(DISTINCT main_table.entity_id)");
291256
return $countSelect;
292257
}
293258

@@ -343,4 +308,66 @@ public function resolveCustomerNames()
343308
next($customersData);
344309
}
345310
}
311+
312+
/**
313+
* Separate query for product and order data
314+
*
315+
* @return array
316+
* @throws \Magento\Eav\Exception
317+
*/
318+
protected function getProductData()
319+
{
320+
$productConnection = $this->_productResource->getConnection('read');
321+
$productAttrName = $this->_productResource->getAttribute('name');
322+
$productAttrNameId = (int)$productAttrName->getAttributeId();
323+
$productAttrPrice = $this->_productResource->getAttribute('price');
324+
$productAttrPriceId = (int)$productAttrPrice->getAttributeId();
325+
326+
$select = clone $this->_productResource->getSelect();
327+
$select->reset();
328+
$select->from(
329+
['main_table' => $this->getTable('catalog_product_entity')]
330+
)->useStraightJoin(
331+
true
332+
)->joinInner(
333+
['product_name' => $productAttrName->getBackend()->getTable()],
334+
'product_name.entity_id = main_table.entity_id'
335+
. ' AND product_name.attribute_id = ' . $productAttrNameId
336+
. ' AND product_name.store_id = ' . \Magento\Store\Model\Store::DEFAULT_STORE_ID,
337+
['name' => 'product_name.value']
338+
)->joinInner(
339+
['product_price' => $productAttrPrice->getBackend()->getTable()],
340+
"product_price.entity_id = main_table.entity_id AND product_price.attribute_id = {$productAttrPriceId}",
341+
['price' => new \Zend_Db_Expr('product_price.value')]
342+
)->joinLeft(
343+
['order_items' => new \Zend_Db_Expr(sprintf('(%s)', $this->getOrdersSubSelect()))],
344+
'order_items.product_id = main_table.entity_id',
345+
[]
346+
)->columns(
347+
'order_items.orders'
348+
);
349+
350+
$productData = $productConnection->fetchAssoc($select);
351+
return $productData;
352+
}
353+
354+
/**
355+
* Add data fetched from another database
356+
*
357+
* @return $this
358+
*/
359+
protected function _afterLoad()
360+
{
361+
parent::_afterLoad();
362+
$productData = $this->getProductData();
363+
$items = $this->getItems();
364+
foreach ($items as $item) {
365+
$item->setId($item->getProductId());
366+
$item->setPrice($productData[$item->getProductId()]['price'] * $item->getBaseToGlobalRate());
367+
$item->setName($productData[$item->getProductId()]['name']);
368+
$item->setOrders($productData[$item->getProductId()]['orders']);
369+
}
370+
371+
return $this;
372+
}
346373
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<?php
2+
/**
3+
* Copyright © 2015 Magento. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Reports\Test\Unit\Model\Resource\Report\Quote;
7+
8+
use \Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
9+
use \Magento\Reports\Model\Resource\Quote\Collection as Collection;
10+
11+
class CollectionTest extends \PHPUnit_Framework_TestCase
12+
{
13+
/**
14+
* @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
15+
*/
16+
protected $objectManager;
17+
18+
/**
19+
* @var \PHPUnit_Framework_MockObject_MockObject
20+
*/
21+
protected $selectMock;
22+
23+
protected function setUp()
24+
{
25+
$this->objectManager = new ObjectManager($this);
26+
$this->selectMock = $this->getMock('\Magento\Framework\DB\Select', [], [], '', false);
27+
}
28+
29+
public function testGetSelectCountSql()
30+
{
31+
/** @var $collection \PHPUnit_Framework_MockObject_MockObject */
32+
$constructArgs = $this->objectManager
33+
->getConstructArguments('Magento\Reports\Model\Resource\Quote\Collection');
34+
$collection = $this->getMock(
35+
'Magento\Reports\Model\Resource\Quote\Collection',
36+
['prepareActiveCartItems', 'getSelect'],
37+
$constructArgs,
38+
'',
39+
false
40+
);
41+
42+
$collection->expects($this->once())->method('getSelect')->willReturn($this->selectMock);
43+
$this->selectMock->expects($this->atLeastOnce())->method('reset')->willReturnSelf();
44+
$this->selectMock->expects($this->once())
45+
->method('columns')
46+
->with('COUNT(DISTINCT main_table.entity_id)')
47+
->willReturnSelf();
48+
$this->assertEquals($this->selectMock, $collection->getSelectCountSql());
49+
}
50+
51+
public function testPrepareActiveCartItems()
52+
{
53+
/** @var $collection \PHPUnit_Framework_MockObject_MockObject */
54+
$constructArgs = $this->objectManager
55+
->getConstructArguments('Magento\Reports\Model\Resource\Quote\Collection');
56+
$collection = $this->getMock(
57+
'Magento\Reports\Model\Resource\Quote\Collection',
58+
['getSelect', 'getTable'],
59+
$constructArgs,
60+
'',
61+
false
62+
);
63+
64+
$collection->expects($this->once())->method('getSelect')->willReturn($this->selectMock);
65+
$this->selectMock->expects($this->once())->method('reset')->willReturnSelf();
66+
$this->selectMock->expects($this->once())->method('from')->willReturnSelf();
67+
$this->selectMock->expects($this->atLeastOnce())->method('columns')->willReturnSelf();
68+
$this->selectMock->expects($this->once())->method('joinInner')->willReturnSelf();
69+
$this->selectMock->expects($this->once())->method('where')->willReturnSelf();
70+
$this->selectMock->expects($this->once())->method('group')->willReturnSelf();
71+
$collection->expects($this->exactly(2))->method('getTable')->willReturn('table');
72+
$collection->prepareActiveCartItems();
73+
}
74+
75+
public function testLoadWithFilter()
76+
{
77+
/** @var $collection \PHPUnit_Framework_MockObject_MockObject */
78+
$constructArgs = $this->objectManager
79+
->getConstructArguments('Magento\Reports\Model\Resource\Quote\Collection');
80+
$constructArgs['eventManager'] = $this->getMock('Magento\Framework\Event\ManagerInterface', [], [], '', false);
81+
$readConnectionMock = $this->getMock('Magento\Framework\DB\Adapter\AdapterInterface', [], [], '', false);
82+
$resourceMock = $this->getMock('\Magento\Quote\Model\Resource\Quote', [], [], '', false);
83+
$constructArgs['resource'] = $resourceMock;
84+
$productResourceMock = $this->getMock('\Magento\Catalog\Model\Resource\Product\Collection', [], [], '', false);
85+
$constructArgs['productResource'] = $productResourceMock;
86+
87+
$collection = $this->getMock(
88+
'Magento\Reports\Model\Resource\Quote\Collection',
89+
[
90+
'_beforeLoad',
91+
'_renderFilters',
92+
'_renderOrders',
93+
'_renderLimit',
94+
'printLogQuery',
95+
'getData',
96+
'_setIsLoaded',
97+
'setConnection',
98+
'_initSelect',
99+
'getTable',
100+
'getItems',
101+
'getOrdersSubSelect',
102+
],
103+
$constructArgs
104+
);
105+
//load()
106+
$collection->expects($this->once())->method('_beforeLoad')->willReturnSelf();
107+
$collection->expects($this->once())->method('_renderFilters')->willReturnSelf();
108+
$collection->expects($this->once())->method('_renderOrders')->willReturnSelf();
109+
$collection->expects($this->once())->method('_renderLimit')->willReturnSelf();
110+
$collection->expects($this->once())->method('printLogQuery')->willReturnSelf();
111+
$collection->expects($this->once())->method('getData')->willReturn(null);
112+
$collection->expects($this->once())->method('_setIsLoaded')->willReturnSelf();
113+
114+
//productLoad()
115+
$productAttributeMock = $this->getMock(
116+
'\Magento\Eav\Model\Entity\Attribute\AbstractAttribute',
117+
[],
118+
[],
119+
'',
120+
false
121+
);
122+
$priceAttributeMock = $this->getMock(
123+
'\Magento\Eav\Model\Entity\Attribute\AbstractAttribute',
124+
[],
125+
[],
126+
'',
127+
false
128+
);
129+
$productResourceMock->expects($this->once())
130+
->method('getConnection')
131+
->with('read')
132+
->willReturn($readConnectionMock);
133+
$productResourceMock->expects($this->any())
134+
->method('getAttribute')
135+
->willReturnMap([['name', $productAttributeMock], ['price', $priceAttributeMock]]);
136+
$productResourceMock->expects($this->once())->method('getSelect')->willReturn($this->selectMock);
137+
$this->selectMock->expects($this->once())->method('reset')->willReturnSelf();
138+
$this->selectMock->expects($this->once())->method('from')->willReturnSelf();
139+
$this->selectMock->expects($this->once())->method('useStraightJoin')->willReturnSelf();
140+
$this->selectMock->expects($this->exactly(2))->method('joinInner')->willReturnSelf();
141+
$this->selectMock->expects($this->once())->method('joinLeft')->willReturnSelf();
142+
$this->selectMock->expects($this->once())->method('columns')->willReturnSelf();
143+
144+
$productAttributeMock->expects($this->once())->method('getBackend')->willReturnSelf();
145+
$priceAttributeMock->expects($this->once())->method('getBackend')->willReturnSelf();
146+
$readConnectionMock->expects($this->once())->method('fetchAssoc')->willReturn([1, 2, 3]);
147+
148+
//_afterLoad()
149+
$collection->expects($this->once())->method('getItems')->willReturn([]);
150+
151+
$collection->loadWithFilter();
152+
}
153+
}

0 commit comments

Comments
 (0)