4

I run a multi website setup where there is only one store and store view per website.

In this scenario the store switcher won't work, so I'd like to implement a website switcher instead.

I'd like to use this [article][1] to implement it,


Based on Daniels solution below, I did the following.

Created a module

/app/code/{vendor}/{module}/ViewModel/StoreSwitchModel.php

With the following code

    <?php

namespace [Vendor]\{Module}\ViewModel;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Locale\TranslatedLists;
use Magento\Framework\Url\EncoderInterface;
use Magento\Framework\UrlInterface;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Model\ResourceModel\Website\Collection as WebsiteCollection;
use Magento\Store\Model\ResourceModel\Website\CollectionFactory as WebsiteCollectionFactory;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Store\ViewModel\SwitcherUrlProvider;

class StoreSwitchModel extends SwitcherUrlProvider
{
    const LOCALE_CONFIG_PATH = 'general/locale/code';

    const DEFAULT_COUNTRY_CONFIG_PATH = 'general/country/default';

    private $websiteCollectionFactory;

    private $scopeConfig;

    private $translatedLists;

    private $storeManager;

    public function __construct(
        EncoderInterface $encoder,
        StoreManagerInterface $storeManager,
        UrlInterface $urlBuilder,
        WebsiteCollectionFactory $websiteCollectionFactory,
        ScopeConfigInterface $scopeConfig,
        TranslatedLists $translatedLists
    ) {
        parent::__construct($encoder, $storeManager, $urlBuilder);
        $this->websiteCollectionFactory = $websiteCollectionFactory;
        $this->scopeConfig = $scopeConfig;
        $this->translatedLists = $translatedLists;
        $this->storeManager = $storeManager;
    }

    public function getWebsite()
    {
        return $this->storeManager->getWebsite();
    }

    public function getWebsites(): WebsiteCollection
    {
        return $this->websiteCollectionFactory->create();
    }

    public function getStoreLocale(StoreInterface $store): string
    {
        $locale = $this->scopeConfig->getValue(self::LOCALE_CONFIG_PATH, ScopeInterface::SCOPE_STORE, $store->getId());

        return $locale;
    }

    public function getStoreCountryCode(StoreInterface $store): string
    {
        return $this->scopeConfig->getValue(
            self::DEFAULT_COUNTRY_CONFIG_PATH,
            ScopeInterface::SCOPE_STORE,
            $store->getId()
        );
    }
}

Created the following file at

/app/code/{vendor}/{module}/etc/frontend/di.xml

    <?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Store\ViewModel\SwitcherUrlProvider" type="[Vendor]\{Module}\ViewModel\StoreSwitchModel" />
</config>

And updated the child_theme languages.phtml at

{vendor}/{child_theme}/Magento_Store/templates/switch/languages.phtml

    <?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

// @codingStandardsIgnoreFile

/** @var \Magento\Store\Block\Switcher $block */
?>
<?php
/** @var \Magento\Store\Block\Switcher $block */
$viewModel = $block->getData('view_model');
$websites = $viewModel->getWebsites();
$websiteid = $viewModel->getWebsite()->getWebsiteId();
?>
<div class="switcher store switcher-store" id="switcher-store">
    <strong class="label switcher-label"><span><?php echo __('Select Store') ?></span></strong>
    <div class="actions dropdown options switcher-options">
        <?php foreach ($websites as $website): ?>
            <?php if ($websiteid == $website->getId()): ?>
                <div class="action toggle switcher-trigger"
                     role="button"
                     tabindex="0"
                     data-mage-init='{"dropdown":{}}'
                     data-toggle="dropdown"
                     data-trigger-keypress-button="true"
                     id="switcher-store-trigger">
                    <strong>
                        <span><?php echo $block->escapeHtml($website->getName()) ?></span>
                    </strong>
                </div>
            <?php endif; ?>
        <?php endforeach; ?>
        <ul class="dropdown switcher-dropdown" data-target="dropdown">
            <?php foreach ($websites as $website): ?>
                <?php if (!($websiteid == $website->getId())): ?>
                    <li class="switcher-option view">
                        <a href='<?php echo $website->getDefaultStore()->getBaseUrl() ?>'>
                            <?php echo $block->escapeHtml($website->getName()) ?>
                        </a>
                    </li>
                <?php endif; ?>
            <?php endforeach; ?>
        </ul>
    </div>
</div>

This shows the website switcher instead of a store switcher.

  • When you set a break point at $websiteid = $viewModel->getWebsite()->getWebsiteId(); do you have any websites in the collection $websites? – Daniel Jul 30 '19 at 9:35
  • I'm sorry, but how do I do that? I'm not that well versed. – user2240778 Jul 30 '19 at 9:40
  • You can find some tutorials for that. You could try to echo var_dump($websites) to see it's contents. – Daniel Jul 30 '19 at 11:32
  • I added echo, <?php $viewModel = $block->getData('view_model'); $websites = $viewModel->getWebsites(); $websiteid = $viewModel->getWebsite()->getWebsiteId(); echo var_dump($websites); ?> Its the same. – user2240778 Jul 30 '19 at 13:46
  • 1
    Let us continue this discussion in chat. – user2240778 Jul 30 '19 at 14:49
4

However it says that it uses "objectManager which is not recommended" so is there a better way of implementing it?

Yes there is you can use a view model for this. The view model will come in handy for your second question too.
You can declare your view model like this:

<?php

namespace [Vendor]\[Module]\ViewModel;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Locale\TranslatedLists;
use Magento\Framework\Url\EncoderInterface;
use Magento\Framework\UrlInterface;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Model\ResourceModel\Website\Collection as WebsiteCollection;
use Magento\Store\Model\ResourceModel\Website\CollectionFactory as WebsiteCollectionFactory;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Store\ViewModel\SwitcherUrlProvider;

