diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider.php index a2242ff0f355b3bca9d332ca5b59ec052cd61cb7..5887c76e8ddc23ef1ce51e822b46f57400d513f6 100644 --- a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider.php @@ -3,10 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation; use Magento\Catalog\Model\Product; -use Magento\CatalogInventory\Model\Stock; +use Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider\QueryBuilder; use Magento\Customer\Model\Session; use Magento\Eav\Model\Config; use Magento\Framework\App\ResourceConnection; @@ -19,7 +20,7 @@ use Magento\Framework\Search\Request\BucketInterface; use Magento\Framework\App\ObjectManager; /** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * DataProvider for Catalog search Mysql. */ class DataProvider implements DataProviderInterface { @@ -48,23 +49,31 @@ class DataProvider implements DataProviderInterface */ private $connection; + /** + * @var QueryBuilder; + */ + private $queryBuilder; + /** * @param Config $eavConfig * @param ResourceConnection $resource * @param ScopeResolverInterface $scopeResolver * @param Session $customerSession + * @param QueryBuilder|null $queryBuilder */ public function __construct( Config $eavConfig, ResourceConnection $resource, ScopeResolverInterface $scopeResolver, - Session $customerSession + Session $customerSession, + QueryBuilder $queryBuilder = null ) { $this->eavConfig = $eavConfig; $this->resource = $resource; $this->connection = $resource->getConnection(); $this->scopeResolver = $scopeResolver; $this->customerSession = $customerSession; + $this->queryBuilder = $queryBuilder ?: ObjectManager::getInstance()->get(QueryBuilder::class); } /** @@ -79,47 +88,13 @@ class DataProvider implements DataProviderInterface $attribute = $this->eavConfig->getAttribute(Product::ENTITY, $bucket->getField()); - $select = $this->getSelect(); - - $select->joinInner( - ['entities' => $entityIdsTable->getName()], - 'main_table.entity_id = entities.entity_id', - [] + $select = $this->queryBuilder->build( + $attribute, + $entityIdsTable->getName(), + $currentScope, + $this->customerSession->getCustomerGroupId() ); - if ($attribute->getAttributeCode() === 'price') { - /** @var \Magento\Store\Model\Store $store */ - $store = $this->scopeResolver->getScope($currentScope); - if (!$store instanceof \Magento\Store\Model\Store) { - throw new \RuntimeException('Illegal scope resolved'); - } - $table = $this->resource->getTableName('catalog_product_index_price'); - $select->from(['main_table' => $table], null) - ->columns([BucketInterface::FIELD_VALUE => 'main_table.min_price']) - ->where('main_table.customer_group_id = ?', $this->customerSession->getCustomerGroupId()) - ->where('main_table.website_id = ?', $store->getWebsiteId()); - } else { - $currentScopeId = $this->scopeResolver->getScope($currentScope) - ->getId(); - $table = $this->resource->getTableName( - 'catalog_product_index_eav' . ($attribute->getBackendType() === 'decimal' ? '_decimal' : '') - ); - $subSelect = $select; - $subSelect->from(['main_table' => $table], ['main_table.entity_id', 'main_table.value']) - ->distinct() - ->joinLeft( - ['stock_index' => $this->resource->getTableName('cataloginventory_stock_status')], - 'main_table.source_id = stock_index.product_id', - [] - ) - ->where('main_table.attribute_id = ?', $attribute->getAttributeId()) - ->where('main_table.store_id = ? ', $currentScopeId) - ->where('stock_index.stock_status = ?', Stock::STOCK_IN_STOCK); - $parentSelect = $this->getSelect(); - $parentSelect->from(['main_table' => $subSelect], ['main_table.value']); - $select = $parentSelect; - } - return $select; } @@ -130,12 +105,4 @@ class DataProvider implements DataProviderInterface { return $this->connection->fetchAssoc($select); } - - /** - * @return Select - */ - private function getSelect() - { - return $this->connection->select(); - } } diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilder.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilder.php new file mode 100644 index 0000000000000000000000000000000000000000..ca077ef7227d5822832005c57fe1ac7451c37e7c --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilder.php @@ -0,0 +1,148 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider; + +use Magento\CatalogInventory\Model\Configuration as CatalogInventoryConfiguration; +use Magento\CatalogInventory\Model\Stock; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\App\ScopeResolverInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; +use Magento\Framework\Search\Request\BucketInterface; + +/** + * Attribute query builder + */ +class QueryBuilder +{ + /** + * @var Resource + */ + private $resource; + + /** + * @var ScopeResolverInterface + */ + private $scopeResolver; + + /** + * @var CatalogInventoryConfiguration + */ + private $inventoryConfig; + + /** + * @param ResourceConnection $resource + * @param ScopeResolverInterface $scopeResolver + * @param CatalogInventoryConfiguration $inventoryConfig + */ + public function __construct( + ResourceConnection $resource, + ScopeResolverInterface $scopeResolver, + CatalogInventoryConfiguration $inventoryConfig + ) { + $this->resource = $resource; + $this->scopeResolver = $scopeResolver; + $this->inventoryConfig = $inventoryConfig; + } + + /** + * Build select. + * + * @param AbstractAttribute $attribute + * @param string $tableName + * @param int $currentScope + * @param int $customerGroupId + * + * @return Select + */ + public function build( + AbstractAttribute $attribute, + string $tableName, + int $currentScope, + int $customerGroupId + ) : Select { + $select = $this->resource->getConnection()->select(); + $select->joinInner( + ['entities' => $tableName], + 'main_table.entity_id = entities.entity_id', + [] + ); + + if ($attribute->getAttributeCode() === 'price') { + return $this->buildQueryForPriceAttribute($currentScope, $customerGroupId, $select); + } + + return $this->buildQueryForAttribute($currentScope, $attribute, $select); + } + + /** + * Build select for price attribute. + * + * @param int $currentScope + * @param int $customerGroupId + * @param Select $select + * + * @return Select + */ + private function buildQueryForPriceAttribute( + int $currentScope, + int $customerGroupId, + Select $select + ) : Select { + /** @var \Magento\Store\Model\Store $store */ + $store = $this->scopeResolver->getScope($currentScope); + if (!$store instanceof \Magento\Store\Model\Store) { + throw new \RuntimeException('Illegal scope resolved'); + } + + $table = $this->resource->getTableName('catalog_product_index_price'); + $select->from(['main_table' => $table], null) + ->columns([BucketInterface::FIELD_VALUE => 'main_table.min_price']) + ->where('main_table.customer_group_id = ?', $customerGroupId) + ->where('main_table.website_id = ?', $store->getWebsiteId()); + + return $select; + } + + /** + * Build select for attribute. + * + * @param int $currentScope + * @param AbstractAttribute $attribute + * @param Select $select + * + * @return Select + */ + private function buildQueryForAttribute( + int $currentScope, + AbstractAttribute $attribute, + Select $select + ) : Select { + $currentScopeId = $this->scopeResolver->getScope($currentScope)->getId(); + $table = $this->resource->getTableName( + 'catalog_product_index_eav' . ($attribute->getBackendType() === 'decimal' ? '_decimal' : '') + ); + $select->from(['main_table' => $table], ['main_table.entity_id', 'main_table.value']) + ->distinct() + ->joinLeft( + ['stock_index' => $this->resource->getTableName('cataloginventory_stock_status')], + 'main_table.source_id = stock_index.product_id', + [] + ) + ->where('main_table.attribute_id = ?', $attribute->getAttributeId()) + ->where('main_table.store_id = ? ', $currentScopeId); + + if (!$this->inventoryConfig->isShowOutOfStock($currentScopeId)) { + $select->where('stock_index.stock_status = ?', Stock::STOCK_IN_STOCK); + } + + $parentSelect = $this->resource->getConnection()->select(); + $parentSelect->from(['main_table' => $select], ['main_table.value']); + return $parentSelect; + } +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilderTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b52664df749fe0954875a7241dc80636aae45b1f --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Aggregation/DataProvider/QueryBuilderTest.php @@ -0,0 +1,154 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\CatalogSearch\Test\Unit\Model\Adapter\Mysql\Aggregation\DataProvider; + +use Magento\CatalogInventory\Model\Configuration as CatalogInventoryConfiguration; +use Magento\CatalogInventory\Model\Stock; +use Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider\QueryBuilder; +use Magento\Eav\Model\Entity\Attribute\AbstractAttribute; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\App\ScopeResolverInterface; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\DB\Select; +use Magento\Store\Model\Store; + +/** + * Test for Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider\QueryBuilder. + */ +class QueryBuilderTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var QueryBuilder + */ + private $model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $resourceConnectionMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $scopeResolverMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $adapterMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $inventoryConfigMock; + + protected function setUp() + { + $this->resourceConnectionMock = $this->createMock(ResourceConnection::class); + $this->scopeResolverMock = $this->createMock(ScopeResolverInterface::class); + $this->adapterMock = $this->createMock(AdapterInterface::class); + $this->inventoryConfigMock = $this->createMock(CatalogInventoryConfiguration::class); + + $this->resourceConnectionMock->expects($this->atLeastOnce()) + ->method('getConnection') + ->willReturn($this->adapterMock); + + $this->model = new QueryBuilder( + $this->resourceConnectionMock, + $this->scopeResolverMock, + $this->inventoryConfigMock + ); + } + + public function testBuildWithPriceAttributeCode() + { + $tableName = 'test_table'; + $scope = 1; + $selectMock = $this->createMock(Select::class); + $attributeMock = $this->createMock(AbstractAttribute::class); + $storeMock = $this->createMock(Store::class); + + $this->adapterMock->expects($this->atLeastOnce())->method('select') + ->willReturn($selectMock); + $selectMock->expects($this->once())->method('joinInner') + ->with(['entities' => $tableName], 'main_table.entity_id = entities.entity_id', []); + $attributeMock->expects($this->once())->method('getAttributeCode') + ->willReturn('price'); + $this->scopeResolverMock->expects($this->once())->method('getScope') + ->with($scope)->willReturn($storeMock); + $storeMock->expects($this->once())->method('getWebsiteId')->willReturn(1); + $this->resourceConnectionMock->expects($this->once())->method('getTableName') + ->with('catalog_product_index_price')->willReturn('catalog_product_index_price'); + $selectMock->expects($this->once())->method('from') + ->with(['main_table' => 'catalog_product_index_price'], null) + ->willReturn($selectMock); + $selectMock->expects($this->once())->method('columns') + ->with(['value' => 'main_table.min_price']) + ->willReturn($selectMock); + $selectMock->expects($this->exactly(2))->method('where') + ->withConsecutive( + ['main_table.customer_group_id = ?', 1], + ['main_table.website_id = ?', 1] + )->willReturn($selectMock); + + $this->model->build($attributeMock, $tableName, $scope, 1); + } + + public function testBuildWithNotPriceAttributeCode() + { + $tableName = 'test_table'; + $scope = 1; + $selectMock = $this->createMock(Select::class); + $attributeMock = $this->createMock(AbstractAttribute::class); + $storeMock = $this->createMock(Store::class); + + $this->adapterMock->expects($this->atLeastOnce())->method('select') + ->willReturn($selectMock); + $selectMock->expects($this->once())->method('joinInner') + ->with(['entities' => $tableName], 'main_table.entity_id = entities.entity_id', []); + $attributeMock->expects($this->once())->method('getBackendType') + ->willReturn('decimal'); + $this->scopeResolverMock->expects($this->once())->method('getScope') + ->with($scope)->willReturn($storeMock); + $storeMock->expects($this->once())->method('getId')->willReturn(1); + $this->resourceConnectionMock->expects($this->exactly(2))->method('getTableName') + ->withConsecutive( + ['catalog_product_index_eav_decimal'], + ['cataloginventory_stock_status'] + )->willReturnOnConsecutiveCalls( + 'catalog_product_index_eav_decimal', + 'cataloginventory_stock_status' + ); + + $selectMock->expects($this->exactly(2))->method('from') + ->withConsecutive( + [ + ['main_table' => 'catalog_product_index_eav_decimal'], + ['main_table.entity_id', 'main_table.value'] + ], + [['main_table' => $selectMock], ['main_table.value']] + ) + ->willReturn($selectMock); + $selectMock->expects($this->once())->method('distinct')->willReturn($selectMock); + $selectMock->expects($this->once())->method('joinLeft') + ->with( + ['stock_index' => 'cataloginventory_stock_status'], + 'main_table.source_id = stock_index.product_id', + [] + )->willReturn($selectMock); + $attributeMock->expects($this->once())->method('getAttributeId')->willReturn(3); + $selectMock->expects($this->exactly(3))->method('where') + ->withConsecutive( + ['main_table.attribute_id = ?', 3], + ['main_table.store_id = ? ', 1], + ['stock_index.stock_status = ?', Stock::STOCK_IN_STOCK] + )->willReturn($selectMock); + $this->inventoryConfigMock->expects($this->once())->method('isShowOutOfStock')->with(1)->willReturn(false); + + $this->model->build($attributeMock, $tableName, $scope, 1); + } +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Aggregation/DataProviderTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Aggregation/DataProviderTest.php index 4305bc5cb0706c69ba4201e23360b75a80a08afd..7c558f60b7433b90b35060254cd94b5ffc0a38ac 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Aggregation/DataProviderTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Aggregation/DataProviderTest.php @@ -7,6 +7,7 @@ namespace Magento\CatalogSearch\Test\Unit\Model\Adapter\Mysql\Aggregation; use Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider; +use Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider\QueryBuilder; use Magento\Eav\Model\Config; use Magento\Customer\Model\Session; use Magento\Framework\App\ResourceConnection; @@ -21,6 +22,8 @@ use Magento\Catalog\Model\Product; use Magento\Framework\DB\Ddl\Table; /** + * Test for Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider. + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DataProviderTest extends \PHPUnit\Framework\TestCase @@ -55,6 +58,11 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase */ private $adapterMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $queryBuilderMock; + protected function setUp() { $this->eavConfigMock = $this->createMock(Config::class); @@ -63,72 +71,53 @@ class DataProviderTest extends \PHPUnit\Framework\TestCase $this->sessionMock = $this->createMock(Session::class); $this->adapterMock = $this->createMock(AdapterInterface::class); $this->resourceConnectionMock->expects($this->once())->method('getConnection')->willReturn($this->adapterMock); + $this->queryBuilderMock = $this->createMock(QueryBuilder::class); $this->model = new DataProvider( $this->eavConfigMock, $this->resourceConnectionMock, $this->scopeResolverMock, - $this->sessionMock + $this->sessionMock, + $this->queryBuilderMock ); } - public function testGetDataSetUsesFrontendPriceIndexerTableIfAttributeIsPrice() + public function testGetDataSet() { $storeId = 1; - $attributeCode = 'price'; + $attributeCode = 'my_decimal'; $scopeMock = $this->createMock(Store::class); $scopeMock->expects($this->any())->method('getId')->willReturn($storeId); + $dimensionMock = $this->createMock(Dimension::class); $dimensionMock->expects($this->any())->method('getValue')->willReturn($storeId); + $this->scopeResolverMock->expects($this->any())->method('getScope')->with($storeId)->willReturn($scopeMock); $bucketMock = $this->createMock(BucketInterface::class); $bucketMock->expects($this->once())->method('getField')->willReturn($attributeCode); + $attributeMock = $this->createMock(Attribute::class); - $attributeMock->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode); - $this->eavConfigMock->expects($this->once()) - ->method('getAttribute')->with(Product::ENTITY, $attributeCode) - ->willReturn($attributeMock); + $this->eavConfigMock->expects($this->once())->method('getAttribute') + ->with(Product::ENTITY, $attributeCode)->willReturn($attributeMock); - $selectMock = $this->createMock(Select::class); - $selectMock->expects($this->any())->method('from')->willReturnSelf(); - $selectMock->expects($this->any())->method('where')->willReturnSelf(); - $selectMock->expects($this->any())->method('columns')->willReturnSelf(); - $this->adapterMock->expects($this->once())->method('select')->willReturn($selectMock); $tableMock = $this->createMock(Table::class); + $tableMock->expects($this->once())->method('getName')->willReturn('test'); + + $this->sessionMock->expects($this->once())->method('getCustomerGroupId')->willReturn(1); + + $this->queryBuilderMock->expects($this->once())->method('build') + ->with($attributeMock, 'test', $storeId, 1); $this->model->getDataSet($bucketMock, ['scope' => $dimensionMock], $tableMock); } - public function testGetDataSetUsesFrontendPriceIndexerTableForDecimalAttributes() + public function testExecute() { - $storeId = 1; - $attributeCode = 'my_decimal'; - - $scopeMock = $this->createMock(Store::class); - $scopeMock->expects($this->any())->method('getId')->willReturn($storeId); - $dimensionMock = $this->createMock(Dimension::class); - $dimensionMock->expects($this->any())->method('getValue')->willReturn($storeId); - $this->scopeResolverMock->expects($this->any())->method('getScope')->with($storeId)->willReturn($scopeMock); - - $bucketMock = $this->createMock(BucketInterface::class); - $bucketMock->expects($this->once())->method('getField')->willReturn($attributeCode); - $attributeMock = $this->createMock(Attribute::class); - $attributeMock->expects($this->any())->method('getAttributeCode')->willReturn($attributeCode); - $this->eavConfigMock->expects($this->once()) - ->method('getAttribute')->with(Product::ENTITY, $attributeCode) - ->willReturn($attributeMock); - $selectMock = $this->createMock(Select::class); - $selectMock->expects($this->any())->method('from')->willReturnSelf(); - $selectMock->expects($this->any())->method('distinct')->willReturnSelf(); - $selectMock->expects($this->any())->method('where')->willReturnSelf(); - $selectMock->expects($this->any())->method('columns')->willReturnSelf(); - $selectMock->expects($this->any())->method('joinLeft')->willReturnSelf(); - $selectMock->expects($this->any())->method('group')->willReturnSelf(); - $this->adapterMock->expects($this->any())->method('select')->willReturn($selectMock); - $tableMock = $this->createMock(Table::class); - $this->model->getDataSet($bucketMock, ['scope' => $dimensionMock], $tableMock); + $this->adapterMock->expects($this->once())->method('fetchAssoc')->with($selectMock); + + $this->model->execute($selectMock); } } diff --git a/app/code/Magento/Directory/Setup/UpgradeData.php b/app/code/Magento/Directory/Setup/UpgradeData.php index aa0f81a32fff06a84eeadc3636d1ef01b3f9b1d7..4ee9ea33673d736e253b7ac3b373365c5b9d6439 100644 --- a/app/code/Magento/Directory/Setup/UpgradeData.php +++ b/app/code/Magento/Directory/Setup/UpgradeData.php @@ -41,23 +41,21 @@ class UpgradeData implements UpgradeDataInterface public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) { if (version_compare($context->getVersion(), '2.0.1', '<')) { - $this->addCroatia($setup); + $this->addCountryRegions($setup, $this->getDataForCroatia()); + } + if (version_compare($context->getVersion(), '2.0.2', '<')) { + $this->addCountryRegions($setup, $this->getDataForIndia()); } } /** - * Add Croatia and it's states to appropriate tables. + * Croatian states data. * - * @param ModuleDataSetupInterface $setup - * @return void + * @return array */ - private function addCroatia($setup) + private function getDataForCroatia() { - /** - * Fill table directory/country_region - * Fill table directory/country_region_name for en_US locale - */ - $data = [ + return [ ['HR', 'HR-01', 'ZagrebaÄka županija'], ['HR', 'HR-02', 'Krapinsko-zagorska županija'], ['HR', 'HR-03', 'SisaÄko-moslavaÄka županija'], @@ -80,6 +78,68 @@ class UpgradeData implements UpgradeDataInterface ['HR', 'HR-20', 'MeÄ‘imurska županija'], ['HR', 'HR-21', 'Grad Zagreb'] ]; + } + + /** + * Indian states data. + * + * @return array + */ + private function getDataForIndia() + { + return [ + ['IN', 'AN', 'Andaman and Nicobar Islands'], + ['IN', 'AP', 'Andhra Pradesh'], + ['IN', 'AR', 'Arunachal Pradesh'], + ['IN', 'AS', 'Assam'], + ['IN', 'BR', 'Bihar'], + ['IN', 'CH', 'Chandigarh'], + ['IN', 'CT', 'Chhattisgarh'], + ['IN', 'DN', 'Dadra and Nagar Haveli'], + ['IN', 'DD', 'Daman and Diu'], + ['IN', 'DL', 'Delhi'], + ['IN', 'GA', 'Goa'], + ['IN', 'GJ', 'Gujarat'], + ['IN', 'HR', 'Haryana'], + ['IN', 'HP', 'Himachal Pradesh'], + ['IN', 'JK', 'Jammu and Kashmir'], + ['IN', 'JH', 'Jharkhand'], + ['IN', 'KA', 'Karnataka'], + ['IN', 'KL', 'Kerala'], + ['IN', 'LD', 'Lakshadweep'], + ['IN', 'MP', 'Madhya Pradesh'], + ['IN', 'MH', 'Maharashtra'], + ['IN', 'MN', 'Manipur'], + ['IN', 'ML', 'Meghalaya'], + ['IN', 'MZ', 'Mizoram'], + ['IN', 'NL', 'Nagaland'], + ['IN', 'OR', 'Odisha'], + ['IN', 'PY', 'Puducherry'], + ['IN', 'PB', 'Punjab'], + ['IN', 'RJ', 'Rajasthan'], + ['IN', 'SK', 'Sikkim'], + ['IN', 'TN', 'Tamil Nadu'], + ['IN', 'TG', 'Telangana'], + ['IN', 'TR', 'Tripura'], + ['IN', 'UP', 'Uttar Pradesh'], + ['IN', 'UT', 'Uttarakhand'], + ['IN', 'WB', 'West Bengal'] + ]; + } + + /** + * Add country regions data to appropriate tables. + * + * @param ModuleDataSetupInterface $setup + * @param array $data + * @return void + */ + private function addCountryRegions(ModuleDataSetupInterface $setup, array $data) + { + /** + * Fill table directory/country_region + * Fill table directory/country_region_name for en_US locale + */ foreach ($data as $row) { $bind = ['country_id' => $row[0], 'code' => $row[1], 'default_name' => $row[2]]; $setup->getConnection()->insert($setup->getTable('directory_country_region'), $bind); diff --git a/app/code/Magento/Directory/etc/module.xml b/app/code/Magento/Directory/etc/module.xml index 2711a91577a9ab7ad6dc8b8c764f3306ba2b57c4..a3735ca6ddde1113cd012aab75564db342336b7f 100644 --- a/app/code/Magento/Directory/etc/module.xml +++ b/app/code/Magento/Directory/etc/module.xml @@ -6,7 +6,7 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> - <module name="Magento_Directory" setup_version="2.0.1"> + <module name="Magento_Directory" setup_version="2.0.2"> <sequence> <module name="Magento_Store"/> </sequence> diff --git a/lib/internal/Magento/Framework/View/Element/UiComponentFactory.php b/lib/internal/Magento/Framework/View/Element/UiComponentFactory.php index 94d84dd0560df92adeb99c81d92fd627b1291288..93fe88a30f0650f0a400f8ad39322b7d5d80e7aa 100755 --- a/lib/internal/Magento/Framework/View/Element/UiComponentFactory.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponentFactory.php @@ -147,6 +147,14 @@ class UiComponentFactory extends DataObject } $components = array_filter($components); $componentArguments['components'] = $components; + + /** + * Prevent passing ACL restricted blocks to htmlContent constructor + */ + if (isset($componentArguments['block']) && !$componentArguments['block']) { + return null; + } + if (!isset($componentArguments['context'])) { $componentArguments['context'] = $renderContext; } diff --git a/lib/internal/Magento/Framework/Webapi/Rest/Response/RendererFactory.php b/lib/internal/Magento/Framework/Webapi/Rest/Response/RendererFactory.php index c86b97b0fa8e7f8a78ad4b16a434ad69d012446c..547b8fcb03640c43e38f6b3c309d76cfc0b87b61 100644 --- a/lib/internal/Magento/Framework/Webapi/Rest/Response/RendererFactory.php +++ b/lib/internal/Magento/Framework/Webapi/Rest/Response/RendererFactory.php @@ -72,14 +72,9 @@ class RendererFactory $acceptTypes = [$acceptTypes]; } foreach ($acceptTypes as $acceptType) { - foreach ($this->_renders as $rendererConfig) { - $rendererType = $rendererConfig['type']; - if ($acceptType == $rendererType || $acceptType == current( - explode('/', $rendererType) - ) . '/*' || $acceptType == '*/*' - ) { - return $rendererConfig['model']; - } + $renderer = $this->getRendererConfig($acceptType); + if ($renderer !== null) { + return $renderer['model']; } } /** If server does not have renderer for any of the accepted types it SHOULD send 406 (not acceptable). */ @@ -93,4 +88,30 @@ class RendererFactory \Magento\Framework\Webapi\Exception::HTTP_NOT_ACCEPTABLE ); } + + /** + * Get renderer config by accept type. + * + * @param string $acceptType + * @return array|null + */ + private function getRendererConfig($acceptType) + { + // If Accept type = '*/*' then return default renderer. + if ($acceptType == '*/*' && isset($this->_renders['default'])) { + return $this->_renders['default']; + } + + foreach ($this->_renders as $rendererConfig) { + $rendererType = $rendererConfig['type']; + if ($acceptType == $rendererType + || $acceptType == current(explode('/', $rendererType)) . '/*' + || $acceptType == '*/*' + ) { + return $rendererConfig; + } + } + + return null; + } } diff --git a/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Response/RendererFactoryTest.php b/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Response/RendererFactoryTest.php index ba460c5a5f6e66e7cd2d8efae56bd8ffa01d5ca4..dd7aa845f3091805a841f0324580f0d12634bc49 100644 --- a/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Response/RendererFactoryTest.php +++ b/lib/internal/Magento/Framework/Webapi/Test/Unit/Rest/Response/RendererFactoryTest.php @@ -26,11 +26,18 @@ class RendererFactoryTest extends \PHPUnit\Framework\TestCase )->disableOriginalConstructor()->getMock(); $renders = [ - 'default' => ['type' => '*/*', 'model' => \Magento\Framework\Webapi\Rest\Response\Renderer\Json::class], + 'application_xml' => [ + 'type' => 'application/xml', + 'model' => \Magento\Framework\Webapi\Rest\Response\Renderer\Xml::class, + ], 'application_json' => [ 'type' => 'application/json', 'model' => \Magento\Framework\Webapi\Rest\Response\Renderer\Json::class, ], + 'default' => [ + 'type' => '*/*', + 'model' => \Magento\Framework\Webapi\Rest\Response\Renderer\Json::class + ], ]; $this->_factory = new \Magento\Framework\Webapi\Rest\Response\RendererFactory( @@ -42,29 +49,43 @@ class RendererFactoryTest extends \PHPUnit\Framework\TestCase /** * Test GET method. + * + * @param array $acceptTypes + * @param string $model + * @dataProvider getTestDataProvider */ - public function testGet() + public function testGet($acceptTypes, $model) { - $acceptTypes = ['application/json']; - /** Mock request getAcceptTypes method to return specified value. */ $this->_requestMock->expects($this->once())->method('getAcceptTypes')->will($this->returnValue($acceptTypes)); /** Mock renderer. */ - $rendererMock = $this->getMockBuilder( - \Magento\Framework\Webapi\Rest\Response\Renderer\Json::class - )->disableOriginalConstructor()->getMock(); + $rendererMock = $this->getMockBuilder($model)->disableOriginalConstructor()->getMock(); /** Mock object to return mocked renderer. */ $this->_objectManagerMock->expects( $this->once() )->method( 'get' )->with( - \Magento\Framework\Webapi\Rest\Response\Renderer\Json::class + $model )->will( $this->returnValue($rendererMock) ); $this->_factory->get(); } + + /** + * Data provider for method testGet + * + * @return array + */ + public function getTestDataProvider() + { + return [ + [['*/*'], \Magento\Framework\Webapi\Rest\Response\Renderer\Json::class], + [['application/json'], \Magento\Framework\Webapi\Rest\Response\Renderer\Json::class], + [['application/xml'], \Magento\Framework\Webapi\Rest\Response\Renderer\Xml::class], + ]; + } /** * Test GET method with wrong Accept HTTP Header.