Question

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.

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top