mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-12 14:14:04 +00:00
Initial commit
This commit is contained in:
commit
deb3ad77f5
19 changed files with 1283 additions and 0 deletions
19
src/CacheManager.php
Normal file
19
src/CacheManager.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy;
|
||||
|
||||
use Illuminate\Cache\CacheManager as BaseCacheManager;
|
||||
|
||||
class CacheManager extends BaseCacheManager
|
||||
{
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
$tags = [config('tenancy.cache.prefix_base') . tenant('uuid')];
|
||||
|
||||
if ($method === "tags") {
|
||||
return $this->store()->tags(array_merge($tags, ...$parameters));
|
||||
}
|
||||
|
||||
return $this->store()->tags($tags)->$method(...$parameters);
|
||||
}
|
||||
}
|
||||
69
src/Commands/Migrate.php
Normal file
69
src/Commands/Migrate.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Stancl\Tenancy\DatabaseManager;
|
||||
use Illuminate\Database\Migrations\Migrator;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Illuminate\Database\Console\Migrations\MigrateCommand;
|
||||
|
||||
class Migrate extends MigrateCommand
|
||||
{
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Run migrations for tenant(s)';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Migrator $migrator, DatabaseManager $database)
|
||||
{
|
||||
parent::__construct($migrator);
|
||||
$this->database = $database;
|
||||
|
||||
$this->setName('tenants:migrate');
|
||||
$this->specifyParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (! $this->confirmToProceed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->input->setOption('database', 'tenant');
|
||||
|
||||
tenant()->all($this->option('tenants'))->each(function ($tenant) {
|
||||
$this->line("Tenant: {$tenant['uuid']} ({$tenant['domain']})");
|
||||
$this->database->connectToTenant($tenant);
|
||||
|
||||
// Migrate
|
||||
parent::handle();
|
||||
});
|
||||
}
|
||||
|
||||
protected function getOptions()
|
||||
{
|
||||
return array_merge([
|
||||
['tenants', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, '', null]
|
||||
], parent::getOptions());
|
||||
}
|
||||
|
||||
protected function getMigrationPaths()
|
||||
{
|
||||
return [database_path('migrations/tenant')];
|
||||
}
|
||||
}
|
||||
69
src/Commands/Rollback.php
Normal file
69
src/Commands/Rollback.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Stancl\Tenancy\DatabaseManager;
|
||||
use Illuminate\Database\Migrations\Migrator;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Illuminate\Database\Console\Migrations\RollbackCommand;
|
||||
|
||||
class Rollback extends RollbackCommand
|
||||
{
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Rollback migrations for tenant(s).';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Migrator $migrator, DatabaseManager $database)
|
||||
{
|
||||
parent::__construct($migrator);
|
||||
$this->database = $database;
|
||||
|
||||
$this->setName('tenants:rollback');
|
||||
$this->specifyParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (! $this->confirmToProceed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->input->setOption('database', 'tenant');
|
||||
|
||||
tenant()->all($this->option('tenants'))->each(function ($tenant) {
|
||||
$this->line("Tenant: {$tenant['uuid']} ({$tenant['domain']})");
|
||||
$this->database->connectToTenant($tenant);
|
||||
|
||||
// Migrate
|
||||
parent::handle();
|
||||
});
|
||||
}
|
||||
|
||||
protected function getOptions()
|
||||
{
|
||||
return array_merge([
|
||||
['tenants', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, '', null]
|
||||
], parent::getOptions());
|
||||
}
|
||||
|
||||
protected function getMigrationPaths()
|
||||
{
|
||||
return [database_path('migrations/tenant')];
|
||||
}
|
||||
}
|
||||
70
src/Commands/Seed.php
Normal file
70
src/Commands/Seed.php
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Stancl\Tenancy\DatabaseManager;
|
||||
use Illuminate\Database\Migrations\Migrator;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Illuminate\Database\Console\Seeds\SeedCommand;
|
||||
use Illuminate\Database\ConnectionResolverInterface;
|
||||
|
||||
class Seed extends SeedCommand
|
||||
{
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Seed tenant database(s).';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(ConnectionResolverInterface $resolver, DatabaseManager $database)
|
||||
{
|
||||
parent::__construct($resolver);
|
||||
$this->database = $database;
|
||||
|
||||
$this->setName('tenants:seed');
|
||||
$this->specifyParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (! $this->confirmToProceed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->input->setOption('database', 'tenant');
|
||||
|
||||
tenant()->all($this->option('tenants'))->each(function ($tenant) {
|
||||
$this->line("Tenant: {$tenant['uuid']} ({$tenant['domain']})");
|
||||
$this->database->connectToTenant($tenant);
|
||||
|
||||
// Migrate
|
||||
parent::handle();
|
||||
});
|
||||
}
|
||||
|
||||
protected function getOptions()
|
||||
{
|
||||
return array_merge([
|
||||
['tenants', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, '', null]
|
||||
], parent::getOptions());
|
||||
}
|
||||
|
||||
protected function getMigrationPaths()
|
||||
{
|
||||
return [database_path('migrations/tenant')];
|
||||
}
|
||||
}
|
||||
45
src/Commands/TenantList.php
Normal file
45
src/Commands/TenantList.php
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class TenantList extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'tenants:list';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'List tenants.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info("Listing all tenants.");
|
||||
tenancy()->all()->each(function ($tenant) {
|
||||
$this->line("[Tenant] uuid: {$tenant['uuid']} @ {$tenant['domain']}");
|
||||
});
|
||||
}
|
||||
}
|
||||
64
src/DatabaseManager.php
Normal file
64
src/DatabaseManager.php
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Database\DatabaseManager as BaseDatabaseManager;
|
||||
|
||||
class DatabaseManager
|
||||
{
|
||||
public function __construct(BaseDatabaseManager $database)
|
||||
{
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
public function connect(string $database)
|
||||
{
|
||||
$this->createTenantConnection($database);
|
||||
$this->database->setDefaultConnection('tenant');
|
||||
$this->database->reconnect('tenant');
|
||||
}
|
||||
|
||||
public function connectToTenant($tenant)
|
||||
{
|
||||
$this->connect(tenant()->getDatabaseName($tenant));
|
||||
}
|
||||
|
||||
public function disconnect()
|
||||
{
|
||||
$this->database->reconnect('default');
|
||||
$this->database->setDefaultConnection('default');
|
||||
}
|
||||
|
||||
public function create(string $name, string $driver = null)
|
||||
{
|
||||
$this->createTenantConnection($name);
|
||||
$driver = $driver ?: $this->getDriver();
|
||||
if ($driver === "sqlite") {
|
||||
$f = fopen(database_path($name), 'w');
|
||||
fclose($f);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return DB::statement("CREATE DATABASE `$name`");
|
||||
}
|
||||
|
||||
public function getDriver(): ?string
|
||||
{
|
||||
return config("database.connections.tenant.driver");
|
||||
}
|
||||
|
||||
public function createTenantConnection(string $database_name)
|
||||
{
|
||||
// Create the `tenancy` database connection.
|
||||
$based_on = config('tenancy.database.based_on') ?: config('database.default');
|
||||
config()->set([
|
||||
'database.connections.tenant' => config('database.connections.' . $based_on)
|
||||
]);
|
||||
|
||||
// Change DB name
|
||||
$database_name = $this->getDriver() === "sqlite" ? database_path($database_name) : $database_name;
|
||||
config()->set(['database.connections.tenant.database' => $database_name]);
|
||||
}
|
||||
}
|
||||
15
src/Interfaces/StorageDriver.php
Normal file
15
src/Interfaces/StorageDriver.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy\Interfaces;
|
||||
|
||||
interface StorageDriver
|
||||
{
|
||||
public function identifyTenant(string $domain): array;
|
||||
public function getAllTenants(array $uuids = []): array;
|
||||
public function getTenantById(string $uuid, $fields = []): array;
|
||||
public function getTenantIdByDomain(string $domain): ?string;
|
||||
public function createTenant(string $domain, string $uuid): array;
|
||||
public function deleteTenant(string $uuid): bool;
|
||||
public function get(string $uuid, string $key);
|
||||
public function put(string $uuid, string $key, $value);
|
||||
}
|
||||
37
src/Middleware/InitializeTenancy.php
Normal file
37
src/Middleware/InitializeTenancy.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy\Middleware;
|
||||
|
||||
use Closure;
|
||||
|
||||
class InitializeTenancy
|
||||
{
|
||||
public function __construct(Closure $onFail = null)
|
||||
{
|
||||
$this->onFail = $onFail ?: function ($e) { throw $e; };
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
try {
|
||||
tenancy()->init();
|
||||
} catch (\Exception $e) {
|
||||
// Pass the exception to the onFail function if it takes any parameters.
|
||||
$callback = $this->onFail;
|
||||
if ((new \ReflectionFunction($callback))->getNumberOfParameters() > 0) {
|
||||
$callback($e);
|
||||
} else {
|
||||
$callback();
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
84
src/StorageDrivers/RedisStorageDriver.php
Normal file
84
src/StorageDrivers/RedisStorageDriver.php
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy\StorageDrivers;
|
||||
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Stancl\Tenancy\Interfaces\StorageDriver;
|
||||
|
||||
class RedisStorageDriver implements StorageDriver
|
||||
{
|
||||
private $redis;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->redis = Redis::connection('tenancy');
|
||||
}
|
||||
|
||||
public function identifyTenant(string $domain): array
|
||||
{
|
||||
$id = $this->getTenantIdByDomain($domain);
|
||||
if (! $id) {
|
||||
throw new \Exception("Tenant could not be identified on domain {$domain}");
|
||||
}
|
||||
return array_merge(['uuid' => $id], $this->getTenantById($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about the tenant based on his uuid.
|
||||
*
|
||||
* @param string $uuid
|
||||
* @param array|string $fields
|
||||
* @return array
|
||||
*/
|
||||
public function getTenantById(string $uuid, $fields = []): array
|
||||
{
|
||||
if (! $fields) {
|
||||
return $this->redis->hgetall("tenants:$uuid");
|
||||
}
|
||||
|
||||
return array_combine($fields, $this->redis->hmget("tenants:$uuid", $fields));
|
||||
}
|
||||
|
||||
public function getTenantIdByDomain(string $domain): ?string
|
||||
{
|
||||
return $this->redis->hget("domains:$domain", 'tenant_id') ?: null;
|
||||
}
|
||||
|
||||
public function createTenant(string $domain, string $uuid): array
|
||||
{
|
||||
$this->redis->hmset("domains:$domain", 'tenant_id', $uuid);
|
||||
$this->redis->hmset("tenants:$uuid", 'uuid', $uuid, 'domain', $domain);
|
||||
return $this->redis->hgetall("tenants:$uuid");
|
||||
}
|
||||
|
||||
public function deleteTenant(string $id): bool
|
||||
{
|
||||
$domain = $this->getTenantById($id)['domain'];
|
||||
$this->redis->del("domains:$domain");
|
||||
return (bool) $this->redis->del("tenants:$id");
|
||||
}
|
||||
|
||||
public function getAllTenants(array $uuids = []): array
|
||||
{
|
||||
$hashes = array_map(function ($hash) {
|
||||
return "tenants:{$hash}";
|
||||
}, $uuids);
|
||||
|
||||
$hashes = $hashes ?: $this->redis->scan(null, 'tenants:*');
|
||||
|
||||
return array_map(function ($tenant) {
|
||||
return $this->redis->hgetall($tenant);
|
||||
}, $hashes);
|
||||
}
|
||||
|
||||
public function get(string $uuid, string $key)
|
||||
{
|
||||
return $this->redis->hget("tenants:$uuid", $key);
|
||||
}
|
||||
|
||||
public function put(string $uuid, string $key, $value)
|
||||
{
|
||||
$this->redis->hset("tenants:$uuid", $key, $value);
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
63
src/TenancyServiceProvider.php
Normal file
63
src/TenancyServiceProvider.php
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy;
|
||||
|
||||
use Stancl\Tenancy\Commands\Seed;
|
||||
use Stancl\Tenancy\TenantManager;
|
||||
use Stancl\Tenancy\DatabaseManager;
|
||||
use Stancl\Tenancy\Commands\Migrate;
|
||||
use Stancl\Tenancy\Commands\Rollback;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Stancl\Tenancy\Commands\TenantList;
|
||||
use Stancl\Tenancy\Interfaces\StorageDriver;
|
||||
use Stancl\Tenancy\StorageDrivers\RedisStorageDriver;
|
||||
|
||||
class TenancyServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
if ($this->app->runningInConsole()) {
|
||||
$this->commands([
|
||||
Migrate::class,
|
||||
Rollback::class,
|
||||
Seed::class,
|
||||
TenantList::class,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->publishes([
|
||||
__DIR__ . '/config/tenancy.php' => config_path('tenancy.php'),
|
||||
], 'config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->mergeConfigFrom(__DIR__ . '/config/tenancy.php', 'tenancy');
|
||||
|
||||
$this->app->bind(StorageDriver::class, $this->app['config']['tenancy.storage_driver']);
|
||||
$this->app->singleton(DatabaseManager::class);
|
||||
$this->app->singleton(TenantManager::class, function ($app) {
|
||||
return new TenantManager($app, $app[StorageDriver::class], $app[DatabaseManager::class]);
|
||||
});
|
||||
|
||||
$this->app->singleton(Migrate::class, function ($app) {
|
||||
return new Migrate($app['migrator'], $app[DatabaseManager::class]);
|
||||
});
|
||||
$this->app->singleton(Rollback::class, function ($app) {
|
||||
return new Rollback($app['migrator'], $app[DatabaseManager::class]);
|
||||
});
|
||||
$this->app->singleton(Seed::class, function ($app) {
|
||||
return new Seed($app['db'], $app[DatabaseManager::class]);
|
||||
});
|
||||
}
|
||||
}
|
||||
30
src/Tenant.php
Normal file
30
src/Tenant.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy;
|
||||
|
||||
class Tenant
|
||||
{
|
||||
public $uuid;
|
||||
public $domain;
|
||||
public $databaseName;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array|string $data
|
||||
*/
|
||||
public function __construct($data)
|
||||
{
|
||||
$data = is_string($data) ? json_decode($data, true) : (array) $data;
|
||||
|
||||
$this->uuid = $data['uuid'];
|
||||
$this->domain = $data['domain'] ?? tenancy()->getTenantById($data['uuid'], 'domain');
|
||||
$this->databaseName = $data['database_name'] ?? $this->getDatabaseName($data);
|
||||
}
|
||||
|
||||
public function getDatabaseName($uuid = null)
|
||||
{
|
||||
$uuid = $uuid ?: $this->uuid;
|
||||
return config('tenancy.database._prefix_base') . $uuid . config('tenancy.database._suffix');
|
||||
}
|
||||
}
|
||||
226
src/TenantManager.php
Normal file
226
src/TenantManager.php
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy;
|
||||
|
||||
use Stancl\Tenancy\BootstrapsTenancy;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Stancl\Tenancy\Interfaces\StorageDriver;
|
||||
|
||||
class TenantManager
|
||||
{
|
||||
use BootstrapsTenancy;
|
||||
|
||||
/**
|
||||
* The application instance.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Foundation\Application|\Illuminate\Foundation\Application
|
||||
*/
|
||||
private $app;
|
||||
|
||||
/**
|
||||
* Storage driver for tenant metadata.
|
||||
*
|
||||
* @var StorageDriver
|
||||
*/
|
||||
public $storage;
|
||||
|
||||
/**
|
||||
* Database manager.
|
||||
*
|
||||
* @var DatabaseManager
|
||||
*/
|
||||
public $database;
|
||||
|
||||
/**
|
||||
* Current tenant.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tenant;
|
||||
|
||||
public function __construct($app, StorageDriver $storage, DatabaseManager $database)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->storage = $storage;
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
public function init(string $domain = null): array
|
||||
{
|
||||
$this->setTenant($this->identify($domain));
|
||||
$this->bootstrap();
|
||||
return $this->tenant;
|
||||
}
|
||||
|
||||
public function identify(string $domain = null): array
|
||||
{
|
||||
$domain = $domain ?: $this->currentDomain();
|
||||
|
||||
if (! $domain) {
|
||||
throw new \Exception("No domain supplied nor detected.");
|
||||
}
|
||||
|
||||
$tenant = $this->storage->identifyTenant($domain);
|
||||
|
||||
if (! $tenant || ! array_key_exists('uuid', $tenant) || ! $tenant['uuid']) {
|
||||
throw new \Exception("Tenant could not be identified on domain {$domain}.");
|
||||
}
|
||||
|
||||
return $tenant;
|
||||
}
|
||||
|
||||
public function create(string $domain = null): array
|
||||
{
|
||||
$domain = $domain ?: $this->currentDomain();
|
||||
|
||||
if ($id = $this->storage->getTenantIdByDomain($domain)) {
|
||||
throw new \Exception("Domain $domain is already occupied by tenant $id.");
|
||||
}
|
||||
|
||||
$tenant = $this->storage->createTenant($domain, \Uuid::generate(1, $domain));
|
||||
$this->database->create($this->getDatabaseName($tenant));
|
||||
\Artisan::call('tenants:migrate', [
|
||||
'--tenants' => [$tenant['uuid']]
|
||||
]);
|
||||
|
||||
return $tenant;
|
||||
}
|
||||
|
||||
public function delete(string $uuid): bool
|
||||
{
|
||||
return $this->storage->deleteTenant($uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array with information about a tenant based on his uuid.
|
||||
*
|
||||
* @param string $uuid
|
||||
* @param array|string $fields
|
||||
* @return array
|
||||
*/
|
||||
public function getTenantById(string $uuid, $fields = [])
|
||||
{
|
||||
$fields = (array) $fields;
|
||||
return $this->storage->getTenantById($uuid, $fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for getTenantById().
|
||||
*
|
||||
* @param string $uuid
|
||||
* @param array|string $fields
|
||||
* @return array
|
||||
*/
|
||||
public function find(string $uuid, $fields = [])
|
||||
{
|
||||
return $this->getTenantById($uuid, $fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tenant uuid based on the domain that belongs to him.
|
||||
*
|
||||
* @param string $domain
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTenantIdByDomain(string $domain = null): ?string
|
||||
{
|
||||
$domain = $domain ?: $this->currentDomain();
|
||||
|
||||
return $this->storage->getTenantIdByDomain($domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for getTenantIdByDomain().
|
||||
*
|
||||
* @param string $domain
|
||||
* @return string|null
|
||||
*/
|
||||
public function getIdByDomain(string $domain = null)
|
||||
{
|
||||
return $this->getTenantIdByDomain($domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tenant information based on his domain.
|
||||
*
|
||||
* @param string $domain
|
||||
* @param mixed $fields
|
||||
* @return array
|
||||
*/
|
||||
public function findByDomain(string $domain = null, $fields = [])
|
||||
{
|
||||
$domain = $domain ?: $this->currentDomain();
|
||||
|
||||
return $this->find($this->getIdByDomain($domain), $fields);
|
||||
}
|
||||
|
||||
public static function currentDomain(): ?string
|
||||
{
|
||||
return request()->getHost() ?? null;
|
||||
}
|
||||
|
||||
public function getDatabaseName($tenant = []): string
|
||||
{
|
||||
$tenant = $tenant ?: $this->tenant;
|
||||
return config('tenancy.database.prefix') . $tenant['uuid'] . config('tenancy.database.suffix');
|
||||
}
|
||||
|
||||
public function setTenant(array $tenant): array
|
||||
{
|
||||
$this->tenant = $tenant;
|
||||
|
||||
return $tenant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tenants.
|
||||
*
|
||||
* @param array|string $uuids
|
||||
* @return array
|
||||
*/
|
||||
public function all($uuids = [])
|
||||
{
|
||||
$uuid = (array) $uuids;
|
||||
return collect($this->storage->getAllTenants($uuids));
|
||||
}
|
||||
|
||||
public function actAsId(string $uuid): array
|
||||
{
|
||||
return $this->setTenant($this->storage->getTenantById($uuid));
|
||||
}
|
||||
|
||||
public function actAsDomain(string $domain): string
|
||||
{
|
||||
return $this->init($domain);
|
||||
}
|
||||
|
||||
public function get(string $key)
|
||||
{
|
||||
return $this->storage->get($this->tenant['uuid'], $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a value into the storage for the current tenant.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function put(string $key, $value)
|
||||
{
|
||||
// Todo allow $value to be null and $key to be an array.
|
||||
return $this->tenant[$key] = $this->storage->put($this->tenant['uuid'], $key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for put().
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function set(string $key, $value)
|
||||
{
|
||||
return $this->put($this->put($key, $value));
|
||||
}
|
||||
}
|
||||
46
src/Traits/BootstrapsTenancy.php
Normal file
46
src/Traits/BootstrapsTenancy.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace Stancl\Tenancy;
|
||||
|
||||
trait BootstrapsTenancy
|
||||
{
|
||||
public function bootstrap()
|
||||
{
|
||||
$this->switchDatabaseConnection();
|
||||
$this->setPhpRedisPrefix($this->app['config']['tenancy.redis.prefixed_connections']);
|
||||
$this->tagCache();
|
||||
$this->suffixFilesystemRootPaths();
|
||||
}
|
||||
|
||||
public function switchDatabaseConnection()
|
||||
{
|
||||
$this->database->connect($this->getDatabaseName());
|
||||
}
|
||||
|
||||
public function setPhpRedisPrefix($connections = ['default'])
|
||||
{
|
||||
return;
|
||||
foreach ($connections as $connection) {
|
||||
$prefix = config('tenancy.redis.prefix_base') . $this->tenant['uuid'];
|
||||
$client = Redis::connection($connection)->client();
|
||||
$client->setOption($client::OPT_PREFIX, $prefix);
|
||||
}
|
||||
}
|
||||
|
||||
public function tagCache()
|
||||
{
|
||||
$this->app->extend('cache', function () {
|
||||
return new CacheManager($this->app);
|
||||
});
|
||||
}
|
||||
|
||||
public function suffixFilesystemRootPaths()
|
||||
{
|
||||
$suffix = $this->app['config']['tenancy.filesystem.suffix_base'] . tenant('uuid');
|
||||
foreach ($this->app['config']['tenancy.filesystem.disks'] as $disk) {
|
||||
\Storage::disk($disk)->getAdapter()->setPathPrefix(
|
||||
$this->app['config']["filesystems.disks.{$disk}.root"] . "/{$suffix}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/config/tenancy.php
Normal file
28
src/config/tenancy.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'storage_driver' => 'Stancl\Tenancy\StorageDrivers\RedisStorageDriver',
|
||||
'database' => [
|
||||
'based_on' => 'sqlite',
|
||||
'prefix' => 'tenant',
|
||||
'suffix' => '.sqlite',
|
||||
],
|
||||
'redis' => [
|
||||
'prefix_base' => 'tenant',
|
||||
'prefixed_connections' => [
|
||||
'default',
|
||||
],
|
||||
],
|
||||
'cache' => [
|
||||
'prefix_base' => 'tenant',
|
||||
],
|
||||
|
||||
'filesystem' => [
|
||||
'suffix_base' => 'tenant',
|
||||
// Disks which should be suffixed with the suffix_base + tenant UUID.
|
||||
'disks' => [
|
||||
// 'local',
|
||||
// 's3',
|
||||
],
|
||||
],
|
||||
];
|
||||
21
src/helpers.php
Normal file
21
src/helpers.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
use Stancl\Tenancy\TenantManager;
|
||||
|
||||
if (! function_exists('tenancy')) {
|
||||
function tenancy($key = null)
|
||||
{
|
||||
if ($key) {
|
||||
return app(TenantManager::class)->tenant[$key];
|
||||
}
|
||||
|
||||
return app(TenantManager::class);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('tenant')) {
|
||||
function tenant($key = null)
|
||||
{
|
||||
return tenancy($key);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue