laravel-multitenancy
laravel-multitenancy copied to clipboard
Routes are not cached
// DomainServiceProvider.php
<?php
namespace App\Providers;
use App\Models\Tenant;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\LazyCollection;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\Finder\Finder;
class DomainServiceProvider extends ServiceProvider implements DeferrableProvider
{
public function boot()
{
if ($this->app->runningUnitTests()) {
return;
}
$this->tenants()->each(function (Tenant $tenant) {
$this->registerWebRoutes($tenant);
$this->registerApiRoutes($tenant);
$this->registerResources($tenant);
});
}
protected function registerWebRoutes(Tenant $tenant): void
{
Route::group([
'as' => "{$tenant->namespace}.",
'domain' => $tenant->domain,
'middleware' => 'web',
], function () use ($tenant) {
$this->loadRoutesFrom(app_path("Domain/{$tenant->name}/Routes/web.php"));
});
}
protected function registerApiRoutes(Tenant $tenant): void
{
Route::group([
'as' => "{$tenant->namespace}.",
'domain' => $tenant->domain,
'prefix' => 'api',
'middleware' => 'api',
], function () use ($tenant) {
$this->loadRoutesFrom(app_path("Domain/{$tenant->name}/Routes/api.php"));
});
}
protected function registerResources(Tenant $tenant): void
{
$current = $this->currentTenant();
if ($tenant->is($current)) {
$path = fn (string $path) => app_path("Domain/{$tenant->name}/{$path}");
$this->mergeConfig($path('Config'), 'domain');
$this->registerServiceProviders('domain');
$this->loadTranslationsFrom($path('Resources/Translations'), 'domain');
$this->loadViewsFrom($path('Resources/Views'), 'domain');
}
}
protected function mergeConfig(string $path, string $namespace): void
{
$finder = (new Finder())->name('*.php')->in($path);
foreach ($finder as $file) {
$path = $file->getRealPath();
$name = $file->getFilenameWithoutExtension();
$this->mergeConfigFrom($path, "{$namespace}.".strtolower($name));
}
}
protected function registerServiceProviders(string $namespace): void
{
$providers = collect(config("{$namespace}.app.providers"));
$providers->each(fn (string $provider) => app()->register($provider));
}
protected function currentTenant(): ?Tenant
{
return Tenant::current();
}
protected function tenants(): LazyCollection
{
return Tenant::cursor();
}
}
// SwitchDomainTask.php
<?php
namespace App\Support\Multitenancy\Tasks;
use App\Providers\DomainServiceProvider;
use Spatie\Multitenancy\Models\Tenant;
use Spatie\Multitenancy\Tasks\SwitchTenantTask;
class SwitchDomainTask implements SwitchTenantTask
{
public function makeCurrent(Tenant $tenant): void
{
app()->register(DomainServiceProvider::class);
}
public function forgetCurrent(): void
{
}
}
$ sail artisan tenant:artisan route:list // lists all routes correctly from tenant (domain)
$ sail artisan tenant:artisan route:cache // does not include routes from tenant (domain)
This does not include the routes from Domain\Foo\Routes\web.php and/or Domain\Foo\Routes\api.php. Clearing the cache makes it work again, but I would like to cache the routes.
Are you using the package task to cache the routes?
@masterix21 Thanks for your reply. :)
Yes, this is my tasks array:
'switch_tenant_tasks' => [
\Spatie\Multitenancy\Tasks\PrefixCacheTask::class,
\App\Support\Multitenancy\Tasks\SwitchFacadeInstancesTask::class,
\App\Support\Multitenancy\Tasks\SwitchTenantTelescopeTask::class,
\App\Support\Multitenancy\Tasks\SwitchRootUrlTask::class,
\App\Support\Multitenancy\Tasks\SwitchDomainTask::class, <== this one
\Spatie\Multitenancy\Tasks\SwitchRouteCacheTask::class,
],
It's weird, it does not seem to cache the routes, as they aren't included in the bootstrap array.
Ok, so how could you work the route cache if you instructed your code to use custom routes after the cache task?
Finally, I think you like to use a custom cache file for each tenant, but the package cache task creates only one file shared across all tenants: I think you should create a custom one.
@masterix21 That's actually a valid point, however the service provider now runs on every tenant switch, as I need to be sure a tenant connection exists. Would it be possible to register the provider earlier in the process? Is it possible to create a service provider which runs even on let's say composer install?
I'm now thinking of two service providers. One that caches all the routes (no defer provider) and one that does all the domain logic.
Thanks for helping me, I'm really lost as the routes are listed, but not cached.
Sure, you can on composer install time, for example with php artisan tenants:artisan "route:cache"