class StoreSwitchModel extends SwitcherUrlProvider
{
    const LOCALE_CONFIG_PATH = 'general/locale/code';

    const DEFAULT_COUNTRY_CONFIG_PATH = 'general/country/default';

    private $websiteCollectionFactory;

    private $scopeConfig;

    private $translatedLists;

    private $storeManager;

    public function __construct(
        EncoderInterface $encoder,
        StoreManagerInterface $storeManager,
        UrlInterface $urlBuilder,
        WebsiteCollectionFactory $websiteCollectionFactory,
        ScopeConfigInterface $scopeConfig,
        TranslatedLists $translatedLists
    ) {
        parent::__construct($encoder, $storeManager, $urlBuilder);
        $this->websiteCollectionFactory = $websiteCollectionFactory;
        $this->scopeConfig = $scopeConfig;
        $this->translatedLists = $translatedLists;
        $this->storeManager = $storeManager;
    }

    public function getWebsite()
    {
        return $this->storeManager->getWebsite();
    }

    public function getWebsites(): WebsiteCollection
    {
        return $this->websiteCollectionFactory->create();
    }

    public function getStoreLocale(StoreInterface $store): string
    {
        $locale = $this->scopeConfig->getValue(self::LOCALE_CONFIG_PATH, ScopeInterface::SCOPE_STORE, $store->getId());

        return $locale;
    }

    public function getStoreCountryCode(StoreInterface $store): string
    {
        return $this->scopeConfig->getValue(
            self::DEFAULT_COUNTRY_CONFIG_PATH,
            ScopeInterface::SCOPE_STORE,
            $store->getId()
        );
    }
}

In your template languages.phtml you can use the folloing code:

<?php
/** @var \Magento\Store\Block\Switcher $block */
$viewModel = $block->getData('view_model');
$websites = $viewModel->getWebsites();
$websiteid = $viewModel->getWebsite()->getWebsiteId();
?>
<div class="switcher store switcher-store" id="switcher-store">
    <strong class="label switcher-label"><span><?php echo __('Select Store') ?></span></strong>
    <div class="actions dropdown options switcher-options">
        <?php foreach ($websites as $website): ?>
            <?php if ($websiteid == $website->getId()): ?>
                <div class="action toggle switcher-trigger"
                     role="button"
                     tabindex="0"
                     data-mage-init='{"dropdown":{}}'
                     data-toggle="dropdown"
                     data-trigger-keypress-button="true"
                     id="switcher-store-trigger">
                    <strong>
                        <span><?php echo $block->escapeHtml($website->getName()) ?></span>
                    </strong>
                </div>
            <?php endif; ?>
        <?php endforeach; ?>
        <ul class="dropdown switcher-dropdown" data-target="dropdown">
            <?php foreach ($websites as $website): ?>
                <?php if (!($websiteid == $website->getId())): ?>
                    <li class="switcher-option view">
                        <a href='<?php echo $viewModel->getTargetStoreRedirectUrl($website->getDefaultStore()) ?>'>
                            <?php echo $block->escapeHtml($website->getName()) ?>
                        </a>
                    </li>
                <?php endif; ?>
            <?php endforeach; ?>
        </ul>
    </div>
</div>

In your module you'll have to set a preference for \Magento\Store\ViewModel\SwitcherUrlProvider:

app/code/[Vendor]/[Module]/etc/frontend/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Store\ViewModel\SwitcherUrlProvider" type="[Vendor]\[Module]\ViewModel\StoreSwitchModel" />
</config>

Also is there a way of adding a country flag (image) against each website listed in the switcher (multi websites are country based, Eg : UK, us, EU etc)

We did not implement this but we are displaying the language and the country in the switcher. By having the according images you could use either the store locale or the store country code to determine the country or language you want to display and adjust the template as needed.
In ViewModel from above you can use the functions getStoreLocale()and getStoreCountryCode() for that.

Update:
The Problem why it didn't work before was that Magento already passes a view model. You can extend the StoreSwitchModel by Magentos and set a preference to use yours. With this approach you don't need to pass the view_model in default.xml.

I tested this code on a fresh Magento 2.3.2 install.

Update 2:

However is there a way of it going to the same link in a different website, for example, when its in xyz.com/us/test.html and I switch website say to UK it goes to xyz.com/uk instead of xyz.com/uk/test.html

Yes it is. I didn't take that into account. This is what \Magento\Store\ViewModel\SwitcherUrlProvider is there for. I updated the code on languages.phtml and on the <a>is now $viewModel->getTargetStoreRedirectUrl($website->getDefaultStore()) used to retrieve the correct redirect url based on the current page.

| improve this answer | |
  • Create a new module and there you can put it. We solved it by creating a module that provides the Viewmodel but passing it to the block inside the theme – Daniel Jul 30 '19 at 7:53
  • So I created a module /app/code/{vendor}/{module}/ViewModel/StoreSwitchModel.php into which I put the ViewModel code. And added <!-- Website switcher --> before the language switcher code in {vendor}/{child_theme}/Magento_Store/templates/switch/languages.phtml, but now the when the page loads its missing the <head>, css, js etc – user2240778 Jul 30 '19 at 8:13
  • 1
    Excellent!, It works fine so far, thank you so very much! :-) – user2240778 Aug 3 '19 at 10:04
  • 1
    However is there a way of it going to the same link in a different website, for example, when its in xyz.com/us/test.html and I switch website say to UK it goes to xyz.com/uk instead of xyz.com/uk/test.html – user2240778 Aug 3 '19 at 10:30
  • 1
    We've refactored this into a seperate open-source module: github.com/iMi-digital/magento2-store-switch-all-store-views – kolaente Feb 17 at 14:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.