├── CHANGELOG.md ├── Helper ├── Data.php └── Settings.php ├── Plugin ├── Magento │ ├── Store │ │ └── App │ │ │ └── Request │ │ │ └── StorePathInfoValidator.php │ └── UrlRewrite │ │ └── Model │ │ └── StoreSwitcher │ │ └── RewriteUrl.php └── Store │ ├── App │ └── Request │ │ └── PathInfoProcessor.php │ └── Model │ └── Store.php ├── README.md ├── composer.json ├── etc ├── adminhtml │ └── system.xml ├── config.xml ├── di.xml └── module.xml └── registration.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.1.1 (2020-04-29) 2 | 3 | [View Release](git@github.com:experius/Magento2-Module-Multiple-Website-Store-Code-Url.git/commits/tag/2.1.1) 4 | 5 | * [BUGFIX] Solved issue with api incorrect redirect because it requires the original storecode *(Lewis Voncken)* 6 | 7 | 8 | ## 2.1.0 (2020-04-24) 9 | 10 | [View Release](git@github.com:experius/Magento2-Module-Multiple-Website-Store-Code-Url.git/commits/tag/2.1.0) 11 | 12 | * [BUGFIX] Made the Store Plugin Global to solve issue invalid urls in the sitemap.xml *(Lewis Voncken)* 13 | * [FEATURE] Force redirect from original store code to the stripped store code *(Lewis Voncken)* 14 | 15 | 16 | ## 2.0.5 (2020-03-16) 17 | 18 | [View Release](git@github.com:experius/Magento2-Module-Multiple-Website-Store-Code-Url.git/commits/tag/2.0.5) 19 | 20 | * [BUGFIX] Solved invalid issue when using www in domain *(Mr. Lewis)* 21 | 22 | 23 | ## 2.0.4 (2020-03-04) 24 | 25 | [View Release](git@github.com:experius/Magento2-Module-Multiple-Website-Store-Code-Url.git/commits/tag/2.0.4) 26 | 27 | * [BUGFIX] Modified website matching by stripping schema and www. *(Egor Dmitriev)* 28 | 29 | 30 | ## 2.0.3 (2020-03-02) 31 | 32 | [View Release](git@github.com:experius/Magento2-Module-Multiple-Website-Store-Code-Url.git/commits/tag/2.0.3) 33 | 34 | * [BUGFIX] Fixed direct links to an aliased storeview. *(Egor Dmitriev)* 35 | 36 | 37 | ## 2.0.2 (2020-02-24) 38 | 39 | [View Release](git@github.com:experius/Magento2-Module-Multiple-Website-Store-Code-Url.git/commits/tag/2.0.2) 40 | 41 | * [BUGFIX] Fixed fallback such that urls with code in form of website_code still work. *(Egor Dmitriev)* 42 | 43 | 44 | ## 2.0.1 (2019-12-18) 45 | 46 | [View Release](git@github.com:experius/Magento2-Module-Multiple-Website-Store-Code-Url.git/commits/tag/2.0.1) 47 | 48 | * [BUGFIX] fixed storeswitcher functionality. Fixed issue with navigating on a non default store. Before all pages would load the default store in de back. Now the correct store *(thomas mondeel)* 49 | * Update README.md *(thokiller)* 50 | * [BUGFIX] fixed switching from homepage *(thomas mondeel)* 51 | 52 | 53 | ## 2.0.0 (2019-12-11) 54 | 55 | [View Release](git@github.com:experius/Magento2-Module-Multiple-Website-Store-Code-Url.git/commits/tag/2.0.0) 56 | 57 | * [FEATURE/BUGFIX] made module compatible with Magento 2.3.* *(thokiller)* 58 | 59 | 60 | ## 1.0.3 (2018-05-11) 61 | 62 | [View Release](git@github.com:experius/Magento2-Module-Multiple-Website-Store-Code-Url.git/commits/tag/1.0.3) 63 | 64 | * [BUGFIX] Solved fatal php error Call to a member function getCode on a boolean *(Lewis Voncken)* 65 | 66 | 67 | ## 1.0.2 (2017-08-24) 68 | 69 | [View Release](git@github.com:experius/Magento2-Module-Multiple-Website-Store-Code-Url.git/commits/tag/1.0.2) 70 | 71 | * [BUGFIX] Solved error in composer .json because of wrong name *(Bart Lubbersen)* 72 | 73 | 74 | ## 1.0.1 (2017-08-24) 75 | 76 | [View Release](git@github.com:experius/Magento2-Module-Multiple-Website-Store-Code-Url.git/commits/tag/1.0.1) 77 | 78 | * [TASK] Add license to composer *(Bart Lubbersen)* 79 | 80 | 81 | ## 1.0.0 (2017-08-24) 82 | 83 | [View Release](git@github.com:experius/Magento2-Module-Multiple-Website-Store-Code-Url.git/commits/tag/1.0.0) 84 | 85 | * [TASK] Initial commit *(Bart Lubbersen)* 86 | * [TASK] Make configuration only available on default level because url param plugin wont know store yet [TASK] Make module independent on Core module [TASK] Add composer and readme file *(Bart Lubbersen)* 87 | 88 | 89 | -------------------------------------------------------------------------------- /Helper/Data.php: -------------------------------------------------------------------------------- 1 | . 20 | */ 21 | 22 | namespace Experius\MultipleWebsiteStoreCodeUrl\Helper; 23 | 24 | use Magento\Config\Model\Config\Backend\Admin\Custom; 25 | use Magento\Framework\App\Helper\AbstractHelper; 26 | use Magento\Store\Model\StoreManagerInterface; 27 | 28 | /** 29 | * Class Data 30 | * @package Experius\MultipleWebsiteStoreCodeUrl\Helper 31 | */ 32 | class Data extends AbstractHelper 33 | { 34 | /** 35 | * @var StoreManagerInterface 36 | */ 37 | protected $storeManager; 38 | 39 | /** @var \Magento\Framework\App\ResourceConnection */ 40 | protected $resource; 41 | 42 | protected $currentWebsite = null; 43 | 44 | /** 45 | * Data constructor. 46 | * @param StoreManagerInterface $storeManager 47 | * @param \Magento\Framework\App\ResourceConnection $resource 48 | */ 49 | public function __construct( 50 | StoreManagerInterface $storeManager, 51 | \Magento\Framework\App\ResourceConnection $resource 52 | ) { 53 | $this->storeManager = $storeManager; 54 | $this->resource = $resource; 55 | } 56 | 57 | /** 58 | * @param string $websiteCode 59 | * @param string $path 60 | * @param bool $isUrl 61 | * @return string 62 | */ 63 | public function setCorrectWebsiteCodeUrl($websiteCode, $path, $isUrl = false) 64 | { 65 | $element = ($isUrl) ? 3 : 0; 66 | $pathParts = explode('/', ltrim($path, '/'), 5); 67 | $storeCode = "{$websiteCode}_{$pathParts[$element]}"; 68 | 69 | if ($this->validateStore($storeCode)) { 70 | $pathParts[$element] = $storeCode; 71 | $path = implode('/', $pathParts); 72 | return $path; 73 | } 74 | //returns the original path if request havenot a valid store 75 | return $path; 76 | } 77 | 78 | /** 79 | * @param string $storeCode 80 | * @return bool 81 | */ 82 | public function validateStore($storeCode) 83 | { 84 | try { 85 | /** @var \Magento\Store\Api\Data\StoreInterface $store */ 86 | $this->storeManager->getStore($storeCode); 87 | return true; 88 | } catch (\Exception $e) { 89 | return false; 90 | } 91 | return false; 92 | } 93 | 94 | /** 95 | * @param \Magento\Framework\App\Request\Http $request 96 | * @return int|null 97 | */ 98 | public function getRequestToWebsiteId($request) 99 | { 100 | $baseUrl = str_replace('www.', '', $request->getDistroBaseUrl()); 101 | // Strip schemas 102 | $baseUrl = str_replace(['https://', 'http://'], '', $baseUrl); 103 | 104 | $connection = $this->resource->getConnection(); 105 | $table = $connection->getTableName('core_config_data'); 106 | $websiteFilter = $connection->select()->from($table, ['scope_id']) 107 | ->where('scope = ?', 'websites') 108 | ->where('path in (?)', [Custom::XML_PATH_SECURE_BASE_URL, Custom::XML_PATH_UNSECURE_BASE_URL]) 109 | ->where('value like ?', "%$baseUrl"); 110 | $match = $connection->fetchCol($websiteFilter); 111 | 112 | return count($match) > 0 ? (int)$match[0] : null; 113 | } 114 | 115 | /** 116 | * Warning: Caches result 117 | * @param \Magento\Framework\App\Request\Http $request 118 | * @return \Magento\Store\Api\Data\WebsiteInterface|null 119 | * @throws \Magento\Framework\Exception\LocalizedException 120 | */ 121 | public function getRequestToWebsite($request) 122 | { 123 | if (!$this->currentWebsite) { 124 | $websiteId = $this->getRequestToWebsiteId($request); 125 | $this->currentWebsite = $websiteId ? $this->storeManager->getWebsite($websiteId) : null; 126 | } 127 | return $this->currentWebsite; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /Helper/Settings.php: -------------------------------------------------------------------------------- 1 | . 20 | */ 21 | namespace Experius\MultipleWebsiteStoreCodeUrl\Helper; 22 | 23 | use Magento\Framework\App\Helper\AbstractHelper; 24 | 25 | class Settings extends AbstractHelper 26 | { 27 | 28 | const CONFIG_PATH_REMOVE_WEBSITE_CODE_FROM_STORE_URL = 'remove_website_code_from_store_url'; 29 | 30 | public $configPathModule = 'web/url'; 31 | 32 | public function shouldRemoveWebsiteCodeFromStoreUrl() 33 | { 34 | return $this->scopeConfig->isSetFlag( 35 | $this->configPathModule . "/" . self::CONFIG_PATH_REMOVE_WEBSITE_CODE_FROM_STORE_URL 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Plugin/Magento/Store/App/Request/StorePathInfoValidator.php: -------------------------------------------------------------------------------- 1 | storeManager = $storeManager; 67 | $this->settings = $settings; 68 | $this->helper = $helper; 69 | $this->storeCookieManager = $storeCookieManager; 70 | $this->pathInfo = $pathInfo; 71 | $this->responseFactory = $responseFactory; 72 | } 73 | 74 | /** 75 | * @param \Magento\Store\App\Request\StorePathInfoValidator $subject 76 | * @param $result 77 | * @param $request 78 | * @param string $pathInfo 79 | * @return string 80 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 81 | */ 82 | public function afterGetValidStoreCode( 83 | \Magento\Store\App\Request\StorePathInfoValidator $subject, 84 | $result, 85 | $request, 86 | $pathInfo = '' 87 | ) { 88 | if (!$this->settings->shouldRemoveWebsiteCodeFromStoreUrl()) { 89 | return $result; 90 | } 91 | if (empty($pathInfo)) { 92 | $pathInfo = $this->pathInfo->getPathInfo( 93 | $request->getRequestUri(), 94 | $request->getBaseUrl() 95 | ); 96 | } 97 | $pathParts = explode('/', ltrim($pathInfo, '/'), 2); 98 | if ($result) { 99 | if (strpos($pathParts[0], '_') === false) { 100 | return $result; 101 | } 102 | 103 | } 104 | $websiteCode = $this->storeCookieManager->getStoreCodeFromCookie(); 105 | 106 | if(!$websiteCode && $website = $this->helper->getRequestToWebsite($request)) { 107 | $websiteCode = $website->getCode(); 108 | } 109 | if (!$websiteCode) { 110 | return $result; 111 | } 112 | 113 | if ($result && strpos($request->getRequestUri(), "/{$websiteCode}_") === 0) { 114 | $requestUri = str_replace("/{$websiteCode}_", "", $request->getRequestUri()); 115 | $response = $this->responseFactory->create(); 116 | $response->setRedirect($request->getDistroBaseUrl() . $requestUri, 301); 117 | $response->sendResponse(); 118 | return ''; // This should never be reached because of the function above. 119 | } 120 | 121 | $storeCode = "{$websiteCode}_{$pathParts[0]}"; 122 | try { 123 | /** @var \Magento\Store\Api\Data\StoreInterface $store */ 124 | $this->storeManager->getStore($storeCode); 125 | } catch (\Exception $e) { 126 | return $result; 127 | } 128 | return $storeCode; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Plugin/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrl.php: -------------------------------------------------------------------------------- 1 | storeManager = $storeManager; 42 | $this->settings = $settings; 43 | $this->data = $data; 44 | } 45 | 46 | /** 47 | * @param \Magento\UrlRewrite\Model\StoreSwitcher\RewriteUrl $subject 48 | * @param $fromStore 49 | * @param $targetStore 50 | * @param $redirectUrl 51 | * @return array 52 | * @throws \Magento\Framework\Exception\LocalizedException 53 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 54 | */ 55 | public function beforeSwitch( 56 | \Magento\UrlRewrite\Model\StoreSwitcher\RewriteUrl $subject, 57 | $fromStore, 58 | $targetStore, 59 | $redirectUrl 60 | ) { 61 | $return = [$fromStore,$targetStore,$redirectUrl]; 62 | if (!$this->settings->shouldRemoveWebsiteCodeFromStoreUrl()) { 63 | return $return; 64 | } 65 | 66 | // to prevent a 404 when used on a home page 67 | $redirectUrlTemp = rtrim($redirectUrl,'/'); 68 | $temp = explode('/',$redirectUrlTemp,5); 69 | if (rtrim($temp[3],'/') == rtrim(end($temp),'/')) 70 | { 71 | if (strpos(rtrim($redirectUrl, '/'), rtrim($targetStore->getBaseUrl(),'/')) === false) { 72 | $redirectUrl = $targetStore->getBaseUrl(); 73 | return [$fromStore,$targetStore,$redirectUrl]; 74 | } 75 | return $return; 76 | } 77 | unset($temp); 78 | unset($redirectUrlTemp); 79 | 80 | $website = $this->storeManager->getWebsite(); 81 | if (!$website) { 82 | return $return; 83 | } 84 | $websiteCode = $website->getCode(); 85 | $redirectUrl = $this->data->setCorrectWebsiteCodeUrl($websiteCode,$redirectUrl,true); 86 | 87 | return [$fromStore,$targetStore,$redirectUrl]; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Plugin/Store/App/Request/PathInfoProcessor.php: -------------------------------------------------------------------------------- 1 | . 20 | */ 21 | namespace Experius\MultipleWebsiteStoreCodeUrl\Plugin\Store\App\Request; 22 | 23 | use Experius\MultipleWebsiteStoreCodeUrl\Helper\Data; 24 | use Experius\MultipleWebsiteStoreCodeUrl\Helper\Settings; 25 | use Magento\Framework\App\RequestInterface; 26 | use Magento\Framework\Exception\NoSuchEntityException; 27 | use Magento\Store\Model\StoreManagerInterface; 28 | 29 | class PathInfoProcessor 30 | { 31 | 32 | /** 33 | * @var \Magento\Store\Model\StoreManagerInterface 34 | */ 35 | private $storeManager; 36 | 37 | /** 38 | * @var \Experius\MultipleWebsiteStoreCodeUrl\Helper\Settings 39 | */ 40 | private $settings; 41 | 42 | /** 43 | * @var Data 44 | */ 45 | protected $data; 46 | 47 | /** 48 | * PathInfoProcessor constructor. 49 | * @param \Magento\Store\Model\StoreManagerInterface $storeManager 50 | * @param \Experius\MultipleWebsiteStoreCodeUrl\Helper\Settings $settings 51 | * @param \Experius\MultipleWebsiteStoreCodeUrl\Helper\Data $data 52 | */ 53 | public function __construct( 54 | StoreManagerInterface $storeManager, 55 | Settings $settings, 56 | Data $data 57 | ) { 58 | $this->storeManager = $storeManager; 59 | $this->settings = $settings; 60 | $this->data = $data; 61 | } 62 | 63 | /** 64 | * @param \Magento\Store\App\Request\PathInfoProcessor $subject 65 | * @param callable $proceed 66 | * @param RequestInterface $request 67 | * @param $pathInfo 68 | * @return mixed 69 | * @throws \Magento\Framework\Exception\LocalizedException 70 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 71 | */ 72 | public function aroundProcess( 73 | \Magento\Store\App\Request\PathInfoProcessor $subject, 74 | callable $proceed, 75 | RequestInterface $request, 76 | $pathInfo 77 | ) { 78 | if (!$this->settings->shouldRemoveWebsiteCodeFromStoreUrl()) { 79 | return $proceed($request, $pathInfo); 80 | } 81 | $website = $this->storeManager->getWebsite(); 82 | if (!$website) { 83 | return $proceed($request, $pathInfo); 84 | } 85 | $websiteCode = $website->getCode(); 86 | $newPath = $this->data->setCorrectWebsiteCodeUrl($websiteCode,$pathInfo,false); 87 | return $proceed($request, "/" . ltrim($newPath, '/')); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Plugin/Store/Model/Store.php: -------------------------------------------------------------------------------- 1 | . 20 | */ 21 | namespace Experius\MultipleWebsiteStoreCodeUrl\Plugin\Store\Model; 22 | 23 | use Experius\MultipleWebsiteStoreCodeUrl\Helper\Settings; 24 | use Magento\Framework\UrlInterface; 25 | 26 | class Store 27 | { 28 | 29 | /** 30 | * @var \Experius\MultipleWebsiteStoreCodeUrl\Helper\Settings 31 | */ 32 | private $settings; 33 | 34 | /** 35 | * PathInfoProcessor constructor. 36 | * @param \Experius\MultipleWebsiteStoreCodeUrl\Helper\Settings $settings 37 | */ 38 | public function __construct( 39 | Settings $settings 40 | ) { 41 | $this->settings = $settings; 42 | } 43 | 44 | public function aroundGetBaseUrl(\Magento\Store\Model\Store $subject, callable $proceed, $type = UrlInterface::URL_TYPE_LINK, $secure = null) 45 | { 46 | $url = $proceed($type, $secure); 47 | if (!$this->settings->shouldRemoveWebsiteCodeFromStoreUrl()) { 48 | return $url; 49 | } 50 | if ($type != UrlInterface::URL_TYPE_LINK) { 51 | return $url; 52 | } 53 | $storeCode = $subject->getCode(); 54 | $website = $subject->getWebsite(); 55 | if (!$website) { 56 | return $proceed($type, $secure); 57 | } 58 | $websiteCode = $website->getCode(); 59 | $storeUrlCode = str_replace("{$websiteCode}_", "", $storeCode); 60 | $url = str_replace($storeCode, $storeUrlCode, $url); 61 | return $url; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multiple website store code url 2 | This module enabled you to use the same store_code multiple times for different websites in the url by removing the website_code from the store_code before adding it to the url. So "example.com/website_en/", will become "example.com/en/". 3 | 4 | The module has configuration to enable/disable it in the web/url section. 5 | 6 | **Important: the store code should include the website code otherwise it won't do anything.** 7 | 8 | # Configuration 9 | In order for direct links to work, you must set base urls on website level. 10 | 11 | In "Stores -> Configuration -> General -> Web -> Base Urls", "web/unsecure/base_url" or "web/secure/base_url" or both must be filled. 12 | The values must be present at website level. 13 | 14 | # Changelog: 15 | - 2.0.0 added compatibility for magento 2.3.*. 16 | - 2.0.1 fixed issues with not being able to navigate on a not default store + fixed storeswitcher url redirect 17 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "experius/module-multiplewebsitestorecodeurl", 3 | "description": "This module enabled you to use the same store_code multiple times for different websites in the url.", 4 | "authors": [ 5 | { 6 | "name": "Experius", 7 | "email": "info@experius.nl" 8 | } 9 | ], 10 | "license": "GPL-3.0", 11 | "autoload": { 12 | "files": [ 13 | "registration.php" 14 | ], 15 | "psr-4": { 16 | "Experius\\MultipleWebsiteStoreCodeUrl\\": "" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /etc/adminhtml/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | Magento\Config\Model\Config\Source\Yesno 9 | 10 | 1 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
-------------------------------------------------------------------------------- /etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 |