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

Merge branch 'master' into cache-prefix

This commit is contained in:
lukinovec 2023-01-05 15:07:59 +01:00 committed by GitHub
commit f8f0e1e5da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 440 additions and 105 deletions

View file

@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Bootstrappers;
use Illuminate\Config\Repository;
use Illuminate\Foundation\Application;
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
use Stancl\Tenancy\Contracts\Tenant;
class MailTenancyBootstrapper implements TenancyBootstrapper
{
/**
* Tenant properties to be mapped to config (similarly to the TenantConfig feature).
*
* For example:
* [
* 'config.key.name' => 'tenant_property',
* ]
*/
public static array $credentialsMap = [];
public static string|null $mailer = null;
protected array $originalConfig = [];
public static array $mapPresets = [
'smtp' => [
'mail.mailers.smtp.host' => 'smtp_host',
'mail.mailers.smtp.port' => 'smtp_port',
'mail.mailers.smtp.username' => 'smtp_username',
'mail.mailers.smtp.password' => 'smtp_password',
],
];
public function __construct(
protected Repository $config,
protected Application $app
) {
static::$mailer ??= $config->get('mail.default');
static::$credentialsMap = array_merge(static::$credentialsMap, static::$mapPresets[static::$mailer] ?? []);
}
public function bootstrap(Tenant $tenant): void
{
// Forget the mail manager instance to clear the cached mailers
$this->app->forgetInstance('mail.manager');
$this->setConfig($tenant);
}
public function revert(): void
{
$this->unsetConfig();
$this->app->forgetInstance('mail.manager');
}
protected function setConfig(Tenant $tenant): void
{
foreach (static::$credentialsMap as $configKey => $storageKey) {
$override = $tenant->$storageKey;
if (array_key_exists($storageKey, $tenant->getAttributes())) {
$this->originalConfig[$configKey] ??= $this->config->get($configKey);
$this->config->set($configKey, $override);
}
}
}
protected function unsetConfig(): void
{
foreach ($this->originalConfig as $key => $value) {
$this->config->set($key, $value);
}
}
}

View file

