octobercms / october

Self-hosted CMS platform based on the Laravel PHP Framework.
https://octobercms.com/
Other
11.01k stars 2.21k forks source link

Multisite & Combiner - cannot find /combiner/xxx files on all sites #5773

Closed ccamarillo closed 7 months ago

ccamarillo commented 8 months ago

We're unable to use Combiner in theme partials when using Multisite. Our setup:

While debugging, we see that on the site that is failing, it cannot find the files in the cache.

I was looking through the CMS config and I did not see anywhere I could specify the location of the CMS files per site - where to save the cache, and where to retrieve the from.

To summarize, OCMS cannot find the /combiner/xxxx files for all sites in Multisite. One site may work, but the rest do not.

daftspunk commented 8 months ago

Hi @ccamarillo

Each site uses its own file cache, configured in our plugin's Plugin.php

Can you share the code used here? It is possible that this code is not running for the combiner URLs and would cause this outcome.

ccamarillo commented 8 months ago

Here's where we specify our two different caches for the two different sites: Plugin.php

public function boot()
    {

        $siteCode = \OakleySI\OakleySI\Classes\Multisite::getSiteCode();

        // CACHE
        if ($siteCode == 'ess-direct') {
            $cacheDriver = 'ess';
        } else {
            $cacheDriver = 'oakley';
        }

        // Once you have the cacheDriver, set the configuration
        config(['cache.default' => $cacheDriver]);
        ...
        }

And then the cache.php config:

return [
  'stores' => [
    'ess' => [
      'driver' => 'file',
      'path' => storage_path('framework/cache/ess'),
    ],
    'oakley' => [
      'driver' => 'file',
      'path' => storage_path('framework/cache/oakley'),
    ],
    ...
  ]
]

We've established a workaround - avoid using the combiner to load our scripts and stylesheets - load one at a time. Thanks @daftspunk

ccamarillo commented 8 months ago

The reason we have two caches is because with just one, one site would overwrite the files and the other site's files would disappear from the cache.

daftspunk commented 8 months ago

Thanks, may I ask what are the contents of the OakleySI\OakleySI\Classes\Multisite::getSiteCode() method?

ccamarillo commented 8 months ago
public static function getSiteCode() {
        $installedOnDomains = env('INSTALLED_ON_DOMAINS', false);

        $user = \Auth::getUser();
        if ($user) {
            if ($user->customer_group === null) {
                return self::getSiteCodeFromUrl();
            }
            $siteCode = $user->customer_group->code;
            return $siteCode;
        } else if (!$installedOnDomains) {
            // get first segment after domain name
            $url = \Request::url();
            $urlParts = explode('/', $url);
            if (count($urlParts) < 4) {
                return 'ess-direct'; // fall back to this site if no code exists in url segment / such as when running in console or admin
            } else {
                $siteCode = $urlParts[3];
                return $siteCode;
            }
        } else {
            return self::getSiteCodeFromUrl();
        }
    }

We are using OFFLINE Mall customer groups to allow users to access one site or another. If they are logged in, we look at the OFFLINE Mall customer group to identify what site they have access to. If they come to the site and are not logged in, we determine which cache to use based on the URL segments or domain. Customer groups and Sites share codes... that is how I am managing access to these sites. We will redirect from one site to another if the user tries to access the wrong site based off their customer group.

I suppose if there is some other method we could use to determine which cache to use, I'm all ears on that. There may be a mismatch between what site id is actually loading and what I am determining here? Thank you.

ccamarillo commented 8 months ago

Also, here is getSiteCodeFromUrl():

private static function getSiteCodeFromUrl() {
        // get the beginning of the url, including protocol and domain name
        $url = \Request::root();
        $site = \System\Models\SiteDefinition::where('app_url', $url)->first();
        if (!$site) {
            return 'ess-direct'; // fall back to this in admin
        }
        $siteCode = $site->code;
        return $siteCode;
    }
daftspunk commented 7 months ago

In v3.6, we've added the ability to enable multisite in the system combiner:

// config/multisite.php
'features' => [
    'system_asset_combiner' => true, // ← set to true
    // ...
],

Once enabled, a different cache key is used for each site, and the active site is set when the combiner first loads to ensure custom configuration will also apply.

Hopefully this will resolve the original issue by itself.

To preserve the current solution, the boot method should hook the site.changed event when the change becomes available later in the app lifecycle.

public function boot()
{
    Event::listen('site.changed', function($id) {
        $siteCode = \Site::getSiteCodeFromContext();

        // CACHE
        if ($siteCode == 'ess-direct') {
            $cacheDriver = 'ess';
        } else {
            $cacheDriver = 'oakley';
        }

        // Once you have the cacheDriver, set the configuration
        config(['cache.default' => $cacheDriver]);
    });
}

v3.6 should be released soon.

I hope this helps!

ccamarillo commented 7 months ago

Thank you SO MUCH @daftspunk We are getting a lot of the multisite feature - we're using it to host two different e-commerce sites with different everything. We had to make some utilities for the Twig templates in mail templates, and we had to do some other things to separate users, because we were not comfortable turning on Mulltiiste for users or email templates. Is that wise?

I'll let you know how it goes with the next upgrade.