Multiple sites: Building the outgoing URLs

In previous recipe we talked about multiple catalogs with same “UriSegment” – which we had a working implementation for incoming URL, i.e. when a customer visit a product url, we know which catalog we should choose from. But we still need to cover the generation of outgoing URL. I.e. when we link a product (For example, from a campaign page), we need to generate an URL which take the “catalog-less” pattern into account.

We need to understand how the outgoing URL is built. The hierarchical router builds the URL by the `RouteSegment` of contents. However, we want to the urls appear to have same catalog, so the `RouteSegment` part for the catalogs must be the same, regardless of the true catalogs. Because all catalogs are on same level, their `RouteSegment` must be unique – and this is enforced from Framework level (which is understandable, otherwise, how can it know which content to choose).

We can’t also rely on name, which is required to be unique.

What can we do?

Well – we already have magic router which automatically figures out which catalog to use regardless of the `RouteSegment` for catalog, so we can just return some magic value.

The tricky part is to know which method to override. Luckily for us, Commerce provides us `TryGetRouteSegment`, which returns the `RouteSegment` of a content in a specific language.

    protected override bool TryGetRouteSegment(ContentReference contentLink, string language, out string segment)
    {
        CatalogContent catalogContent;
        if (_contentLoader.TryGet<CatalogContent>(contentLink, CultureInfo.GetCultureInfo(language), out catalogContent))
        {
            segment = "products";
            return true;
        }
        return base.TryGetRouteSegment(contentLink, language, out segment);
    }

The code is fairly simple, we try to load the catalog content, and if succeed, and return a fixed value – “products”, otherwise, we fallback to the default implementation.

We return something hard-coded here, but you can of course make that a configurable value. Just keep in mind that changing URLs will hurt your SEO and also make your customers confusing, so do it as little as possible. An alternative is to have a plugin to handle the “changed” URLs for you, so at least it will redirect your customers to the correct, working link.

The final part is to register our new router. This is almost as same as with our previous recipe to remove the catalog name from url – we just need to use our new class here.

    Func<ContentReference> startingPoint = () =>
                ContentReference.IsNullOrEmpty(SiteDefinition.Current.StartPage)
                    ? SiteDefinition.Current.RootPage
                    : SiteDefinition.Current.StartPage;
    var referenceConverter = ServiceLocator.Current.GetInstance<ReferenceConverter>();
    var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
    var commerceRootContent = contentLoader.Get<CatalogContentBase>(referenceConverter.GetRootLink());
    RouteTable.Routes.RegisterPartialRouter(new MultipleSiteCatalogPartialRouter(startingPoint, commerceRootContent, false, context.Locate.ContentLoader()));

And that would mean your outgoing URLs will respect the fixed catalog segment you wanted them to.