1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2026-02-05 18:14:04 +00:00

cleanup, resolve todos, add immediate todos

This commit is contained in:
Samuel Štancl 2025-02-20 11:59:02 +01:00
parent eac88dcc2a
commit a85c88e258
16 changed files with 71 additions and 90 deletions

View file

@ -145,24 +145,20 @@ class TenancyServiceProvider extends ServiceProvider
*/ */
protected function overrideUrlInTenantContext(): void protected function overrideUrlInTenantContext(): void
{ {
/** // // Import your tenant model!
* Import your tenant model! // \Stancl\Tenancy\Bootstrappers\RootUrlBootstrapper::$rootUrlOverride = function (Tenant $tenant, string $originalRootUrl) {
* // $tenantDomain = $tenant instanceof \Stancl\Tenancy\Contracts\SingleDomainTenant
* \Stancl\Tenancy\Bootstrappers\RootUrlBootstrapper::$rootUrlOverride = function (Tenant $tenant, string $originalRootUrl) { // ? $tenant->domain
* $tenantDomain = $tenant instanceof \Stancl\Tenancy\Contracts\SingleDomainTenant // : $tenant->domains->first()->domain;
* ? $tenant->domain // $scheme = str($originalRootUrl)->before('://');
* : $tenant->domains->first()->domain;
* // // If you're using domain identification:
* $scheme = str($originalRootUrl)->before('://'); // return $scheme . '://' . $tenantDomain . '/';
*
* // If you're using domain identification: // // If you're using subdomain identification:
* return $scheme . '://' . $tenantDomain . '/'; // $originalDomain = str($originalRootUrl)->after($scheme . '://');
* // return $scheme . '://' . $tenantDomain . '.' . $originalDomain . '/';
* // If you're using subdomain identification: // };
* $originalDomain = str($originalRootUrl)->after($scheme . '://');
* return $scheme . '://' . $tenantDomain . '.' . $originalDomain . '/';
* };
*/
} }
public function register() public function register()
@ -178,32 +174,17 @@ class TenancyServiceProvider extends ServiceProvider
$this->makeTenancyMiddlewareHighestPriority(); $this->makeTenancyMiddlewareHighestPriority();
$this->overrideUrlInTenantContext(); $this->overrideUrlInTenantContext();
/** // // Include soft deleted resources in synced resource queries.
* Include soft deleted resources in synced resource queries. // ResourceSyncing\Listeners\UpdateOrCreateSyncedResource::$scopeGetModelQuery = function (Builder $query) {
* // if ($query->hasMacro('withTrashed')) {
* ResourceSyncing\Listeners\UpdateOrCreateSyncedResource::$scopeGetModelQuery = function (Builder $query) { // $query->withTrashed();
* if ($query->hasMacro('withTrashed')) { // }
* $query->withTrashed(); // };
* }
* };
*/
/** // // To make Livewire v3 work with Tenancy, make the update route universal.
* To make Livewire v3 work with Tenancy, make the update route universal. // Livewire::setUpdateRoute(function ($handle) {
* // return RouteFacade::post('/livewire/update', $handle)->middleware(['web', 'universal', \Stancl\Tenancy::defaultMiddleware()]);
* Livewire::setUpdateRoute(function ($handle) { // });
* return RouteFacade::post('/livewire/update', $handle)->middleware(['web', 'universal']);
* });
*/
// if (InitializeTenancyByRequestData::inGlobalStack()) {
// FortifyRouteBootstrapper::$fortifyHome = 'dashboard';
// TenancyUrlGenerator::$prefixRouteNames = false;
// }
if (tenancy()->globalStackHasMiddleware(config('tenancy.identification.path_identification_middleware'))) {
TenancyUrlGenerator::$prefixRouteNames = true;
}
} }
protected function bootEvents() protected function bootEvents()
@ -228,10 +209,7 @@ class TenancyServiceProvider extends ServiceProvider
->group(base_path('routes/tenant.php')); ->group(base_path('routes/tenant.php'));
} }
// Delete this condition when using route-level path identification // $this->cloneRoutes();
if (tenancy()->globalStackHasMiddleware(config('tenancy.identification.path_identification_middleware'))) {
$this->cloneRoutes();
}
}); });
} }
@ -245,16 +223,13 @@ class TenancyServiceProvider extends ServiceProvider
/** @var CloneRoutesAsTenant $cloneRoutes */ /** @var CloneRoutesAsTenant $cloneRoutes */
$cloneRoutes = $this->app->make(CloneRoutesAsTenant::class); $cloneRoutes = $this->app->make(CloneRoutesAsTenant::class);
/** // // You can provide a closure for cloning a specific route, e.g.:
* You can provide a closure for cloning a specific route, e.g.: // $cloneRoutes->cloneUsing('welcome', function () {
* $cloneRoutes->cloneUsing('welcome', function () { // RouteFacade::get('/tenant-welcome', fn () => 'Current tenant: ' . tenant()->getTenantKey())
* RouteFacade::get('/tenant-welcome', fn () => 'Current tenant: ' . tenant()->getTenantKey()) // ->middleware(['universal', InitializeTenancyByPath::class])
* ->middleware(['universal', InitializeTenancyByPath::class]) // ->name('tenant.welcome');
* ->name('tenant.welcome'); // });
* }); // // To see the default behavior of cloning the universal routes, check out the cloneRoute() method in CloneRoutesAsTenant.
*
* To see the default behavior of cloning the universal routes, check out the cloneRoute() method in CloneRoutesAsTenant.
*/
$cloneRoutes->handle(); $cloneRoutes->handle();
} }

