5

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.

9
  • When you set a break point at $websiteid = $viewModel->getWebsite()->getWebsiteId(); do you have any websites in the collection $websites?
    – Daniel
    Jul 30, 2019 at 9:35
  • I'm sorry, but how do I do that? I'm not that well versed. Jul 30, 2019 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, 2019 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. Jul 30, 2019 at 13:46
  • 1
    Let us continue this discussion in chat. Jul 30, 2019 at 14:49

1 Answer 1

5

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.

10
  • 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, 2019 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 Jul 30, 2019 at 8:13
  • 1
    Excellent!, It works fine so far, thank you so very much! :-) Aug 3, 2019 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 Aug 3, 2019 at 10:30
  • 2
    We've refactored this into a seperate open-source module: github.com/iMi-digital/magento2-store-switch-all-store-views
    – kolaente
    Feb 17, 2020 at 14:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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