From 3648333e569907c2b2d8c79b4d18641233861163 Mon Sep 17 00:00:00 2001 From: Oleksandr Osadchyi Date: Fri, 27 Jan 2017 15:27:31 +0200 Subject: [PATCH 01/18] MAGETWO-62616: Products are missed and total count is wrong on category page --- .../Category/Product/AbstractAction.php | 3 +- .../Framework/DB/Adapter/Pdo/Mysql.php | 47 +++- .../DB/Query/BatchIteratorFactory.php | 58 ++++- .../Framework/DB/Query/BatchRangeIterator.php | 208 ++++++++++++++++++ .../Magento/Framework/DB/Query/Generator.php | 37 +++- .../Unit/DB/Query/BatchRangeIteratorTest.php | 121 ++++++++++ .../Test/Unit/DB/Query/GeneratorTest.php | 9 +- 7 files changed, 456 insertions(+), 27 deletions(-) create mode 100644 lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php create mode 100644 lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php index c4d8e4fd9be34..dbdd164486fab 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php @@ -314,7 +314,8 @@ protected function prepareSelectsByRange(\Magento\Framework\DB\Select $select, $ return $this->isRangingNeeded() ? $this->connection->selectsByRange( $field, $select, - $range + $range, + \Magento\Framework\DB\Query\BatchIteratorFactory::NON_UNIQUE_FIELD_ITERATOR ) : [ $select ]; diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index b4cb60f5a5c74..fa0d081d0a0ea 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -3374,18 +3374,59 @@ public function insertFromSelect(Select $select, $table, array $fields = [], $mo } /** - * Get insert queries in array for insert by range with step parameter + * Get an array of select for insert by range with step parameter * + * @see selectsByRangeStrategy() * @param string $rangeField * @param \Magento\Framework\DB\Select $select * @param int $stepCount + * @param string $batchStrategy * @return \Magento\Framework\DB\Select[] * @throws LocalizedException * @deprecated */ - public function selectsByRange($rangeField, \Magento\Framework\DB\Select $select, $stepCount = 100) + public function selectsByRange( + $rangeField, + \Magento\Framework\DB\Select $select, + $stepCount = 100, + $batchStrategy = \Magento\Framework\DB\Query\BatchIteratorFactory::UNIQUE_FIELD_ITERATOR + ) { - $iterator = $this->getQueryGenerator()->generate($rangeField, $select, $stepCount); + return $this->selectsByRangeStrategy($rangeField, $select, $stepCount, $batchStrategy); + } + + /** + * Get an array of select queries using the batching strategy + * + * Depending on the $batchStrategy parameter chooses a strategy. This strategy will be used to create + * an array of select queries. By default method use $batchStrategy parameter: + * \Magento\Framework\DB\Query\BatchIteratorFactory::UNIQUE_FIELD_ITERATOR. + * This parameter means that values of $rangeField have relationship + * one-to-one. + * If values of $rangeField is non-unique and have relationship one-to-many, + * than must be used next $batchStrategy parameter: + * \Magento\Framework\DB\Query\BatchIteratorFactory::NON_UNIQUE_FIELD_ITERATOR. + * + * @see BatchIteratorFactory + * @param string $rangeField - Field which is used for the range mechanism in select + * @param Select $select + * @param int $batchSize - Determines on how many parts will be divided + * the number of values in the select. + * @param string $batchStrategy - It determines which strategy is chosen + * @return \Magento\Framework\DB\Select[] + * @throws LocalizedException Throws if incorrect "FROM" part in \Select exists + * @deprecated This is a temporary solution which is made due to the fact that we + * can't change method selectsByRange() in version 2.1 due to a backwards incompatibility. + * In 2.2 version need to use original method selectsByRange() with additional parameter. + */ + public function selectsByRangeStrategy( + $rangeField, + \Magento\Framework\DB\Select $select, + $batchSize = 100, + $batchStrategy = \Magento\Framework\DB\Query\BatchIteratorFactory::UNIQUE_FIELD_ITERATOR + ) { + $iterator = $this->getQueryGenerator()->generate($rangeField, $select, $batchSize, $batchStrategy); + $queries = []; foreach ($iterator as $query) { $queries[] = $query; diff --git a/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php b/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php index 147e65b8df466..f327569bee64c 100644 --- a/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php +++ b/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php @@ -6,10 +6,25 @@ namespace Magento\Framework\DB\Query; /** - * Factory class for @see \Magento\Framework\DB\Query\BatchIterator + * Factory class for \Magento\Framework\DB\Query\BatchIterator, \Magento\Framework\DB\Query\BatchRangeIterator + * + * @see \Magento\Framework\DB\Query\BatchIterator + * @see \Magento\Framework\DB\Query\BatchRangeIterator */ class BatchIteratorFactory { + /** + * Constant which determine strategy to create iterator which will to process + * range field eg. entity_id with unique values. + */ + const UNIQUE_FIELD_ITERATOR = "unique"; + + /** + * Constant which determine strategy to create iterator which will to process + * range field with non-unique values. + */ + const NON_UNIQUE_FIELD_ITERATOR = "non_unqiue"; + /** * Object Manager instance * @@ -18,34 +33,53 @@ class BatchIteratorFactory private $objectManager = null; /** - * Instance name to create - * * @var string */ - private $instanceName = null; + private $uniqueIteratorInstanceName; + + /** + * @var string + */ + private $nonUniqueIteratorInstanceName; /** - * Factory constructor - * * @param \Magento\Framework\ObjectManagerInterface $objectManager - * @param string $instanceName + * @param $uniqueIteratorInstanceName + * @param $nonUniqueIteratorInstanceName */ public function __construct( \Magento\Framework\ObjectManagerInterface $objectManager, - $instanceName = \Magento\Framework\DB\Query\BatchIterator::class + $nonUniqueIteratorInstanceName = \Magento\Framework\DB\Query\BatchRangeIterator::class, + $uniqueIteratorInstanceName = \Magento\Framework\DB\Query\BatchIterator::class + ) { $this->objectManager = $objectManager; - $this->instanceName = $instanceName; + $this->uniqueIteratorInstanceName = $uniqueIteratorInstanceName; + $this->nonUniqueIteratorInstanceName = $nonUniqueIteratorInstanceName; } /** - * Create class instance with specified parameters + * Create iterator instance with specified parameters + * + * Depending on the chosen strategy specified in $data['batchStrategy'] for selects, + * create an instance of iterator. + * By default will be created \Magento\Framework\DB\Query\BatchIterator class. + * This iterator provide interface for accessing sub-selects which was created from main select. * + * If $data['batchStrategy'] == 'non_unqiue' value then we should to create BatchRangeIterator. This Iterator + * allows to operate with rangeField, which has one-to-many relations with other fields and is not unique. + * The main idea is to separate select to few parts in order to reduce the load of SQL server. + * + * @see \Magento\Framework\DB\Query\Generator * @param array $data - * @return \Magento\Framework\DB\Query\BatchIterator + * @return \Iterator */ public function create(array $data = []) { - return $this->objectManager->create($this->instanceName, $data); + if (isset($data['batchStrategy']) && $data['batchStrategy'] == self::NON_UNIQUE_FIELD_ITERATOR) { + return $this->objectManager->create($this->nonUniqueIteratorInstanceName, $data); + } + + return $this->objectManager->create($this->uniqueIteratorInstanceName, $data); } } diff --git a/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php b/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php new file mode 100644 index 0000000000000..8b14acc175822 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php @@ -0,0 +1,208 @@ +batchSize = $batchSize; + $this->select = $select; + $this->correlationName = $correlationName; + $this->rangeField = $rangeField; + $this->rangeFieldAlias = $rangeFieldAlias; + $this->connection = $select->getConnection(); + } + + /** + * Return the current element + * + * If we don't have sub-select we should create and remember it. + * + * @return Select + */ + public function current() + { + if (null == $this->currentSelect) { + $this->isValid = ($this->currentBatch + $this->batchSize) < $this->totalItemCount; + $this->currentSelect = $this->initSelectObject(); + } + return $this->currentSelect; + } + + /** + * Return the key of the current element + * + * Сan return the number of the current sub-select in the iteration. + * + * @return int + */ + public function key() + { + return $this->iteration; + } + + /** + * Move forward to next element + * + * Retrieve the next sub-select and move cursor to the next element. + * Checks that the count of elements more than the sum of limit and offset. + * + * @return Select + */ + public function next() + { + if (null == $this->currentSelect) { + $this->current(); + } + $this->isValid = ($this->batchSize + $this->currentBatch) < $this->totalItemCount; + $select = $this->initSelectObject(); + if ($this->isValid) { + $this->iteration++; + $this->currentSelect = $select; + } else { + $this->currentSelect = null; + } + return $this->currentSelect; + } + + /** + * Rewind the BatchRangeIterator to the first element. + * + * Allows to start iteration from the beginning. + * Move cursor to the start. + * + * @return void + */ + public function rewind() + { + $this->currentSelect = null; + $this->iteration = 0; + $this->isValid = true; + $this->totalItemCount = 0; + } + + /** + * Checks if current position is valid + * + * @return bool + */ + public function valid() + { + return $this->isValid; + } + + /** + * Initialize select object + * + * Return sub-select which is limited by current batch value and return items from n page of SQL request. + * + * @return \Magento\Framework\DB\Select + */ + private function initSelectObject() + { + $object = clone $this->select; + + if (!$this->totalItemCount) { + $wrapperSelect = $this->connection->select(); + $wrapperSelect->from( + $object, + [ + new \Zend_Db_Expr('COUNT(*) as cnt') + ] + ); + $row = $this->connection->fetchRow($wrapperSelect); + + $this->totalItemCount = intval($row['cnt']); + } + /** + * Reset sort order section from origin select object + */ + $object->order($this->correlationName . '.' . $this->rangeField . ' ' . \Magento\Framework\DB\Select::SQL_ASC); + $object->limit($this->currentBatch, $this->batchSize); + $this->currentBatch += $this->batchSize; + + return $object; + } +} diff --git a/lib/internal/Magento/Framework/DB/Query/Generator.php b/lib/internal/Magento/Framework/DB/Query/Generator.php index 909f4752397a3..d6a1546490fc3 100644 --- a/lib/internal/Magento/Framework/DB/Query/Generator.php +++ b/lib/internal/Magento/Framework/DB/Query/Generator.php @@ -28,16 +28,36 @@ public function __construct(BatchIteratorFactory $iteratorFactory) } /** - * Generate select query list with predefined items count in each select item. + * Generate select query list with predefined items count in each select item * - * @param string $rangeField + * Generates select parameters - batchSize, correlationName, rangeField, rangeFieldAlias, batchStrategy + * to obtain instance of iterator. The behavior of the iterator will depend on the parameters passed to it. + * For example: by default for $batchStrategy parameter used + * \Magento\Framework\DB\Query\BatchIteratorFactory::UNIQUE_FIELD_ITERATOR. This parameter is determine, what + * instance of Iterator will be returned. + * + * Other params: + * select - represents the select object, that should be passed into Iterator. + * batchSize - sets the number of items in select. + * correlationName - is the base table involved in the select. + * rangeField - this is the basic field which used to split select. + * rangeFieldAlias - alias of range field. + * + * @see BatchIteratorFactory + * @param string $rangeField - Field which is used for the range mechanism in select * @param \Magento\Framework\DB\Select $select - * @param int $batchSize - * @return BatchIterator - * @throws LocalizedException + * @param int $batchSize - Determines on how many parts will be divided + * the number of values in the select. + * @param string $batchStrategy It determines which strategy is chosen + * @return \Iterator + * @throws LocalizedException Throws if incorrect "FROM" part in \Select exists */ - public function generate($rangeField, \Magento\Framework\DB\Select $select, $batchSize = 100) - { + public function generate( + $rangeField, + \Magento\Framework\DB\Select $select, + $batchSize = 100, + $batchStrategy = \Magento\Framework\DB\Query\BatchIteratorFactory::UNIQUE_FIELD_ITERATOR + ) { $fromSelect = $select->getPart(\Magento\Framework\DB\Select::FROM); if (empty($fromSelect)) { throw new LocalizedException( @@ -72,7 +92,8 @@ public function generate($rangeField, \Magento\Framework\DB\Select $select, $bat 'batchSize' => $batchSize, 'correlationName' => $fieldCorrelationName, 'rangeField' => $rangeField, - 'rangeFieldAlias' => $rangeFieldAlias + 'rangeFieldAlias' => $rangeFieldAlias, + 'batchStrategy' => $batchStrategy ] ); } diff --git a/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php b/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php new file mode 100644 index 0000000000000..6ae87f9856e87 --- /dev/null +++ b/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php @@ -0,0 +1,121 @@ +batchSize = 10; + $this->currentBatch = 0; + $this->correlationName = 'correlationName'; + $this->rangeField = 'rangeField'; + $this->rangeFieldAlias = 'rangeFieldAlias'; + + $this->selectMock = $this->getMock(Select::class, [], [], '', false, false); + $this->wrapperSelectMock = $this->getMock(Select::class, [], [], '', false, false); + $this->connectionMock = $this->getMock(AdapterInterface::class); + $this->connectionMock->expects($this->any())->method('select')->willReturn($this->wrapperSelectMock); + $this->selectMock->expects($this->once())->method('getConnection')->willReturn($this->connectionMock); + $this->connectionMock->expects($this->any())->method('quoteIdentifier')->willReturnArgument(0); + + $this->model = new BatchRangeIterator( + $this->selectMock, + $this->batchSize, + $this->correlationName, + $this->rangeField, + $this->rangeFieldAlias + ); + } + + /** + * Test steps: + * 1. $iterator->current(); + * 2. $iterator->key(); + * @return void + */ + public function testCurrent() + { + $filed = $this->correlationName . '.' . $this->rangeField; + + $this->selectMock->expects($this->once())->method('limit')->with($this->currentBatch, $this->batchSize); + $this->selectMock->expects($this->once())->method('order')->with($filed . ' ASC'); + $this->assertEquals($this->selectMock, $this->model->current()); + $this->assertEquals(0, $this->model->key()); + } + + /** + * Test the separation of batches + */ + public function testIterations() + { + $iterations = 0; + $this->connectionMock->expects($this->once()) + ->method('fetchRow') + ->willReturn(['cnt' => 105]); + + foreach ($this->model as $key => $value) { + $iterations++; + }; + + $this->assertEquals(10, $iterations); + } +} diff --git a/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php b/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php index fcfaa41ffb4e6..b20f14a1d9bee 100644 --- a/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php @@ -70,7 +70,8 @@ public function testGenerate() 'batchSize' => 100, 'correlationName' => 'cp', 'rangeField' => 'entity_id', - 'rangeFieldAlias' => 'product_id' + 'rangeFieldAlias' => 'product_id', + 'batchStrategy' => 'unique' ] )->willReturn($this->iteratorMock); $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100)); @@ -126,7 +127,8 @@ public function testGenerateWithRangeFieldWithoutAlias() 'batchSize' => 100, 'correlationName' => 'cp', 'rangeField' => 'entity_id', - 'rangeFieldAlias' => 'entity_id' + 'rangeFieldAlias' => 'entity_id', + 'batchStrategy' => 'unique' ] )->willReturn($this->iteratorMock); $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100)); @@ -160,7 +162,8 @@ public function testGenerateWithInvalidWithWildcard() 'batchSize' => 100, 'correlationName' => 'cp', 'rangeField' => 'entity_id', - 'rangeFieldAlias' => 'entity_id' + 'rangeFieldAlias' => 'entity_id', + 'batchStrategy' => 'unique' ] )->willReturn($this->iteratorMock); $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100)); From d8ce0cfd3496f23627954fa9cffc95486e12c8ed Mon Sep 17 00:00:00 2001 From: Oleksandr Osadchyi Date: Fri, 27 Jan 2017 16:35:23 +0200 Subject: [PATCH 02/18] MAGETWO-62616: Products are missed and total count is wrong on category page --- .../Magento/Framework/DB/Query/BatchIteratorFactory.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php b/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php index f327569bee64c..565e441246c20 100644 --- a/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php +++ b/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php @@ -44,14 +44,13 @@ class BatchIteratorFactory /** * @param \Magento\Framework\ObjectManagerInterface $objectManager - * @param $uniqueIteratorInstanceName - * @param $nonUniqueIteratorInstanceName + * @param \Magento\Framework\DB\Query\BatchIterator $uniqueIteratorInstanceName + * @param \Magento\Framework\DB\Query\BatchRangeIterator $nonUniqueIteratorInstanceName */ public function __construct( \Magento\Framework\ObjectManagerInterface $objectManager, $nonUniqueIteratorInstanceName = \Magento\Framework\DB\Query\BatchRangeIterator::class, $uniqueIteratorInstanceName = \Magento\Framework\DB\Query\BatchIterator::class - ) { $this->objectManager = $objectManager; $this->uniqueIteratorInstanceName = $uniqueIteratorInstanceName; From c015f8f0a96ad1612abf196391b15272842e756b Mon Sep 17 00:00:00 2001 From: Oleksandr Osadchyi Date: Fri, 27 Jan 2017 18:05:00 +0200 Subject: [PATCH 03/18] MAGETWO-62616: Products are missed and total count is wrong on category page --- lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php b/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php index 8b14acc175822..0718d2cb5b7b7 100644 --- a/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php +++ b/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php @@ -52,6 +52,7 @@ class BatchRangeIterator implements \Iterator * @var int */ private $totalItemCount; + /** * @var int */ @@ -76,6 +77,7 @@ class BatchRangeIterator implements \Iterator * Initialize dependencies. * * @param Select $select + * @param int $batchSize * @param string $correlationName * @param string $rangeField * @param string $rangeFieldAlias From 65b69dc6f0eb5bbb2f45f311d22ea7ba2cf38a78 Mon Sep 17 00:00:00 2001 From: Oleksandr Osadchyi Date: Fri, 27 Jan 2017 18:32:39 +0200 Subject: [PATCH 04/18] MAGETWO-62616: Products are missed and total count is wrong on category page --- .../Test/Unit/DB/Query/BatchRangeIteratorTest.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php b/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php index 6ae87f9856e87..d51ceed1f25eb 100644 --- a/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php @@ -107,15 +107,16 @@ public function testCurrent() */ public function testIterations() { - $iterations = 0; $this->connectionMock->expects($this->once()) ->method('fetchRow') ->willReturn(['cnt' => 105]); - foreach ($this->model as $key => $value) { - $iterations++; - }; + $this->model->rewind(); - $this->assertEquals(10, $iterations); + $this->assertEquals($this->selectMock, $this->model->current()); + $this->assertEquals(0, $this->model->key()); + $this->assertEquals($this->selectMock, $this->model->next()); + $this->assertTrue($this->model->valid()); + $this->assertEquals(1, $this->model->key()); } } From bde30bc21de847b0b20fe983dc90ae7564e554eb Mon Sep 17 00:00:00 2001 From: Sergii Kovalenko Date: Sat, 28 Jan 2017 23:40:34 +0200 Subject: [PATCH 05/18] MAGETWO-63403: Auto Generated coupon codes not applying --- .../SalesRule/Model/ResourceModel/Rule/Collection.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index b884a39a8bd8a..f3bd4c239ae37 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -158,8 +158,9 @@ public function setValidationFilter( $orWhereConditions = [ $connection->quoteInto( - '(main_table.coupon_type = ? AND rule_coupons.type = 0)', - \Magento\SalesRule\Model\Rule::COUPON_TYPE_AUTO + '(main_table.coupon_type = ? AND rule_coupons.type = ?)', + \Magento\SalesRule\Model\Rule::COUPON_TYPE_AUTO, + \Magento\SalesRule\Helper\Coupon::COUPON_TYPE_SPECIFIC_AUTOGENERATED ), $connection->quoteInto( '(main_table.coupon_type = ? AND main_table.use_auto_generation = 1 AND rule_coupons.type = 1)', From 89afdc3a501c6a2335eeb8db242f3914428290a3 Mon Sep 17 00:00:00 2001 From: Sergii Kovalenko Date: Sun, 29 Jan 2017 01:19:37 +0200 Subject: [PATCH 06/18] MAGETWO-63403: Auto Generated coupon codes not applying --- .../integration/testsuite/Magento/SalesRule/_files/coupons.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupons.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupons.php index 3062683019b03..36f3365f3de91 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupons.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/coupons.php @@ -27,4 +27,4 @@ // type AUTO $coupon = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\SalesRule\Model\Coupon::class); -$coupon->setRuleId($items[3]->getId())->setCode('coupon_code_auto')->setType(0)->save(); +$coupon->setRuleId($items[3]->getId())->setCode('coupon_code_auto')->setType(1)->save(); From ec92cc2fad05952ef7d66ec6fd558be2534b498b Mon Sep 17 00:00:00 2001 From: Sergii Kovalenko Date: Sun, 29 Jan 2017 01:29:02 +0200 Subject: [PATCH 07/18] MAGETWO-63403: Auto Generated coupon codes not applying --- .../SalesRule/Model/ResourceModel/Rule/Collection.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index f3bd4c239ae37..e9181504563d7 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -158,9 +158,8 @@ public function setValidationFilter( $orWhereConditions = [ $connection->quoteInto( - '(main_table.coupon_type = ? AND rule_coupons.type = ?)', - \Magento\SalesRule\Model\Rule::COUPON_TYPE_AUTO, - \Magento\SalesRule\Helper\Coupon::COUPON_TYPE_SPECIFIC_AUTOGENERATED + '(main_table.coupon_type = ? AND rule_coupons.type = 1)', + \Magento\SalesRule\Model\Rule::COUPON_TYPE_AUTO ), $connection->quoteInto( '(main_table.coupon_type = ? AND main_table.use_auto_generation = 1 AND rule_coupons.type = 1)', From 1cf3cb4b6ea24dc7120463a95351a6eccf2dcfd5 Mon Sep 17 00:00:00 2001 From: Sergey Semenov Date: Mon, 30 Jan 2017 18:55:46 +0200 Subject: [PATCH 08/18] MAGETWO-63561: category_ids attribute scope dropdown --- app/code/Magento/Catalog/etc/eav_attributes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/etc/eav_attributes.xml b/app/code/Magento/Catalog/etc/eav_attributes.xml index 133849a28e048..005402937232f 100644 --- a/app/code/Magento/Catalog/etc/eav_attributes.xml +++ b/app/code/Magento/Catalog/etc/eav_attributes.xml @@ -30,6 +30,7 @@ + From 22e8f97b1346e6a2d3a6ea26c58c10ffc7a09818 Mon Sep 17 00:00:00 2001 From: Sergii Kovalenko Date: Tue, 31 Jan 2017 11:57:14 +0200 Subject: [PATCH 09/18] MAGETWO-63403: Auto Generated coupon codes not applying --- .../Model/ResourceModel/Rule/Collection.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php index e9181504563d7..b1c6fd79d418c 100644 --- a/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php +++ b/app/code/Magento/SalesRule/Model/ResourceModel/Rule/Collection.php @@ -156,11 +156,19 @@ public function setValidationFilter( \Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON ); - $orWhereConditions = [ + $autoGeneratedCouponCondition = [ $connection->quoteInto( - '(main_table.coupon_type = ? AND rule_coupons.type = 1)', + "main_table.coupon_type = ?", \Magento\SalesRule\Model\Rule::COUPON_TYPE_AUTO ), + $connection->quoteInto( + "rule_coupons.type = ?", + \Magento\SalesRule\Api\Data\CouponInterface::TYPE_GENERATED + ), + ]; + + $orWhereConditions = [ + "(" . implode($autoGeneratedCouponCondition, " AND ") . ")", $connection->quoteInto( '(main_table.coupon_type = ? AND main_table.use_auto_generation = 1 AND rule_coupons.type = 1)', \Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC From a631bb6b31479bcb37665699fd2c8a6481c5ffde Mon Sep 17 00:00:00 2001 From: Sergii Kovalenko Date: Tue, 31 Jan 2017 16:58:00 +0200 Subject: [PATCH 10/18] MAGETWO-63403: Auto Generated coupon codes not applying --- .../Promo/Quote/Edit/PromoQuoteForm.xml | 5 ++ .../Quote/Edit/Section/ManageCouponCode.php | 50 +++++++++++++++++++ .../AssertCartPriceRuleApplying.php | 14 ++++-- .../SalesRule/Test/Repository/SalesRule.xml | 1 - .../TestCase/CreateSalesRuleEntityTest.php | 21 +++++++- .../TestCase/CreateSalesRuleEntityTest.xml | 27 ++++++++++ 6 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/Section/ManageCouponCode.php diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml index 7e30946214475..28541b56d43f8 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/PromoQuoteForm.xml @@ -80,4 +80,9 @@ [data-index="labels"] css selector + + \Magento\SalesRule\Test\Block\Adminhtml\Promo\Quote\Edit\Section\ManageCouponCode + [data-index="block_promo_sales_rule_edit_tab_coupons"] + css selector + diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/Section/ManageCouponCode.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/Section/ManageCouponCode.php new file mode 100644 index 0000000000000..ebd20616dc891 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Block/Adminhtml/Promo/Quote/Edit/Section/ManageCouponCode.php @@ -0,0 +1,50 @@ +_rootElement->find(self::GENERATE_CODES_BUTTON_CSS_SELECTOR); + $button->click(); + } + + /** + * Retrieve last generated coupon code + * + * @return string + */ + public function getGeneratedCouponCode() + { + $this->waitForSpinner(); + $column = $this->_rootElement->find(self::LAST_GENERATED_COUPON_CODE_SELECTOR, Locator::SELECTOR_XPATH); + return $column->getText(); + } + + private function waitForSpinner() + { + $this->waitForElementNotVisible(self::SPINNER); + sleep(1); + } +} diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php index d554ec2ce7991..f27667e91ea64 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php @@ -156,6 +156,7 @@ public function processAssert( Customer $customer = null, Address $address = null, $isLoggedIn = null, + $couponCode = null, array $shipping = [], array $cartPrice = [] ) { @@ -179,10 +180,15 @@ public function processAssert( $this->checkoutCart->getShippingBlock()->fillEstimateShippingAndTax($address); $this->checkoutCart->getShippingBlock()->selectShippingMethod($shipping); } - if ($salesRule->getCouponCode() || $salesRuleOrigin->getCouponCode()) { - $this->checkoutCart->getDiscountCodesBlock()->applyCouponCode( - $salesRule->getCouponCode() ? $salesRule->getCouponCode() : $salesRuleOrigin->getCouponCode() - ); + + if ($salesRule->getCouponCode()) { + $couponCode = $salesRule->getCouponCode(); + } elseif($salesRuleOrigin->getCouponCode()) { + $couponCode = $salesRuleOrigin->getCouponCode(); + } + + if ($salesRule->getCouponCode() || $salesRuleOrigin->getCouponCode() || $couponCode) { + $this->checkoutCart->getDiscountCodesBlock()->applyCouponCode($couponCode); } $this->assert(); } diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml index 911ac8e8482bb..9ac8e5abd4fb9 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Repository/SalesRule.xml @@ -95,7 +95,6 @@ 50 Yes - Cart Price Rule with with complex conditions %isolation% Cart Price Rule with with complex conditions diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.php index 68f4470dc275c..1e413bf3ca5ae 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.php @@ -6,6 +6,7 @@ namespace Magento\SalesRule\Test\TestCase; +use Magento\SalesRule\Test\Block\Adminhtml\Promo\Quote\Edit\Section\ManageCouponCode; use Magento\SalesRule\Test\Fixture\SalesRule; use Magento\SalesRule\Test\Page\Adminhtml\PromoQuoteEdit; use Magento\SalesRule\Test\Page\Adminhtml\PromoQuoteIndex; @@ -107,7 +108,8 @@ public function testCreateSalesRule( CatalogProductSimple $productForSalesRule1, CatalogProductSimple $productForSalesRule2 = null, Customer $customer = null, - $conditionEntity = null + $conditionEntity = null, + SalesRule $salesRuleEdit = null ) { $replace = null; $this->salesRuleName = $salesRule->getName(); @@ -127,7 +129,22 @@ public function testCreateSalesRule( // Steps $this->promoQuoteNew->open(); $this->promoQuoteNew->getSalesRuleForm()->fill($salesRule, null, $replace); - $this->promoQuoteNew->getFormPageActions()->save(); + + if ($salesRule->getCouponType() == "Auto") { + $this->promoQuoteNew->getFormPageActions()->saveAndContinue(); + $form = $this->promoQuoteEdit->getSalesRuleForm(); + $form->openSection('manage_coupon_code'); + /** @var ManageCouponCode $section */ + $section = $form->getSection('manage_coupon_code'); + $section->fill($salesRuleEdit); + $section->generateCouponCodes(); + $couponCode = $section->getGeneratedCouponCode(); + $this->promoQuoteEdit->getFormPageActions()->save(); + + return ["couponCode" => $couponCode]; + } else { + $this->promoQuoteNew->getFormPageActions()->save(); + } } /** diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml index 05df20e4025b5..7710bd2dc0f0f 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/TestCase/CreateSalesRuleEntityTest.xml @@ -424,5 +424,32 @@ + + United States + California + 95814 + Flat Rate + Fixed + Cart Price Rule2 %isolation% + Cart Price Rule Description %isolation% + Yes + Main Website + NOT LOGGED IN + Auto + Fixed amount discount + 35 + No + No + Coupon code+fixed amount discount + 1 + simple_for_salesrule_1 + 2 + 200.00 + 140.00 + 70.00 + + + + From f6eb21855f4e615ee6cf1b02a67967da47ada084 Mon Sep 17 00:00:00 2001 From: Sergii Kovalenko Date: Tue, 31 Jan 2017 17:46:43 +0200 Subject: [PATCH 11/18] MAGETWO-63403: Auto Generated coupon codes not applying --- .../SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php index f27667e91ea64..a1893412681cd 100644 --- a/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php +++ b/dev/tests/functional/tests/app/Magento/SalesRule/Test/Constraint/AssertCartPriceRuleApplying.php @@ -183,7 +183,7 @@ public function processAssert( if ($salesRule->getCouponCode()) { $couponCode = $salesRule->getCouponCode(); - } elseif($salesRuleOrigin->getCouponCode()) { + } elseif ($salesRuleOrigin->getCouponCode()) { $couponCode = $salesRuleOrigin->getCouponCode(); } From d2c036691c497dad60de8037d59a859574563739 Mon Sep 17 00:00:00 2001 From: Oleksandr Osadchyi Date: Thu, 2 Feb 2017 18:32:56 +0200 Subject: [PATCH 12/18] MAGETWO-62616: Products are missed and total count is wrong on category page --- .../Framework/DB/Adapter/Pdo/Mysql.php | 27 +-------------- .../DB/Query/BatchIteratorFactory.php | 2 +- .../Framework/DB/Query/BatchRangeIterator.php | 3 +- .../Unit/DB/Query/BatchRangeIteratorTest.php | 17 +++++----- .../Test/Unit/DB/Query/GeneratorTest.php | 34 +++++++++++++++++++ 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index fa0d081d0a0ea..99f687d172f36 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -3373,28 +3373,6 @@ public function insertFromSelect(Select $select, $table, array $fields = [], $mo return $query; } - /** - * Get an array of select for insert by range with step parameter - * - * @see selectsByRangeStrategy() - * @param string $rangeField - * @param \Magento\Framework\DB\Select $select - * @param int $stepCount - * @param string $batchStrategy - * @return \Magento\Framework\DB\Select[] - * @throws LocalizedException - * @deprecated - */ - public function selectsByRange( - $rangeField, - \Magento\Framework\DB\Select $select, - $stepCount = 100, - $batchStrategy = \Magento\Framework\DB\Query\BatchIteratorFactory::UNIQUE_FIELD_ITERATOR - ) - { - return $this->selectsByRangeStrategy($rangeField, $select, $stepCount, $batchStrategy); - } - /** * Get an array of select queries using the batching strategy * @@ -3415,11 +3393,8 @@ public function selectsByRange( * @param string $batchStrategy - It determines which strategy is chosen * @return \Magento\Framework\DB\Select[] * @throws LocalizedException Throws if incorrect "FROM" part in \Select exists - * @deprecated This is a temporary solution which is made due to the fact that we - * can't change method selectsByRange() in version 2.1 due to a backwards incompatibility. - * In 2.2 version need to use original method selectsByRange() with additional parameter. */ - public function selectsByRangeStrategy( + public function selectsByRange( $rangeField, \Magento\Framework\DB\Select $select, $batchSize = 100, diff --git a/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php b/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php index 565e441246c20..218bfa413f8c9 100644 --- a/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php +++ b/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php @@ -6,7 +6,7 @@ namespace Magento\Framework\DB\Query; /** - * Factory class for \Magento\Framework\DB\Query\BatchIterator, \Magento\Framework\DB\Query\BatchRangeIterator + * Factory class for \Magento\Framework\DB\Query\BatchIterator | \Magento\Framework\DB\Query\BatchRangeIterator * * @see \Magento\Framework\DB\Query\BatchIterator * @see \Magento\Framework\DB\Query\BatchRangeIterator diff --git a/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php b/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php index 0718d2cb5b7b7..576b545b236b3 100644 --- a/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php +++ b/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php @@ -126,7 +126,7 @@ public function key() } /** - * Move forward to next element + * Move forward to next sub-select * * Retrieve the next sub-select and move cursor to the next element. * Checks that the count of elements more than the sum of limit and offset. @@ -153,7 +153,6 @@ public function next() * Rewind the BatchRangeIterator to the first element. * * Allows to start iteration from the beginning. - * Move cursor to the start. * * @return void */ diff --git a/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php b/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php index d51ceed1f25eb..aeb4ed97b4853 100644 --- a/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/DB/Query/BatchRangeIteratorTest.php @@ -94,10 +94,8 @@ protected function setUp() */ public function testCurrent() { - $filed = $this->correlationName . '.' . $this->rangeField; - $this->selectMock->expects($this->once())->method('limit')->with($this->currentBatch, $this->batchSize); - $this->selectMock->expects($this->once())->method('order')->with($filed . ' ASC'); + $this->selectMock->expects($this->once())->method('order')->with('correlationName.rangeField' . ' ASC'); $this->assertEquals($this->selectMock, $this->model->current()); $this->assertEquals(0, $this->model->key()); } @@ -107,16 +105,17 @@ public function testCurrent() */ public function testIterations() { + $iterations = 0; + $this->connectionMock->expects($this->once()) ->method('fetchRow') ->willReturn(['cnt' => 105]); - $this->model->rewind(); + foreach ($this->model as $key) { + $this->assertEquals($this->selectMock, $key); + $iterations++; + }; - $this->assertEquals($this->selectMock, $this->model->current()); - $this->assertEquals(0, $this->model->key()); - $this->assertEquals($this->selectMock, $this->model->next()); - $this->assertTrue($this->model->valid()); - $this->assertEquals(1, $this->model->key()); + $this->assertEquals(10, $iterations); } } diff --git a/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php b/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php index b20f14a1d9bee..e96429cb42211 100644 --- a/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php @@ -168,4 +168,38 @@ public function testGenerateWithInvalidWithWildcard() )->willReturn($this->iteratorMock); $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100)); } + + /** + * Test success generate with non-unique strategy. + * @return void + */ + public function testGenerateWithNonUniqueStrategy() + { + $map = [ + [ + Select::FROM, + [ + 'cp' => ['joinType' => Select::FROM] + ] + ], + [ + Select::COLUMNS, + [ + ['cp', 'entity_id', 'product_id'] + ] + ] + ]; + $this->selectMock->expects($this->exactly(2))->method('getPart')->willReturnMap($map); + $this->factoryMock->expects($this->once())->method('create')->with( + [ + 'select' => $this->selectMock, + 'batchSize' => 100, + 'correlationName' => 'cp', + 'rangeField' => 'entity_id', + 'rangeFieldAlias' => 'product_id', + 'batchStrategy' => 'non_unique' + ] + )->willReturn($this->iteratorMock); + $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100, 'non_unique')); + } } From 421ad1639be8a3a547398c49d25277b12314f23e Mon Sep 17 00:00:00 2001 From: Oleksandr Osadchyi Date: Fri, 3 Feb 2017 17:39:52 +0200 Subject: [PATCH 13/18] MAGETWO-62616: Products are missed and total count is wrong on category page --- .../Magento/Framework/DB/Query/BatchRangeIterator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php b/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php index 576b545b236b3..baf0a3921c6be 100644 --- a/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php +++ b/lib/internal/Magento/Framework/DB/Query/BatchRangeIterator.php @@ -106,7 +106,7 @@ public function __construct( */ public function current() { - if (null == $this->currentSelect) { + if (null === $this->currentSelect) { $this->isValid = ($this->currentBatch + $this->batchSize) < $this->totalItemCount; $this->currentSelect = $this->initSelectObject(); } @@ -135,7 +135,7 @@ public function key() */ public function next() { - if (null == $this->currentSelect) { + if (null === $this->currentSelect) { $this->current(); } $this->isValid = ($this->batchSize + $this->currentBatch) < $this->totalItemCount; From 5a53b8ac28d866cd4e7c2c4bd401cfc6e91910a6 Mon Sep 17 00:00:00 2001 From: Oleksandr Osadchyi Date: Thu, 9 Feb 2017 18:11:04 +0200 Subject: [PATCH 14/18] MAGETWO-62616: Products are missed and total count is wrong on category page --- .../Category/Product/AbstractAction.php | 40 +++++--- .../Framework/DB/Adapter/Pdo/Mysql.php | 36 ++----- .../Framework/DB/Query/BatchIterator.php | 2 +- .../DB/Query/BatchIteratorFactory.php | 57 +++-------- .../DB/Query/BatchIteratorInterface.php | 68 +++++++++++++ .../Framework/DB/Query/BatchRangeIterator.php | 2 +- .../Magento/Framework/DB/Query/Generator.php | 95 +++++++++++++++++-- .../Test/Unit/DB/Query/GeneratorTest.php | 21 ++-- 8 files changed, 220 insertions(+), 101 deletions(-) create mode 100644 lib/internal/Magento/Framework/DB/Query/BatchIteratorInterface.php diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php index dbdd164486fab..b031e5bda2944 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php @@ -8,6 +8,8 @@ namespace Magento\Catalog\Model\Indexer\Category\Product; +use Magento\Framework\DB\Query\BatchIteratorInterface as BatchIteratorInterface; +use Magento\Framework\DB\Query\Generator as QueryGenerator; use Magento\Framework\App\ResourceConnection; use Magento\Framework\EntityManager\MetadataPool; @@ -102,6 +104,11 @@ abstract class AbstractAction */ protected $tempTreeIndexTableName; + /** + * @var QueryGenerator + */ + private $queryGenerator; + /** * @param ResourceConnection $resource * @param \Magento\Store\Model\StoreManagerInterface $storeManager @@ -110,12 +117,14 @@ abstract class AbstractAction public function __construct( \Magento\Framework\App\ResourceConnection $resource, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Catalog\Model\Config $config + \Magento\Catalog\Model\Config $config, + QueryGenerator $queryGenerator ) { $this->resource = $resource; $this->connection = $resource->getConnection(); $this->storeManager = $storeManager; $this->config = $config; + $this->queryGenerator = $queryGenerator; } /** @@ -309,16 +318,25 @@ protected function isRangingNeeded() * @param int $range * @return \Magento\Framework\DB\Select[] */ - protected function prepareSelectsByRange(\Magento\Framework\DB\Select $select, $field, $range = self::RANGE_CATEGORY_STEP) - { - return $this->isRangingNeeded() ? $this->connection->selectsByRange( - $field, - $select, - $range, - \Magento\Framework\DB\Query\BatchIteratorFactory::NON_UNIQUE_FIELD_ITERATOR - ) : [ - $select - ]; + protected function prepareSelectsByRange( + \Magento\Framework\DB\Select $select, + $field, + $range = self::RANGE_CATEGORY_STEP + ) { + if($this->isRangingNeeded()) { + $iterator = $this->queryGenerator->generate( + $field, + $select, + $range + ); + + $queries = []; + foreach ($iterator as $query) { + $queries[] = $query; + } + return $queries; + } + return [$select]; } /** diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php index 1185ade40ac3a..210ced53e5916 100644 --- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php +++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php @@ -3375,34 +3375,18 @@ public function insertFromSelect(Select $select, $table, array $fields = [], $mo } /** - * Get an array of select queries using the batching strategy - * - * Depending on the $batchStrategy parameter chooses a strategy. This strategy will be used to create - * an array of select queries. By default method use $batchStrategy parameter: - * \Magento\Framework\DB\Query\BatchIteratorFactory::UNIQUE_FIELD_ITERATOR. - * This parameter means that values of $rangeField have relationship - * one-to-one. - * If values of $rangeField is non-unique and have relationship one-to-many, - * than must be used next $batchStrategy parameter: - * \Magento\Framework\DB\Query\BatchIteratorFactory::NON_UNIQUE_FIELD_ITERATOR. - * - * @see BatchIteratorFactory - * @param string $rangeField - Field which is used for the range mechanism in select - * @param Select $select - * @param int $batchSize - Determines on how many parts will be divided - * the number of values in the select. - * @param string $batchStrategy - It determines which strategy is chosen + * Get insert queries in array for insert by range with step parameter + * + * @param string $rangeField + * @param \Magento\Framework\DB\Select $select + * @param int $stepCount * @return \Magento\Framework\DB\Select[] - * @throws LocalizedException Throws if incorrect "FROM" part in \Select exists + * @throws LocalizedException + * @deprecated */ - public function selectsByRange( - $rangeField, - \Magento\Framework\DB\Select $select, - $batchSize = 100, - $batchStrategy = \Magento\Framework\DB\Query\BatchIteratorFactory::UNIQUE_FIELD_ITERATOR - ) { - $iterator = $this->getQueryGenerator()->generate($rangeField, $select, $batchSize, $batchStrategy); - + public function selectsByRange($rangeField, \Magento\Framework\DB\Select $select, $stepCount = 100) + { + $iterator = $this->getQueryGenerator()->generate($rangeField, $select, $stepCount); $queries = []; foreach ($iterator as $query) { $queries[] = $query; diff --git a/lib/internal/Magento/Framework/DB/Query/BatchIterator.php b/lib/internal/Magento/Framework/DB/Query/BatchIterator.php index 4235eeb7a65e9..76e46d72d5c9c 100644 --- a/lib/internal/Magento/Framework/DB/Query/BatchIterator.php +++ b/lib/internal/Magento/Framework/DB/Query/BatchIterator.php @@ -11,7 +11,7 @@ /** * Query batch iterator */ -class BatchIterator implements \Iterator +class BatchIterator implements BatchIteratorInterface { /** * @var int diff --git a/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php b/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php index 218bfa413f8c9..563439bfb13e9 100644 --- a/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php +++ b/lib/internal/Magento/Framework/DB/Query/BatchIteratorFactory.php @@ -6,25 +6,10 @@ namespace Magento\Framework\DB\Query; /** - * Factory class for \Magento\Framework\DB\Query\BatchIterator | \Magento\Framework\DB\Query\BatchRangeIterator - * - * @see \Magento\Framework\DB\Query\BatchIterator - * @see \Magento\Framework\DB\Query\BatchRangeIterator + * Factory class for @see \Magento\Framework\DB\Query\BatchIterator */ class BatchIteratorFactory { - /** - * Constant which determine strategy to create iterator which will to process - * range field eg. entity_id with unique values. - */ - const UNIQUE_FIELD_ITERATOR = "unique"; - - /** - * Constant which determine strategy to create iterator which will to process - * range field with non-unique values. - */ - const NON_UNIQUE_FIELD_ITERATOR = "non_unqiue"; - /** * Object Manager instance * @@ -33,52 +18,34 @@ class BatchIteratorFactory private $objectManager = null; /** + * Instance name to create + * * @var string */ - private $uniqueIteratorInstanceName; - - /** - * @var string - */ - private $nonUniqueIteratorInstanceName; + private $instanceName = null; /** + * Factory constructor + * * @param \Magento\Framework\ObjectManagerInterface $objectManager - * @param \Magento\Framework\DB\Query\BatchIterator $uniqueIteratorInstanceName - * @param \Magento\Framework\DB\Query\BatchRangeIterator $nonUniqueIteratorInstanceName + * @param string $instanceName */ public function __construct( \Magento\Framework\ObjectManagerInterface $objectManager, - $nonUniqueIteratorInstanceName = \Magento\Framework\DB\Query\BatchRangeIterator::class, - $uniqueIteratorInstanceName = \Magento\Framework\DB\Query\BatchIterator::class + $instanceName = \Magento\Framework\DB\Query\BatchIterator::class ) { $this->objectManager = $objectManager; - $this->uniqueIteratorInstanceName = $uniqueIteratorInstanceName; - $this->nonUniqueIteratorInstanceName = $nonUniqueIteratorInstanceName; + $this->instanceName = $instanceName; } /** - * Create iterator instance with specified parameters + * Create class instance with specified parameters * - * Depending on the chosen strategy specified in $data['batchStrategy'] for selects, - * create an instance of iterator. - * By default will be created \Magento\Framework\DB\Query\BatchIterator class. - * This iterator provide interface for accessing sub-selects which was created from main select. - * - * If $data['batchStrategy'] == 'non_unqiue' value then we should to create BatchRangeIterator. This Iterator - * allows to operate with rangeField, which has one-to-many relations with other fields and is not unique. - * The main idea is to separate select to few parts in order to reduce the load of SQL server. - * - * @see \Magento\Framework\DB\Query\Generator * @param array $data - * @return \Iterator + * @return \Magento\Framework\DB\Query\BatchIteratorInterface */ public function create(array $data = []) { - if (isset($data['batchStrategy']) && $data['batchStrategy'] == self::NON_UNIQUE_FIELD_ITERATOR) { - return $this->objectManager->create($this->nonUniqueIteratorInstanceName, $data); - } - - return $this->objectManager->create($this->uniqueIteratorInstanceName, $data); + return $this->objectManager->create($this->instanceName, $data); } } diff --git a/lib/internal/Magento/Framework/DB/Query/BatchIteratorInterface.php b/lib/internal/Magento/Framework/DB/Query/BatchIteratorInterface.php new file mode 100644 index 0000000000000..067b4e76a91c3 --- /dev/null +++ b/lib/internal/Magento/Framework/DB/Query/BatchIteratorInterface.php @@ -0,0 +1,68 @@ +iteratorFactory = $iteratorFactory; + $this->rangeIteratorFactory = $rangeIteratorFactory; } /** * Generate select query list with predefined items count in each select item * - * Generates select parameters - batchSize, correlationName, rangeField, rangeFieldAlias, batchStrategy + * Generates select parameters - batchSize, correlationName, rangeField, rangeFieldAlias * to obtain instance of iterator. The behavior of the iterator will depend on the parameters passed to it. * For example: by default for $batchStrategy parameter used - * \Magento\Framework\DB\Query\BatchIteratorFactory::UNIQUE_FIELD_ITERATOR. This parameter is determine, what + * \Magento\Framework\DB\Query\BatchIteratorInterface::UNIQUE_FIELD_ITERATOR. This parameter is determine, what * instance of Iterator will be returned. * * Other params: @@ -43,21 +51,25 @@ public function __construct(BatchIteratorFactory $iteratorFactory) * rangeField - this is the basic field which used to split select. * rangeFieldAlias - alias of range field. * - * @see BatchIteratorFactory + * @see \Magento\Framework\DB\Query\BatchIteratorInterface * @param string $rangeField - Field which is used for the range mechanism in select * @param \Magento\Framework\DB\Select $select * @param int $batchSize - Determines on how many parts will be divided * the number of values in the select. * @param string $batchStrategy It determines which strategy is chosen - * @return \Iterator + * @return BatchIteratorInterface * @throws LocalizedException Throws if incorrect "FROM" part in \Select exists */ public function generate( $rangeField, \Magento\Framework\DB\Select $select, $batchSize = 100, - $batchStrategy = \Magento\Framework\DB\Query\BatchIteratorFactory::UNIQUE_FIELD_ITERATOR + $batchStrategy = \Magento\Framework\DB\Query\BatchIteratorInterface::UNIQUE_FIELD_ITERATOR ) { + if ($batchStrategy == \Magento\Framework\DB\Query\BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR) { + return $this->generateByRange($rangeField, $select, $batchSize); + } + $fromSelect = $select->getPart(\Magento\Framework\DB\Select::FROM); if (empty($fromSelect)) { throw new LocalizedException( @@ -87,13 +99,80 @@ public function generate( } return $this->iteratorFactory->create( + [ + 'select' => $select, + 'batchSize' => $batchSize, + 'correlationName' => $fieldCorrelationName, + 'rangeField' => $rangeField, + 'rangeFieldAlias' => $rangeFieldAlias + ] + ); + } + + /** + * Generate select query list with predefined items count in each select item. + * + * Generates select parameters - batchSize, correlationName, rangeField, rangeFieldAlias + * to obtain instance of BatchRangeIterator. + * + * Other params: + * select - represents the select object, that should be passed into Iterator. + * batchSize - sets the number of items in select. + * correlationName - is the base table involved in the select. + * rangeField - this is the basic field which used to split select. + * rangeFieldAlias - alias of range field. + * + * @see BatchRangeIterator + * @param string $rangeField - Field which is used for the range mechanism in select + * @param \Magento\Framework\DB\Select $select + * @param int $batchSize + * @return BatchIteratorInterface + * @throws LocalizedException Throws if incorrect "FROM" part in \Select exists + * @see \Magento\Framework\DB\Query\Generator + * @deprecated This is a temporary solution which is made due to the fact that we + * can't change method generate() in version 2.1 due to a backwards incompatibility. + * In 2.2 version need to use original method generate() with additional parameter. + */ + public function generateByRange( + $rangeField, + \Magento\Framework\DB\Select $select, + $batchSize = 100 + ) { + $fromSelect = $select->getPart(\Magento\Framework\DB\Select::FROM); + if (empty($fromSelect)) { + throw new LocalizedException( + new \Magento\Framework\Phrase('Select object must have correct "FROM" part') + ); + } + + $fieldCorrelationName = ''; + foreach ($fromSelect as $correlationName => $fromPart) { + if ($fromPart['joinType'] == \Magento\Framework\DB\Select::FROM) { + $fieldCorrelationName = $correlationName; + break; + } + } + + $columns = $select->getPart(\Magento\Framework\DB\Select::COLUMNS); + /** + * Calculate $rangeField alias + */ + $rangeFieldAlias = $rangeField; + foreach ($columns as $column) { + list($table, $columnName, $alias) = $column; + if ($table == $fieldCorrelationName && $columnName == $rangeField) { + $rangeFieldAlias = $alias ?: $rangeField; + break; + } + } + + return $this->rangeIteratorFactory->create( [ 'select' => $select, 'batchSize' => $batchSize, 'correlationName' => $fieldCorrelationName, 'rangeField' => $rangeField, 'rangeFieldAlias' => $rangeFieldAlias, - 'batchStrategy' => $batchStrategy ] ); } diff --git a/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php b/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php index e96429cb42211..1355d62e891d3 100644 --- a/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php @@ -7,6 +7,7 @@ use Magento\Framework\DB\Query\Generator; use Magento\Framework\DB\Query\BatchIteratorFactory; +use Magento\Framework\DB\Query\BatchRangeIteratorFactory; use Magento\Framework\DB\Select; use Magento\Framework\DB\Query\BatchIterator; @@ -32,15 +33,21 @@ class GeneratorTest extends \PHPUnit_Framework_TestCase */ private $iteratorMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $rangeFactoryMock; + /** * Setup test dependencies. */ protected function setUp() { $this->factoryMock = $this->getMock(BatchIteratorFactory::class, [], [], '', false, false); + $this->rangeFactoryMock = $this->getMock(BatchRangeIteratorFactory::class, ['create'], [], '', false, false); $this->selectMock = $this->getMock(Select::class, [], [], '', false, false); $this->iteratorMock = $this->getMock(BatchIterator::class, [], [], '', false, false); - $this->model = new Generator($this->factoryMock); + $this->model = new Generator($this->factoryMock, $this->rangeFactoryMock); } /** @@ -70,8 +77,7 @@ public function testGenerate() 'batchSize' => 100, 'correlationName' => 'cp', 'rangeField' => 'entity_id', - 'rangeFieldAlias' => 'product_id', - 'batchStrategy' => 'unique' + 'rangeFieldAlias' => 'product_id' ] )->willReturn($this->iteratorMock); $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100)); @@ -127,8 +133,7 @@ public function testGenerateWithRangeFieldWithoutAlias() 'batchSize' => 100, 'correlationName' => 'cp', 'rangeField' => 'entity_id', - 'rangeFieldAlias' => 'entity_id', - 'batchStrategy' => 'unique' + 'rangeFieldAlias' => 'entity_id' ] )->willReturn($this->iteratorMock); $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100)); @@ -162,8 +167,7 @@ public function testGenerateWithInvalidWithWildcard() 'batchSize' => 100, 'correlationName' => 'cp', 'rangeField' => 'entity_id', - 'rangeFieldAlias' => 'entity_id', - 'batchStrategy' => 'unique' + 'rangeFieldAlias' => 'entity_id' ] )->willReturn($this->iteratorMock); $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100)); @@ -196,8 +200,7 @@ public function testGenerateWithNonUniqueStrategy() 'batchSize' => 100, 'correlationName' => 'cp', 'rangeField' => 'entity_id', - 'rangeFieldAlias' => 'product_id', - 'batchStrategy' => 'non_unique' + 'rangeFieldAlias' => 'product_id' ] )->willReturn($this->iteratorMock); $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100, 'non_unique')); From fe5525285ac4e05cd3117e00a85903123f95b645 Mon Sep 17 00:00:00 2001 From: Sergey Semenov Date: Thu, 9 Feb 2017 19:12:49 +0200 Subject: [PATCH 15/18] MAGETWO-61830: Modify ConfigurableProduct datasets --- .../Test/Repository/ConfigurableProduct.xml | 34 +++++++++++++++++ .../ConfigurableAttributesData.xml | 38 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml index 07d85bd5b3491..7660a6ea94f44 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml @@ -241,6 +241,40 @@ + + Test configurable product with color and size %isolation% + sku_test_configurable_product_%isolation% + This item has weight + 30 + Yes + Catalog, Search + + taxable_goods + + configurable-product-%isolation% + + color_for_promo_rules + + + In Stock + + + + default + + + + default + + + configurable_default + + + 40 + price_40 + + + Test configurable product %isolation% sku_test_configurable_product_%isolation% diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/ConfigurableAttributesData.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/ConfigurableAttributesData.xml index 2c2b02990b45a..9046294258b54 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/ConfigurableAttributesData.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/ConfigurableAttributesData.xml @@ -563,6 +563,44 @@ + + + + + + 5.00 + Yes + + + 10.00 + Yes + + + 15.00 + Yes + + + + + + catalogProductAttribute::color_for_promo_rules + + + + 1 + 1 + + + 1 + 1 + + + 1 + 1 + + + + From adf169c4295bb8db4532d2da27b98c61fac890ef Mon Sep 17 00:00:00 2001 From: Oleksandr Osadchyi Date: Mon, 13 Feb 2017 13:47:10 +0200 Subject: [PATCH 16/18] MAGETWO-62616: Products are missed and total count is wrong on category page --- .../Catalog/Model/Indexer/Category/Product/AbstractAction.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php index b031e5bda2944..c719c2a5fa6f5 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php @@ -327,7 +327,8 @@ protected function prepareSelectsByRange( $iterator = $this->queryGenerator->generate( $field, $select, - $range + $range, + \Magento\Framework\DB\Query\BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR ); $queries = []; From 3e4a7cec63e4f3a52e48c3772590ca81307bf9da Mon Sep 17 00:00:00 2001 From: Oleksandr Osadchyi Date: Tue, 14 Feb 2017 13:52:54 +0200 Subject: [PATCH 17/18] MAGETWO-62616: Products are missed and total count is wrong on category page --- lib/internal/Magento/Framework/DB/Query/Generator.php | 1 + .../Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/DB/Query/Generator.php b/lib/internal/Magento/Framework/DB/Query/Generator.php index 813c7722eba49..793d6c9667c00 100644 --- a/lib/internal/Magento/Framework/DB/Query/Generator.php +++ b/lib/internal/Magento/Framework/DB/Query/Generator.php @@ -26,6 +26,7 @@ class Generator * Initialize dependencies. * * @param BatchIteratorFactory $iteratorFactory + * @param BatchRangeIteratorFactory $rangeIteratorFactory */ public function __construct( BatchIteratorFactory $iteratorFactory, diff --git a/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php b/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php index 1355d62e891d3..28e06506f66ce 100644 --- a/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php +++ b/lib/internal/Magento/Framework/Test/Unit/DB/Query/GeneratorTest.php @@ -203,6 +203,9 @@ public function testGenerateWithNonUniqueStrategy() 'rangeFieldAlias' => 'product_id' ] )->willReturn($this->iteratorMock); - $this->assertEquals($this->iteratorMock, $this->model->generate('entity_id', $this->selectMock, 100, 'non_unique')); + $this->assertEquals( + $this->iteratorMock, + $this->model->generate('entity_id', $this->selectMock, 100, 'non_unique') + ); } } From 97ca0eff89db8a4f391792e81924203959d1fc49 Mon Sep 17 00:00:00 2001 From: Oleksandr Osadchyi Date: Wed, 15 Feb 2017 17:40:17 +0200 Subject: [PATCH 18/18] MAGETWO-62616: Products are missed and total count is wrong on category page --- .../Model/Indexer/Category/Product/AbstractAction.php | 6 ++++-- lib/internal/Magento/Framework/DB/Query/Generator.php | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php index c719c2a5fa6f5..3a6f833a5a09c 100644 --- a/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php +++ b/app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php @@ -113,18 +113,20 @@ abstract class AbstractAction * @param ResourceConnection $resource * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Catalog\Model\Config $config + * @param QueryGenerator $queryGenerator */ public function __construct( \Magento\Framework\App\ResourceConnection $resource, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Catalog\Model\Config $config, - QueryGenerator $queryGenerator + QueryGenerator $queryGenerator = null ) { $this->resource = $resource; $this->connection = $resource->getConnection(); $this->storeManager = $storeManager; $this->config = $config; - $this->queryGenerator = $queryGenerator; + $this->queryGenerator = $queryGenerator ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(QueryGenerator::class); } /** diff --git a/lib/internal/Magento/Framework/DB/Query/Generator.php b/lib/internal/Magento/Framework/DB/Query/Generator.php index 793d6c9667c00..4e3da17e21908 100644 --- a/lib/internal/Magento/Framework/DB/Query/Generator.php +++ b/lib/internal/Magento/Framework/DB/Query/Generator.php @@ -30,10 +30,11 @@ class Generator */ public function __construct( BatchIteratorFactory $iteratorFactory, - BatchRangeIteratorFactory $rangeIteratorFactory + BatchRangeIteratorFactory $rangeIteratorFactory = null ) { $this->iteratorFactory = $iteratorFactory; - $this->rangeIteratorFactory = $rangeIteratorFactory; + $this->rangeIteratorFactory = $rangeIteratorFactory ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\DB\Query\BatchRangeIteratorFactory::class); } /**