View file

@ -130,8 +130,6 @@ return [
'cache_store' => null, // null = default 'cache_store' => null, // null = default
], ],
], ],
// todo@docs update integration guides to use Stancl\Tenancy::defaultMiddleware()
], ],
/** /**
@ -215,7 +213,7 @@ return [
// 'pgsql' => Stancl\Tenancy\Database\TenantDatabaseManagers\PermissionControlledPostgreSQLSchemaManager::class, // Also permission controlled // 'pgsql' => Stancl\Tenancy\Database\TenantDatabaseManagers\PermissionControlledPostgreSQLSchemaManager::class, // Also permission controlled
], ],
// todo@docblock // todo0
'drop_tenant_databases_on_migrate_fresh' => false, 'drop_tenant_databases_on_migrate_fresh' => false,
], ],
@ -320,7 +318,7 @@ return [
*/ */
'url_override' => [ 'url_override' => [
// Note that the local disk you add must exist in the tenancy.filesystem.root_override config // Note that the local disk you add must exist in the tenancy.filesystem.root_override config
// todo@v4 Rename url_override to something that describes the config key better // todo0 Rename url_override to something that describes the config key better
'public' => 'public-%tenant%', 'public' => 'public-%tenant%',
], ],
@ -356,7 +354,7 @@ return [
* leave asset() helper tenancy disabled and explicitly use tenant_asset() calls in places * leave asset() helper tenancy disabled and explicitly use tenant_asset() calls in places
* where you want to use tenant-specific assets (product images, avatars, etc). * where you want to use tenant-specific assets (product images, avatars, etc).
*/ */
'asset_helper_tenancy' => false, // todo@rename asset_helper_override? 'asset_helper_override' => false,
], ],
/** /**

View file

@ -92,7 +92,7 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
protected function assetHelper(string|false $suffix): void protected function assetHelper(string|false $suffix): void
{ {
if (! $this->app['config']['tenancy.filesystem.asset_helper_tenancy']) { if (! $this->app['config']['tenancy.filesystem.asset_helper_override']) {
return; return;
} }

View file

@ -11,10 +11,15 @@ use Stancl\Tenancy\Enums\Context;
use Stancl\Tenancy\Resolvers\PathTenantResolver; use Stancl\Tenancy\Resolvers\PathTenantResolver;
/** /**
* Allows customizing Fortify action redirects * Allows customizing Fortify action redirects so that they can also redirect
* so that they can also redirect to tenant routes instead of just the central routes. * to tenant routes instead of just the central routes.
* *
* Works with path and query string identification. * This should be used with path/query string identification OR when using Fortify
* universally, including with domains.
*
* When using domain identification, there's no need to pass the tenant parameter,
* you only want to customize the routes being used, so you can set $passTenantParameter
* to false.
*/ */
class FortifyRouteBootstrapper implements TenancyBootstrapper class FortifyRouteBootstrapper implements TenancyBootstrapper
{ {
@ -50,6 +55,13 @@ class FortifyRouteBootstrapper implements TenancyBootstrapper
*/ */
public static array $fortifyRedirectMap = []; public static array $fortifyRedirectMap = [];
/**
* Should the tenant parameter be passed to fortify routes in the tenant context.
*
* This should be enabled with path/query string identification and disabled with domain identification
*/
public static bool $passTenantParameter = true;
/** /**
* Tenant route that serves as Fortify's home (e.g. a tenant dashboard route). * Tenant route that serves as Fortify's home (e.g. a tenant dashboard route).
* This route will always receive the tenant parameter. * This route will always receive the tenant parameter.
@ -82,7 +94,7 @@ class FortifyRouteBootstrapper implements TenancyBootstrapper
$generateLink = function (array $redirect) use ($tenantKey, $tenantParameterName) { $generateLink = function (array $redirect) use ($tenantKey, $tenantParameterName) {
// Specifying the context is only required with query string identification // Specifying the context is only required with query string identification
// because with path identification, the tenant parameter should always present // because with path identification, the tenant parameter should always present
$passTenantParameter = $redirect['context'] === Context::TENANT; $passTenantParameter = static::$passTenantParameter && $redirect['context'] === Context::TENANT;
// Only pass the tenant parameter when the user should be redirected to a tenant route // Only pass the tenant parameter when the user should be redirected to a tenant route
return route($redirect['route_name'], $passTenantParameter ? [$tenantParameterName => $tenantKey] : []); return route($redirect['route_name'], $passTenantParameter ? [$tenantParameterName => $tenantKey] : []);

View file

@ -42,7 +42,7 @@ class RootUrlBootstrapper implements TenancyBootstrapper
* that are generating URLs in things like mails, the bootstrapper should be used * that are generating URLs in things like mails, the bootstrapper should be used
* just like in any queued job. * just like in any queued job.
* *
* todo@revisit * todo0 update docblock
*/ */
public static bool $rootUrlOverrideInTests = true; public static bool $rootUrlOverrideInTests = true;

View file

@ -10,6 +10,7 @@ trait CreatesDatabaseUsers
{ {
public function createDatabase(TenantWithDatabase $tenant): bool public function createDatabase(TenantWithDatabase $tenant): bool
{ {
// todo0 only continue if this returns true, same below
parent::createDatabase($tenant); parent::createDatabase($tenant);
return $this->createUser($tenant->database()); return $this->createUser($tenant->database());

View file

@ -23,6 +23,8 @@ use Stancl\Tenancy\Events\PullingPendingTenant;
*/ */
trait HasPending trait HasPending
{ {
public static string $pendingSinceCast = 'timestamp';
/** Boot the trait. */ /** Boot the trait. */
public static function bootHasPending(): void public static function bootHasPending(): void
{ {
@ -32,7 +34,7 @@ trait HasPending
/** Initialize the trait. */ /** Initialize the trait. */
public function initializeHasPending(): void public function initializeHasPending(): void
{ {
$this->casts['pending_since'] = 'timestamp'; $this->casts['pending_since'] = static::$pendingSinceCast;
} }
/** Determine if the model instance is in a pending state. */ /** Determine if the model instance is in a pending state. */

View file

@ -11,8 +11,6 @@ class MicrosoftSQLDatabaseManager extends TenantDatabaseManager
public function createDatabase(TenantWithDatabase $tenant): bool public function createDatabase(TenantWithDatabase $tenant): bool
{ {
$database = $tenant->database()->getName(); $database = $tenant->database()->getName();
$charset = $this->connection()->getConfig('charset');
$collation = $this->connection()->getConfig('collation'); // todo check why these are not used
return $this->connection()->statement("CREATE DATABASE [{$database}]"); return $this->connection()->statement("CREATE DATABASE [{$database}]");
} }

View file

@ -32,6 +32,8 @@ class PermissionControlledPostgreSQLSchemaManager extends PostgreSQLSchemaManage
// Grant permissions to any existing tables. This is used with RLS // Grant permissions to any existing tables. This is used with RLS
// todo@samuel refactor this along with the todo in TenantDatabaseManager // todo@samuel refactor this along with the todo in TenantDatabaseManager
// and move the grantPermissions() call inside the condition in `ManagesPostgresUsers::createUser()` // and move the grantPermissions() call inside the condition in `ManagesPostgresUsers::createUser()`
// but maybe moving it inside $createUser is wrong because some central user may migrate new tables
// while the RLS user should STILL get access to those tables
foreach ($tables as $table) { foreach ($tables as $table) {
$tableName = $table->table_name; $tableName = $table->table_name;

View file

@ -7,15 +7,11 @@ namespace Stancl\Tenancy\Events\Contracts;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Stancl\Tenancy\Contracts\Tenant; use Stancl\Tenancy\Contracts\Tenant;
abstract class TenantEvent // todo we could add a feature to JobPipeline that automatically gets data for the send() from here abstract class TenantEvent // todo0 we could add a feature to JobPipeline that automatically gets data for the send() from here
{ {
use SerializesModels; use SerializesModels;
/** @var Tenant */ public function __construct(
public $tenant; public Tenant $tenant,
) {}
public function __construct(Tenant $tenant)
{
$this->tenant = $tenant;
}
} }

View file

@ -2,8 +2,6 @@
declare(strict_types=1); declare(strict_types=1);
// todo perhaps create Identification namespace
namespace Stancl\Tenancy\Exceptions; namespace Stancl\Tenancy\Exceptions;
use Stancl\Tenancy\Contracts\TenantCouldNotBeIdentifiedException; use Stancl\Tenancy\Contracts\TenantCouldNotBeIdentifiedException;

View file

@ -54,6 +54,7 @@ class UserImpersonation implements Feature
return redirect($token->redirect_url); return redirect($token->redirect_url);
} }
// todo0 test with session scoping
public static function isImpersonating(): bool public static function isImpersonating(): bool
{ {
return session()->has('tenancy_impersonating'); return session()->has('tenancy_impersonating');
@ -62,7 +63,7 @@ class UserImpersonation implements Feature
/** /**
* Logout from the current domain and forget impersonation session. * Logout from the current domain and forget impersonation session.
*/ */
public static function leave(): void // todo@name possibly rename public static function stopImpersonating(): void
{ {
auth()->logout(); auth()->logout();

View file

@ -68,7 +68,7 @@ class PreventAccessFromUnwantedDomains
return in_array($request->getHost(), config('tenancy.identification.central_domains'), true); return in_array($request->getHost(), config('tenancy.identification.central_domains'), true);
} }
// todo@samuel // todo@samuel technically not an identification middleware but probably ok to keep this here
public function requestHasTenant(Request $request): bool public function requestHasTenant(Request $request): bool
{ {
return false; return false;

View file

@ -6,8 +6,6 @@ namespace Stancl\Tenancy\Overrides;
use Illuminate\Cache\CacheManager as BaseCacheManager; use Illuminate\Cache\CacheManager as BaseCacheManager;
// todo@move move to Cache namespace?
class CacheManager extends BaseCacheManager class CacheManager extends BaseCacheManager
{ {
/** /**

View file

@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Stancl\Tenancy\Database\Contracts\TenantWithDatabase; use Stancl\Tenancy\Database\Contracts\TenantWithDatabase;
// todo@move move all resource syncing-related things to a separate namespace? // todo@move move all resource syncing-related things to a separate namespace? todo0 seems to be done, confirm EVERYTHING is moved
/** /**
* @property-read TenantWithDatabase[]|Collection<int, TenantWithDatabase&Model> $tenants * @property-read TenantWithDatabase[]|Collection<int, TenantWithDatabase&Model> $tenants

View file

@ -65,7 +65,7 @@ test('asset can be accessed using the url returned by the tenant asset helper',
test('asset helper returns a link to tenant asset controller when asset url is null', function () { test('asset helper returns a link to tenant asset controller when asset url is null', function () {
config(['app.asset_url' => null]); config(['app.asset_url' => null]);
config(['tenancy.filesystem.asset_helper_tenancy' => true]); config(['tenancy.filesystem.asset_helper_override' => true]);
$tenant = Tenant::create(); $tenant = Tenant::create();
tenancy()->initialize($tenant); tenancy()->initialize($tenant);
@ -78,7 +78,7 @@ test('asset helper returns a link to tenant asset controller when asset url is n
test('asset helper returns a link to an external url when asset url is not null', function () { test('asset helper returns a link to an external url when asset url is not null', function () {
config(['app.asset_url' => 'https://an-s3-bucket']); config(['app.asset_url' => 'https://an-s3-bucket']);
config(['tenancy.filesystem.asset_helper_tenancy' => true]); config(['tenancy.filesystem.asset_helper_override' => true]);
$tenant = Tenant::create(); $tenant = Tenant::create();
tenancy()->initialize($tenant); tenancy()->initialize($tenant);
@ -93,7 +93,7 @@ test('asset helper works correctly with path identification', function (bool $ke
TenancyUrlGenerator::$prefixRouteNames = true; TenancyUrlGenerator::$prefixRouteNames = true;
TenancyUrlGenerator::$passTenantParameterToRoutes = true; TenancyUrlGenerator::$passTenantParameterToRoutes = true;
config(['tenancy.filesystem.asset_helper_tenancy' => true]); config(['tenancy.filesystem.asset_helper_override' => true]);
config(['tenancy.identification.default_middleware' => InitializeTenancyByPath::class]); config(['tenancy.identification.default_middleware' => InitializeTenancyByPath::class]);
config(['tenancy.bootstrappers' => array_merge([UrlGeneratorBootstrapper::class], config('tenancy.bootstrappers'))]); config(['tenancy.bootstrappers' => array_merge([UrlGeneratorBootstrapper::class], config('tenancy.bootstrappers'))]);
@ -165,7 +165,7 @@ test('asset helper tenancy can be disabled', function () {
config([ config([
'app.asset_url' => null, 'app.asset_url' => null,
'tenancy.filesystem.asset_helper_tenancy' => false, 'tenancy.filesystem.asset_helper_override' => false,
]); ]);
$tenant = Tenant::create(); $tenant = Tenant::create();