@ -10,7 +10,6 @@ use Illuminate\Database\Eloquent\Builder;
class ClearPendingTenants extends Command
{
protected $signature = 'tenants:pending-clear
{--all : Override the default settings and deletes all pending tenants}
{--older-than-days= : Deletes all pending tenants older than the amount of days}
{--older-than-hours= : Deletes all pending tenants older than the amount of hours}';
@ -18,38 +17,30 @@ class ClearPendingTenants extends Command
public function handle(): int
{
$this->info('Removing pending tenants.');
$this->components->info('Removing pending tenants.');
$expirationDate = now();
// We compare the original expiration date to the new one to check if the new one is different later
$originalExpirationDate = $expirationDate->copy()->toImmutable();
// Skip the time constraints if the 'all' option is given
if (! $this->option('all')) {
/** @var ?int $olderThanDays */
$olderThanDays = $this->option('older-than-days');
$olderThanDays = (int) $this->option('older-than-days');
$olderThanHours = (int) $this->option('older-than-hours');
/** @var ?int $olderThanHours */
$olderThanHours = $this->option('older-than-hours');
if ($olderThanDays && $olderThanHours) {
$this->components->error("Cannot use '--older-than-days' and '--older-than-hours' together. Please, choose only one of these options.");
if ($olderThanDays && $olderThanHours) {
$this->line("<options=bold,reverse;fg=red> Cannot use '--older-than-days' and '--older-than-hours' together \n"); // todo@cli refactor all of these styled command outputs to use $this->components
$this->line('Please, choose only one of these options.');
return 1; // Exit code for failure
}
if ($olderThanDays) {
$expirationDate->subDays($olderThanDays);
}
if ($olderThanHours) {
$expirationDate->subHours($olderThanHours);
}
return 1; // Exit code for failure
}
$deletedTenantCount = tenancy()
->query()
if ($olderThanDays) {
$expirationDate->subDays($olderThanDays);
}
if ($olderThanHours) {
$expirationDate->subHours($olderThanHours);
}
$deletedTenantCount = tenancy()->query()
->onlyPending()
->when($originalExpirationDate->notEqualTo($expirationDate), function (Builder $query) use ($expirationDate) {
$query->where($query->getModel()->getColumnForQuery('pending_since'), '<', $expirationDate->timestamp);
@ -59,7 +50,7 @@ class ClearPendingTenants extends Command
->delete()
->count();
$this->info($deletedTenantCount . ' pending ' . str('tenant')->plural($deletedTenantCount) . ' deleted.');
$this->components->info($deletedTenantCount . ' pending ' . str('tenant')->plural($deletedTenantCount) . ' deleted.');
return 0;
}

View file

@ -14,7 +14,7 @@ class CreatePendingTenants extends Command
public function handle(): int
{
$this->info('Creating pending tenants.');
$this->components->info('Creating pending tenants.');
$maxPendingTenantCount = (int) ($this->option('count') ?? config('tenancy.pending.count'));
$pendingTenantCount = $this->getPendingTenantCount();
@ -30,8 +30,8 @@ class CreatePendingTenants extends Command
$createdCount++;
}
$this->info($createdCount . ' ' . str('tenant')->plural($createdCount) . ' created.');
$this->info($maxPendingTenantCount . ' ' . str('tenant')->plural($maxPendingTenantCount) . ' ready to be used.');
$this->components->info($createdCount . ' pending ' . str('tenant')->plural($createdCount) . ' created.');
$this->components->info($maxPendingTenantCount . ' pending ' . str('tenant')->plural($maxPendingTenantCount) . ' ready to be used.');
return 0;
}
@ -39,8 +39,7 @@ class CreatePendingTenants extends Command
/** Calculate the number of currently available pending tenants. */
protected function getPendingTenantCount(): int
{
return tenancy()
->query()
return tenancy()->query()
->onlyPending()
->count();
}

View file

@ -34,7 +34,7 @@ class Link extends Command
$this->createLinks($tenants);
}
} catch (Exception $exception) {
$this->error($exception->getMessage());
$this->components->error($exception->getMessage());
return 1;
}

View file

@ -5,13 +5,18 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Commands;
use Illuminate\Database\Console\Migrations\FreshCommand;
use Illuminate\Support\Facades\Schema;
class MigrateFreshOverride extends FreshCommand
{
public function handle()
{
if (config('tenancy.database.drop_tenant_databases_on_migrate_fresh')) {
tenancy()->model()::cursor()->each->delete();
$tenantModel = tenancy()->model();
if (Schema::hasTable($tenantModel->getTable())) {
$tenantModel::cursor()->each->delete();
}
}
return parent::handle();

View file

@ -23,7 +23,7 @@ class TenantDump extends DumpCommand
public function handle(ConnectionResolverInterface $connections, Dispatcher $dispatcher): int
{
if (is_null($this->option('path'))) {
$this->input->setOption('path', database_path('schema/tenant-schema.dump'));
$this->input->setOption('path', config('tenancy.migration_parameters.--schema-path') ?? database_path('schema/tenant-schema.dump'));
}
$tenant = $this->option('tenant')
@ -41,7 +41,7 @@ class TenantDump extends DumpCommand
return 1;
}
parent::handle($connections, $dispatcher);
$tenant->run(fn () => parent::handle($connections, $dispatcher));
return 0;
}

View file

@ -23,8 +23,7 @@ trait HasTenantOptions
protected function getTenants(): LazyCollection
{
return tenancy()
->query()
return tenancy()->query()
->when($this->option('tenants'), function ($query) {
$query->whereIn(tenancy()->model()->getTenantKeyName(), $this->option('tenants'));
})

View file

@ -11,7 +11,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
*
* @see \Stancl\Tenancy\Database\Models\Domain
*
* @method __call(string $method, array $parameters) IDE support. This will be a model.
* @method __call(string $method, array $parameters) IDE support. This will be a model. // todo check if we can remove these now
* @method static __callStatic(string $method, array $parameters) IDE support. This will be a model.
* @mixin \Illuminate\Database\Eloquent\Model
*/

View file

@ -48,6 +48,23 @@ class UserImpersonation implements Feature
$token->delete();
session()->put('tenancy_impersonating', true);
return redirect($token->redirect_url);
}
public static function isImpersonating(): bool
{
return session()->has('tenancy_impersonating');
}
/**
* Logout from the current domain and forget impersonation session.
*/
public static function leave(): void // todo possibly rename
{
auth()->logout();
session()->forget('tenancy_impersonating');
}
}

View file

@ -6,7 +6,7 @@ namespace Stancl\Tenancy\Listeners;
use Stancl\Tenancy\Database\Contracts\TenantWithDatabase;
use Stancl\Tenancy\Database\DatabaseManager;
use Stancl\Tenancy\Events\Contracts\TenantEvent;
use Stancl\Tenancy\Events\Contracts\TenancyEvent;
class CreateTenantConnection
{
@ -15,11 +15,12 @@ class CreateTenantConnection
) {
}
public function handle(TenantEvent $event): void
public function handle(TenancyEvent $event): void
{
/** @var TenantWithDatabase */
$tenant = $event->tenant;
/** @var TenantWithDatabase $tenant */
$tenant = $event->tenancy->tenant;
$this->database->purgeTenantConnection();
$this->database->createTenantConnection($tenant);
}
}

View file

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Listeners;
use Stancl\Tenancy\Database\DatabaseManager;
use Stancl\Tenancy\Events\Contracts\TenancyEvent;
class UseCentralConnection
{
public function __construct(
protected DatabaseManager $database,
) {
}
public function handle(TenancyEvent $event): void
{
$this->database->reconnectToCentral();
}
}

View file

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Listeners;
use Stancl\Tenancy\Database\DatabaseManager;
use Stancl\Tenancy\Events\Contracts\TenancyEvent;
class UseTenantConnection
{
public function __construct(
protected DatabaseManager $database,
) {
}
public function handle(TenancyEvent $event): void
{
$this->database->setDefaultConnection('tenant');
}
}

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Resolvers;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Stancl\Tenancy\Contracts\Domain;
use Stancl\Tenancy\Contracts\Tenant;
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException;
@ -39,14 +40,16 @@ class DomainTenantResolver extends Contracts\CachedTenantResolver
protected function setCurrentDomain(Tenant $tenant, string $domain): void
{
/** @var Tenant&Model $tenant */
static::$currentDomain = $tenant->domains->where('domain', $domain)->first();
}
public function getArgsForTenant(Tenant $tenant): array
{
/** @var Tenant&Model $tenant */
$tenant->unsetRelation('domains');
return $tenant->domains->map(function (Domain $domain) {
return $tenant->domains->map(function (Domain&Model $domain) {
return [$domain->domain];
})->toArray();
}

View file

@ -118,6 +118,7 @@ class Tenancy
*/
public static function find(int|string $id): Tenant|null
{
// todo update all syntax like this once we're fully on PHP 8.2
/** @var (Tenant&Model)|null */
$tenant = static::model()->where(static::model()->getTenantKeyName(), $id)->first();