diff --git a/app/code/Magento/Store/Model/ResourceModel/StoreWebsiteRelation.php b/app/code/Magento/Store/Model/ResourceModel/StoreWebsiteRelation.php index 7fcd9ac28c3ec..19bb45de24d64 100644 --- a/app/code/Magento/Store/Model/ResourceModel/StoreWebsiteRelation.php +++ b/app/code/Magento/Store/Model/ResourceModel/StoreWebsiteRelation.php @@ -27,6 +27,8 @@ public function __construct(ResourceConnection $resource) } /** + * Get store by website id + * * @param int $websiteId * @return array */ @@ -41,4 +43,29 @@ public function getStoreByWebsiteId($websiteId) $data = $connection->fetchCol($storeSelect); return $data; } + + /** + * Get website store data + * + * @param int $websiteId + * @param bool $available + * @return array + */ + public function getWebsiteStores(int $websiteId, bool $available = false): array + { + $connection = $this->resource->getConnection(); + $storeTable = $this->resource->getTableName('store'); + $storeSelect = $connection->select()->from($storeTable)->where( + 'website_id = ?', + $websiteId + ); + + if ($available) { + $storeSelect = $storeSelect->where( + 'is_active = 1' + ); + } + + return $connection->fetchAll($storeSelect); + } } diff --git a/app/code/Magento/Store/Model/Service/StoreConfigManager.php b/app/code/Magento/Store/Model/Service/StoreConfigManager.php index debb08438a3b4..ebc73036f7e37 100644 --- a/app/code/Magento/Store/Model/Service/StoreConfigManager.php +++ b/app/code/Magento/Store/Model/Service/StoreConfigManager.php @@ -3,27 +3,33 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Store\Model\Service; -/** - * Allows to get store config - */ +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Api\Data\StoreConfigInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\Data\StoreConfig; +use Magento\Store\Model\Data\StoreConfigFactory; +use Magento\Store\Model\ResourceModel\Store\CollectionFactory; +use Magento\Store\Model\Store; + class StoreConfigManager implements \Magento\Store\Api\StoreConfigManagerInterface { /** - * @var \Magento\Store\Model\ResourceModel\Store\CollectionFactory + * @var CollectionFactory */ protected $storeCollectionFactory; /** - * @var \Magento\Store\Model\Data\StoreConfigFactory + * @var StoreConfigFactory */ protected $storeConfigFactory; /** * Core store config * - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $scopeConfig; @@ -41,14 +47,14 @@ class StoreConfigManager implements \Magento\Store\Api\StoreConfigManagerInterfa ]; /** - * @param \Magento\Store\Model\ResourceModel\Store\CollectionFactory $storeCollectionFactory - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Store\Model\Data\StoreConfigFactory $storeConfigFactory + * @param CollectionFactory $storeCollectionFactory + * @param ScopeConfigInterface $scopeConfig + * @param StoreConfigFactory $storeConfigFactory */ public function __construct( - \Magento\Store\Model\ResourceModel\Store\CollectionFactory $storeCollectionFactory, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Store\Model\Data\StoreConfigFactory $storeConfigFactory + CollectionFactory $storeCollectionFactory, + ScopeConfigInterface $scopeConfig, + StoreConfigFactory $storeConfigFactory ) { $this->storeCollectionFactory = $storeCollectionFactory; $this->scopeConfig = $scopeConfig; @@ -56,10 +62,10 @@ public function __construct( } /** - * Get store configs + * Get store configurations * * @param string[] $storeCodes list of stores by store codes, will return all if storeCodes is not set - * @return \Magento\Store\Api\Data\StoreConfigInterface[] + * @return StoreConfigInterface[] */ public function getStoreConfigs(array $storeCodes = null) { @@ -76,14 +82,14 @@ public function getStoreConfigs(array $storeCodes = null) } /** - * Get store config + * Get store specific configs * - * @param \Magento\Store\Model\Store $store - * @return \Magento\Store\Api\Data\StoreConfigInterface + * @param Store|StoreInterface $store + * @return StoreConfigInterface */ protected function getStoreConfig($store) { - /** @var \Magento\Store\Model\Data\StoreConfig $storeConfig */ + /** @var StoreConfig $storeConfig */ $storeConfig = $this->storeConfigFactory->create(); $storeConfig->setId($store->getId()) diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/AvailableStoresResolver.php b/app/code/Magento/StoreGraphQl/Model/Resolver/AvailableStoresResolver.php new file mode 100644 index 0000000000000..eedd7e21fa058 --- /dev/null +++ b/app/code/Magento/StoreGraphQl/Model/Resolver/AvailableStoresResolver.php @@ -0,0 +1,48 @@ +storeConfigDataProvider = $storeConfigsDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + return $this->storeConfigDataProvider->getAvailableStoreConfig( + (int)$context->getExtensionAttributes()->getStore()->getWebsiteId() + ); + } +} diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php index 0baee00f468a0..6538d87de9780 100644 --- a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php +++ b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php @@ -8,7 +8,9 @@ namespace Magento\StoreGraphQl\Model\Resolver\Store; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Api\Data\StoreConfigInterface; use Magento\Store\Api\StoreConfigManagerInterface; +use Magento\Store\Model\ResourceModel\StoreWebsiteRelation; use Magento\Store\Model\ScopeInterface; use Magento\Store\Api\Data\StoreInterface; @@ -32,19 +34,27 @@ class StoreConfigDataProvider */ private $extendedConfigData; + /** + * @var StoreWebsiteRelation + */ + private $storeWebsiteRelation; + /** * @param StoreConfigManagerInterface $storeConfigManager * @param ScopeConfigInterface $scopeConfig + * @param StoreWebsiteRelation $storeWebsiteRelation * @param array $extendedConfigData */ public function __construct( StoreConfigManagerInterface $storeConfigManager, ScopeConfigInterface $scopeConfig, + StoreWebsiteRelation $storeWebsiteRelation, array $extendedConfigData = [] ) { $this->storeConfigManager = $storeConfigManager; $this->scopeConfig = $scopeConfig; $this->extendedConfigData = $extendedConfigData; + $this->storeWebsiteRelation = $storeWebsiteRelation; } /** @@ -55,23 +65,42 @@ public function __construct( */ public function getStoreConfigData(StoreInterface $store): array { - return array_merge( - $this->getBaseConfigData($store), - $this->getExtendedConfigData((int)$store->getId()) - ); + $defaultStoreConfig = $this->storeConfigManager->getStoreConfigs([$store->getCode()]); + return $this->prepareStoreConfigData(current($defaultStoreConfig), $store->getName()); } /** - * Get base config data + * Get available website stores * - * @param StoreInterface $store + * @param int $websiteId * @return array */ - private function getBaseConfigData(StoreInterface $store) : array + public function getAvailableStoreConfig(int $websiteId): array { - $storeConfig = current($this->storeConfigManager->getStoreConfigs([$store->getCode()])); + $websiteStores = $this->storeWebsiteRelation->getWebsiteStores($websiteId, true); + $storeCodes = array_column($websiteStores, 'code'); + + $storeConfigs = $this->storeConfigManager->getStoreConfigs($storeCodes); + $storesConfigData = []; + + foreach ($storeConfigs as $storeConfig) { + $key = array_search($storeConfig->getCode(), array_column($websiteStores, 'code'), true); + $storesConfigData[] = $this->prepareStoreConfigData($storeConfig, $websiteStores[$key]['name']); + } - return [ + return $storesConfigData; + } + + /** + * Prepare store config data + * + * @param StoreConfigInterface $storeConfig + * @param string $storeName + * @return array + */ + private function prepareStoreConfigData(StoreConfigInterface $storeConfig, string $storeName): array + { + return array_merge([ 'id' => $storeConfig->getId(), 'code' => $storeConfig->getCode(), 'website_id' => $storeConfig->getWebsiteId(), @@ -82,14 +111,14 @@ private function getBaseConfigData(StoreInterface $store) : array 'weight_unit' => $storeConfig->getWeightUnit(), 'base_url' => $storeConfig->getBaseUrl(), 'base_link_url' => $storeConfig->getBaseLinkUrl(), - 'base_static_url' => $storeConfig->getSecureBaseStaticUrl(), + 'base_static_url' => $storeConfig->getBaseStaticUrl(), 'base_media_url' => $storeConfig->getBaseMediaUrl(), 'secure_base_url' => $storeConfig->getSecureBaseUrl(), 'secure_base_link_url' => $storeConfig->getSecureBaseLinkUrl(), 'secure_base_static_url' => $storeConfig->getSecureBaseStaticUrl(), 'secure_base_media_url' => $storeConfig->getSecureBaseMediaUrl(), - 'store_name' => $store->getName() - ]; + 'store_name' => $storeName, + ], $this->getExtendedConfigData((int)$storeConfig->getId())); } /** @@ -98,7 +127,7 @@ private function getBaseConfigData(StoreInterface $store) : array * @param int $storeId * @return array */ - private function getExtendedConfigData(int $storeId) + private function getExtendedConfigData(int $storeId): array { $extendedConfigData = []; foreach ($this->extendedConfigData as $key => $path) { diff --git a/app/code/Magento/StoreGraphQl/etc/schema.graphqls b/app/code/Magento/StoreGraphQl/etc/schema.graphqls index 919c94684eb21..d85bac7801f39 100644 --- a/app/code/Magento/StoreGraphQl/etc/schema.graphqls +++ b/app/code/Magento/StoreGraphQl/etc/schema.graphqls @@ -2,6 +2,7 @@ # See COPYING.txt for license details. type Query { storeConfig : StoreConfig @resolver(class: "Magento\\StoreGraphQl\\Model\\Resolver\\StoreConfigResolver") @doc(description: "The store config query") @cache(cacheable: false) + availableStores: [StoreConfig] @resolver(class: "Magento\\StoreGraphQl\\Model\\Resolver\\AvailableStoresResolver") @doc(description: "Get a list of available store views and their config information.") } type Website @doc(description: "Website is deprecated because it is should not be used on storefront. The type contains information about a website") { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/AvailableStoreConfigTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/AvailableStoreConfigTest.php new file mode 100644 index 0000000000000..eaf76e4559557 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/AvailableStoreConfigTest.php @@ -0,0 +1,171 @@ +objectManager = Bootstrap::getObjectManager(); + $this->storeConfigManager = $this->objectManager->get(StoreConfigManagerInterface::class); + $this->storeResource = $this->objectManager->get(StoreResource::class); + } + + /** + * @magentoApiDataFixture Magento/Store/_files/store.php + * @magentoApiDataFixture Magento/Store/_files/inactive_store.php + */ + public function testDefaultWebsiteAvailableStoreConfigs(): void + { + $storeConfigs = $this->storeConfigManager->getStoreConfigs(); + + $expectedAvailableStores = []; + $expectedAvailableStoreCodes = [ + 'default', + 'test' + ]; + + foreach ($storeConfigs as $storeConfig) { + if (in_array($storeConfig->getCode(), $expectedAvailableStoreCodes)) { + $expectedAvailableStores[] = $storeConfig; + } + } + + $query + = <<graphQlQuery($query); + + $this->assertArrayHasKey('availableStores', $response); + foreach ($expectedAvailableStores as $key => $storeConfig) { + $this->validateStoreConfig($storeConfig, $response['availableStores'][$key]); + } + } + + /** + * @magentoApiDataFixture Magento/Store/_files/second_website_with_two_stores.php + */ + public function testNonDefaultWebsiteAvailableStoreConfigs(): void + { + $storeConfigs = $this->storeConfigManager->getStoreConfigs(['fixture_second_store', 'fixture_third_store']); + + $query + = << 'fixture_second_store']; + $response = $this->graphQlQuery($query, [], '', $headerMap); + + $this->assertArrayHasKey('availableStores', $response); + foreach ($storeConfigs as $key => $storeConfig) { + $this->validateStoreConfig($storeConfig, $response['availableStores'][$key]); + } + } + + /** + * Validate Store Config Data + * + * @param StoreConfigInterface $storeConfig + * @param array $responseConfig + */ + private function validateStoreConfig(StoreConfigInterface $storeConfig, array $responseConfig): void + { + $store = $this->objectManager->get(Store::class); + $this->storeResource->load($store, $storeConfig->getCode(), 'code'); + $this->assertEquals($storeConfig->getId(), $responseConfig['id']); + $this->assertEquals($storeConfig->getCode(), $responseConfig['code']); + $this->assertEquals($storeConfig->getLocale(), $responseConfig['locale']); + $this->assertEquals($storeConfig->getBaseCurrencyCode(), $responseConfig['base_currency_code']); + $this->assertEquals( + $storeConfig->getDefaultDisplayCurrencyCode(), + $responseConfig['default_display_currency_code'] + ); + $this->assertEquals($storeConfig->getTimezone(), $responseConfig['timezone']); + $this->assertEquals($storeConfig->getWeightUnit(), $responseConfig['weight_unit']); + $this->assertEquals($storeConfig->getBaseUrl(), $responseConfig['base_url']); + $this->assertEquals($storeConfig->getBaseLinkUrl(), $responseConfig['base_link_url']); + $this->assertEquals($storeConfig->getBaseStaticUrl(), $responseConfig['base_static_url']); + $this->assertEquals($storeConfig->getBaseMediaUrl(), $responseConfig['base_media_url']); + $this->assertEquals($storeConfig->getSecureBaseUrl(), $responseConfig['secure_base_url']); + $this->assertEquals($storeConfig->getSecureBaseLinkUrl(), $responseConfig['secure_base_link_url']); + $this->assertEquals($storeConfig->getSecureBaseStaticUrl(), $responseConfig['secure_base_static_url']); + $this->assertEquals($storeConfig->getSecureBaseMediaUrl(), $responseConfig['secure_base_media_url']); + $this->assertEquals($store->getName(), $responseConfig['store_name']); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php index e7af77f23fa88..cc8a60cf0937a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php @@ -7,10 +7,12 @@ namespace Magento\GraphQl\Store; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Store\Api\Data\StoreConfigInterface; use Magento\Store\Api\StoreConfigManagerInterface; use Magento\Store\Api\StoreRepositoryInterface; use Magento\Store\Api\StoreResolverInterface; +use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -20,34 +22,37 @@ class StoreConfigResolverTest extends GraphQlAbstract { - /** @var ObjectManager */ + /** @var ObjectManager */ private $objectManager; + /** + * @inheritDoc + */ protected function setUp(): void { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->objectManager = Bootstrap::getObjectManager(); } /** * @magentoApiDataFixture Magento/Store/_files/store.php - * @magentoConfigFixture default_store store/information/name Test Store + * @throws NoSuchEntityException */ - public function testGetStoreConfig() + public function testGetStoreConfig(): void { - /** @var StoreConfigManagerInterface $storeConfigsManager */ - $storeConfigsManager = $this->objectManager->get(StoreConfigManagerInterface::class); + /** @var StoreConfigManagerInterface $storeConfigManager */ + $storeConfigManager = $this->objectManager->get(StoreConfigManagerInterface::class); /** @var StoreResolverInterface $storeResolver */ $storeResolver = $this->objectManager->get(StoreResolverInterface::class); /** @var StoreRepositoryInterface $storeRepository */ $storeRepository = $this->objectManager->get(StoreRepositoryInterface::class); $storeId = $storeResolver->getCurrentStoreId(); $store = $storeRepository->getById($storeId); - /** @var StoreConfigInterface $storeConfig */ - $storeConfig = current($storeConfigsManager->getStoreConfigs([$store->getCode()])); + /** @var StoreConfigInterface $defaultStoreConfig */ + $defaultStoreConfig = current($storeConfigManager->getStoreConfigs([$store->getCode()])); $query = <<graphQlQuery($query); $this->assertArrayHasKey('storeConfig', $response); - $this->assertEquals($storeConfig->getId(), $response['storeConfig']['id']); - $this->assertEquals($storeConfig->getCode(), $response['storeConfig']['code']); - $this->assertEquals($storeConfig->getLocale(), $response['storeConfig']['locale']); - $this->assertEquals($storeConfig->getBaseCurrencyCode(), $response['storeConfig']['base_currency_code']); + $this->validateStoreConfig($defaultStoreConfig, $response['storeConfig'], $store->getName()); + } + + /** + * Validate Store Config Data + * + * @param StoreConfigInterface $storeConfig + * @param array $responseConfig + * @param string $storeName + */ + private function validateStoreConfig( + StoreConfigInterface $storeConfig, + array $responseConfig, + string $storeName + ): void { + $this->assertEquals($storeConfig->getId(), $responseConfig['id']); + $this->assertEquals($storeConfig->getCode(), $responseConfig['code']); + $this->assertEquals($storeConfig->getLocale(), $responseConfig['locale']); + $this->assertEquals($storeConfig->getBaseCurrencyCode(), $responseConfig['base_currency_code']); $this->assertEquals( $storeConfig->getDefaultDisplayCurrencyCode(), - $response['storeConfig']['default_display_currency_code'] - ); - $this->assertEquals($storeConfig->getTimezone(), $response['storeConfig']['timezone']); - $this->assertEquals($storeConfig->getWeightUnit(), $response['storeConfig']['weight_unit']); - $this->assertEquals($storeConfig->getBaseUrl(), $response['storeConfig']['base_url']); - $this->assertEquals($storeConfig->getBaseLinkUrl(), $response['storeConfig']['base_link_url']); - $this->assertEquals($storeConfig->getBaseStaticUrl(), $response['storeConfig']['base_static_url']); - $this->assertEquals($storeConfig->getBaseMediaUrl(), $response['storeConfig']['base_media_url']); - $this->assertEquals($storeConfig->getSecureBaseUrl(), $response['storeConfig']['secure_base_url']); - $this->assertEquals($storeConfig->getSecureBaseLinkUrl(), $response['storeConfig']['secure_base_link_url']); - $this->assertEquals( - $storeConfig->getSecureBaseStaticUrl(), - $response['storeConfig']['secure_base_static_url'] + $responseConfig['default_display_currency_code'] ); - $this->assertEquals($storeConfig->getSecureBaseMediaUrl(), $response['storeConfig']['secure_base_media_url']); - $this->assertEquals($store->getName(), $response['storeConfig']['store_name']); + $this->assertEquals($storeConfig->getTimezone(), $responseConfig['timezone']); + $this->assertEquals($storeConfig->getWeightUnit(), $responseConfig['weight_unit']); + $this->assertEquals($storeConfig->getBaseUrl(), $responseConfig['base_url']); + $this->assertEquals($storeConfig->getBaseLinkUrl(), $responseConfig['base_link_url']); + $this->assertEquals($storeConfig->getBaseStaticUrl(), $responseConfig['base_static_url']); + $this->assertEquals($storeConfig->getBaseMediaUrl(), $responseConfig['base_media_url']); + $this->assertEquals($storeConfig->getSecureBaseUrl(), $responseConfig['secure_base_url']); + $this->assertEquals($storeConfig->getSecureBaseLinkUrl(), $responseConfig['secure_base_link_url']); + $this->assertEquals($storeConfig->getSecureBaseStaticUrl(), $responseConfig['secure_base_static_url']); + $this->assertEquals($storeConfig->getSecureBaseMediaUrl(), $responseConfig['secure_base_media_url']); + $this->assertEquals($storeName, $responseConfig['store_name']); } }