vue-storefront-1 icon indicating copy to clipboard operation
vue-storefront-1 copied to clipboard

Optional defaultStoreCode in url for multistore

Open rain2o opened this issue 6 years ago • 5 comments

What is the motivation for adding / enhancing this feature?

It would be very useful if there could be a configuration option to have VSF keep the store code/url key of the default store in the URL. At the moment the defaultStoreCode is not included in the url and it just loads the store without it. However, in some multistore situations it makes more sense to have the storecode/url key always in the url, even for the default store.

See https://forum.vuestorefront.io/t/handling-defaultstorecode-for-multi-store/564 for discussion.

What are the acceptance criteria

Browsing to mydomain.tld will redirect users to mydomain.tld/{default-store} if this ability is enabled.

Can you complete this feature request by yourself?

  • [ ] YES
  • [x] NO

At the moment I'm not able to commit to this, but could get to it later on if it hasn't been completed.

Which Release Cycle state this refers to? Info for developer.

Pick one option.

  • [x] This is a normal feature request. This should be available on https://test.storefrontcloud.io and then after tests this can be added to next Vue Storefront version. In this case Developer should create branch from develop branch and create Pull Request 2. Feature / Improvement back to develop.
  • [ ] (Pick this option only if you're sure) This is an important improvement request for current Release Candidate version on https://next.storefrontcloud.io and should be placed in next RC version. In this case Developer should create branch from release branch and create Pull Request 3. Stabilisation fix back to release.
  • [ ] (Pick this option only if you're sure) This is a critical improvement request for current Stable version on https://demo.storefrontcloud.io and should be placed in next stable version. In this case Developer should create branch from hotfix or master branch and create Pull Request 4. Hotfix back to hotfix.

Additional information

rain2o avatar Aug 21 '19 13:08 rain2o

I solved it by adding a second store, like which inherits from my desired default one.

For example: I'm having a german store config which I wan't to be the default store. So in best case the default store should also be available in http://localhost/specific/url and http://localhost/de/specific/url – so both is the /de store.

My solution would be to use the extend flag to extend from the default store ore visa versa to achieve this like:

{
  "defaultStoreCode": "default",
  "storeViews": {
    "mapStoreUrlsFor": ["de", "fr", "uk", "it"],
    "default": {
      "storeCode": "default",
      "storeId": 1,
      "name": "German Store",
      "url": "/",
      "appendStoreCode": false,
      "disabled": false,
      "elasticsearch": {
        "host": "/api/catalog",
        "index": "vue_storefront_catalog_1"
      },
      "tax": {
        "sourcePriceIncludesTax": false,
        "defaultCountry": "DE",
        "defaultRegion": "",
        "calculateServerSide": true
      }
    },
    "de": {
      "extend": "default",
      "storeCode": "de",
      "storeId": 1,
      "appendStoreCode": true,
      "disabled": false,
      "elasticsearch": {
        "host": "/api/catalog",
        "index": "vue_storefront_catalog_1"
      }
    },
    "fr": {
      "extend": "de",
      "storeCode": "fr",
      "storeId": 12,
      "name": "French Store",
      "url": "/fr",
      "appendStoreCode": true,
      "disabled": false,
      "elasticsearch": {
        "index": "vue_storefront_catalog_12"
      },
      "tax": {
        "defaultCountry": "FR"
      },
      "i18n": {
        "fullCountryName": "France",
        "fullLanguageName": "French",
        "defaultLanguage": "FR",
        "defaultCountry": "FR",
        "defaultLocale": "fr-FR"
      }
    },
    ...
  },
  "i18n": {
    "defaultCountry": "DE",
    "defaultLanguage": "DE",
    "availableLocale": ["en-GB","de-DE","fr-FR","it-IT"],
    "defaultLocale": "de-DE",
    "currencyCode": "EUR",
    "currencySign": "€",
    "currencySignPlacement": "append",
    "dateFormat": "HH:mm D.M.YYYY",
    "fullCountryName": "Germany",
    "fullLanguageName": "German",
    "bundleAllStoreviewLanguages": true
  },
  ...
}

In fact extending the configs isn't working totally smooth yet, like "appendStoreCode": true has to be on each node, etc. – but it is working for us at the moment and leaves a lot of flexibility without changing the code at the core.

cewald avatar Sep 06 '19 09:09 cewald

Claiming this issue for today's Hackathon. Working on it with @khako

rain2o avatar Sep 27 '19 12:09 rain2o

If somebody is still looking for this: I implemented it using a server hook inside a new module (like in robots module with the server.ts) to add a new ExpressJS route with a regex to check if a store code exists inside the URL (and more magic) and redirect to the first store-code in config.storeViews.mapStoreUrlsFor.

My src/modules/custom-module/server.ts looks like this:

import appConfig from 'config'
import { serverHooks } from '@vue-storefront/core/server/hooks'

if (appConfig.storeViews.multistore) {
  serverHooks.afterApplicationInitialized(({ app }) => {
    const blacklist = ['__webpack_hmr']
    const storeCodes = appConfig.storeViews.mapStoreUrlsFor

    const blacklistStr = blacklist.join('|')
    const storeCodesStr = storeCodes.join('|')
    const hasStoreCode = new RegExp(
      `^((?!\\/(${storeCodesStr})(\\/|$))(?!\\/(${blacklistStr})$))\\/?.*((?<=\\.html)|(?<!\\.[a-zA-Z0-9]*))$`
    )

    app.get(hasStoreCode, (req, res) => {
      const { path } = req
      const newUrl = '/' + storeCodes[0] + path

      let query = ''
      if (Object.values(req.query).length > 0) {
        const params = new URLSearchParams(req.query)
        query += '?' + params.toString()
      }

      res.redirect(newUrl + query)

      console.log('Redirect to default:', newUrl)
    })
  })
}

This isn't tested anywhere else but for my environment – you should check yourself if this catches all your use cases.

cewald avatar Dec 16 '19 07:12 cewald

@cewald great job; can you submit this module (as a separate npm - to the forum.vuestorfront.io modules section) or even to the core (src/modules)?

pkarw avatar Dec 16 '19 09:12 pkarw

I solved it by adding a second store, like which inherits from my desired default one.

For example: I'm having a german store config which I wan't to be the default store. So in best case the default store should also be available in http://localhost/specific/url and http://localhost/de/specific/url – so both is the /de store.

My solution would be to use the extend flag to extend from the default store ore visa versa to achieve this like:

{
  "defaultStoreCode": "default",
  "storeViews": {
    "mapStoreUrlsFor": ["de", "fr", "uk", "it"],
    "default": {
      "storeCode": "default",
      "storeId": 1,
      "name": "German Store",
      "url": "/",
      "appendStoreCode": false,
      "disabled": false,
      "elasticsearch": {
        "host": "/api/catalog",
        "index": "vue_storefront_catalog_1"
      },
      "tax": {
        "sourcePriceIncludesTax": false,
        "defaultCountry": "DE",
        "defaultRegion": "",
        "calculateServerSide": true
      }
    },
    "de": {
      "extend": "default",
      "storeCode": "de",
      "storeId": 1,
      "appendStoreCode": true,
      "disabled": false,
      "elasticsearch": {
        "host": "/api/catalog",
        "index": "vue_storefront_catalog_1"
      }
    },
    "fr": {
      "extend": "de",
      "storeCode": "fr",
      "storeId": 12,
      "name": "French Store",
      "url": "/fr",
      "appendStoreCode": true,
      "disabled": false,
      "elasticsearch": {
        "index": "vue_storefront_catalog_12"
      },
      "tax": {
        "defaultCountry": "FR"
      },
      "i18n": {
        "fullCountryName": "France",
        "fullLanguageName": "French",
        "defaultLanguage": "FR",
        "defaultCountry": "FR",
        "defaultLocale": "fr-FR"
      }
    },
    ...
  },
  "i18n": {
    "defaultCountry": "DE",
    "defaultLanguage": "DE",
    "availableLocale": ["en-GB","de-DE","fr-FR","it-IT"],
    "defaultLocale": "de-DE",
    "currencyCode": "EUR",
    "currencySign": "€",
    "currencySignPlacement": "append",
    "dateFormat": "HH:mm D.M.YYYY",
    "fullCountryName": "Germany",
    "fullLanguageName": "German",
    "bundleAllStoreviewLanguages": true
  },
  ...
}

In fact extending the configs isn't working totally smooth yet, like "appendStoreCode": true has to be on each node, etc. – but it is working for us at the moment and leaves a lot of flexibility without changing the code at the core.

Have you also set up a default store in Magento to make it work? I triet your config but it doesn't work as it should :(

antonioglass avatar Sep 23 '21 19:09 antonioglass