1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-12 06:44:04 +00:00

Add TenantConfigBootstrapper, deprecate Feature implementation

The feature was pretty much a soft-bootstrapper -- it listened
to both Bootstrapped and Reverted. Bootstrappers have a few more
protections in terms of error handling and safe reverting, so there's
no point in (badly) re-implementing bootstrapper functionality within
TenantConfig just so it could be a Feature.

Going forward, all Features should be things that are mostly agnostic
of the tenant state, and especially they should not use bootstrapped/
reverted events. Bootstrappers are simply more appropriate and safe.
This commit is contained in:
Samuel Štancl 2025-09-26 11:29:14 +02:00
parent c152031cc1
commit b320f8f33d
5 changed files with 68 additions and 20 deletions

View file

@ -178,6 +178,7 @@ return [
Bootstrappers\DatabaseSessionBootstrapper::class,
// Configurable bootstrappers
// Bootstrappers\TenantConfigBootstrapper::class,
// Bootstrappers\RootUrlBootstrapper::class,
// Bootstrappers\UrlGeneratorBootstrapper::class,
// Bootstrappers\MailConfigBootstrapper::class, // Note: Queueing mail requires using QueueTenancyBootstrapper with $forceRefresh set to true
@ -419,7 +420,6 @@ return [
'features' => [
// Stancl\Tenancy\Features\UserImpersonation::class,
// Stancl\Tenancy\Features\TelescopeTags::class,
// Stancl\Tenancy\Features\TenantConfig::class,
// Stancl\Tenancy\Features\CrossDomainRedirect::class,
// Stancl\Tenancy\Features\ViteBundler::class,
// Stancl\Tenancy\Features\DisallowSqliteAttach::class,

View file

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Bootstrappers;
use Illuminate\Config\Repository;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
use Stancl\Tenancy\Contracts\Tenant;
class TenantConfigBootstrapper implements TenancyBootstrapper
{
public array $originalConfig = [];
/** @var array<string, string|array> */
public static array $storageToConfigMap = [
// 'paypal_api_key' => 'services.paypal.api_key',
];
public function __construct(
protected Repository $config,
) {}
public function bootstrap(Tenant $tenant): void
{
foreach (static::$storageToConfigMap as $storageKey => $configKey) {
/** @var Tenant&Model $tenant */
$override = Arr::get($tenant, $storageKey);
if (! is_null($override)) {
if (is_array($configKey)) {
foreach ($configKey as $key) {
$this->originalConfig[$key] = $this->originalConfig[$key] ?? $this->config->get($key);
$this->config->set($key, $override);
}
} else {
$this->originalConfig[$configKey] = $this->originalConfig[$configKey] ?? $this->config->get($configKey);
$this->config->set($configKey, $override);
}
}
}
}
public function revert(): void
{
foreach ($this->originalConfig as $key => $value) {
$this->config->set($key, $value);
}
}
}

View file

@ -13,6 +13,9 @@ use Stancl\Tenancy\Contracts\Tenant;
use Stancl\Tenancy\Events\RevertedToCentralContext;
use Stancl\Tenancy\Events\TenancyBootstrapped;
// todo@release remove this class
/** @deprecated Use the TenantConfigBootstrapper instead. */
class TenantConfig implements Feature
{
public array $originalConfig = [];

View file

@ -2,34 +2,27 @@
declare(strict_types=1);
use Illuminate\Support\Facades\Event;
use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Features\TenantConfig;
use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Bootstrappers\TenantConfigBootstrapper;
use Stancl\Tenancy\Tests\Etc\Tenant;
use function Stancl\Tenancy\Tests\pest;
use function Stancl\Tenancy\Tests\withBootstrapping;
beforeEach(function () {
config([
'tenancy.features' => [TenantConfig::class],
'tenancy.bootstrappers' => [],
'tenancy.bootstrappers' => [TenantConfigBootstrapper::class],
]);
tenancy()->bootstrapFeatures();
withBootstrapping();
});
afterEach(function () {
TenantConfig::$storageToConfigMap = [];
TenantConfigBootstrapper::$storageToConfigMap = [];
});
test('nested tenant values are merged', function () {
expect(config('whitelabel.theme'))->toBeNull();
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
TenantConfig::$storageToConfigMap = [
TenantConfigBootstrapper::$storageToConfigMap = [
'whitelabel.config.theme' => 'whitelabel.theme',
];
@ -44,10 +37,8 @@ test('nested tenant values are merged', function () {
test('config is merged and removed', function () {
expect(config('services.paypal'))->toBe(null);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
TenantConfig::$storageToConfigMap = [
TenantConfigBootstrapper::$storageToConfigMap = [
'paypal_api_public' => 'services.paypal.public',
'paypal_api_private' => 'services.paypal.private',
];
@ -69,10 +60,8 @@ test('config is merged and removed', function () {
test('the value can be set to multiple config keys', function () {
expect(config('services.paypal'))->toBe(null);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
TenantConfig::$storageToConfigMap = [
TenantConfigBootstrapper::$storageToConfigMap = [
'paypal_api_public' => [
'services.paypal.public1',
'services.paypal.public2',

View file

@ -25,6 +25,7 @@ use Stancl\Tenancy\Bootstrappers\BroadcastChannelPrefixBootstrapper;
use Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper;
use function Stancl\Tenancy\Tests\pest;
use Stancl\Tenancy\Bootstrappers\DatabaseCacheBootstrapper;
use Stancl\Tenancy\Bootstrappers\TenantConfigBootstrapper;
abstract class TestCase extends \Orchestra\Testbench\TestCase
{
@ -193,6 +194,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
$app->singleton(RootUrlBootstrapper::class);
$app->singleton(UrlGeneratorBootstrapper::class);
$app->singleton(FilesystemTenancyBootstrapper::class);
$app->singleton(TenantConfigBootstrapper::class);
}
protected function getPackageProviders($app)