mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-13 04:24:03 +00:00
Storage drivers WIP
This commit is contained in:
parent
7eba041509
commit
69db512d30
6 changed files with 128 additions and 49 deletions
|
|
@ -7,6 +7,7 @@ namespace Stancl\Tenancy\Contracts;
|
||||||
use Stancl\Tenancy\TenantManager;
|
use Stancl\Tenancy\TenantManager;
|
||||||
|
|
||||||
/** Additional features, like Telescope tags and tenant redirects. */
|
/** Additional features, like Telescope tags and tenant redirects. */
|
||||||
|
// todo interface name. Feature, FeatureProvider, ProvidesFeature(s)
|
||||||
interface FeatureProvider
|
interface FeatureProvider
|
||||||
{
|
{
|
||||||
public function bootstrap(TenantManager $tenantManager): void;
|
public function bootstrap(TenantManager $tenantManager): void;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||||
namespace Stancl\Tenancy\Contracts;
|
namespace Stancl\Tenancy\Contracts;
|
||||||
|
|
||||||
use Stancl\Tenancy\Tenant;
|
use Stancl\Tenancy\Tenant;
|
||||||
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedException;
|
|
||||||
|
|
||||||
// todo this class now manages types (json encoding)
|
// todo this class now manages types (json encoding)
|
||||||
// make sure ids are not json encoded
|
// make sure ids are not json encoded
|
||||||
|
|
@ -23,7 +22,7 @@ interface StorageDriver
|
||||||
*
|
*
|
||||||
* @param string $id
|
* @param string $id
|
||||||
* @return Tenant
|
* @return Tenant
|
||||||
* @throws TenantCouldNotBeIdentifiedException
|
* @throws \Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedException
|
||||||
*/
|
*/
|
||||||
public function findById(string $id): Tenant;
|
public function findById(string $id): Tenant;
|
||||||
|
|
||||||
|
|
@ -41,5 +40,42 @@ interface StorageDriver
|
||||||
* @param Tenant $tenant
|
* @param Tenant $tenant
|
||||||
* @return true|TenantCannotBeCreatedException
|
* @return true|TenantCannotBeCreatedException
|
||||||
*/
|
*/
|
||||||
public function canCreate(Tenant $tenant);
|
public function canCreateTenant(Tenant $tenant);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a value from storage.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param ?Tenant $tenant
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get(string $key, Tenant $tenant = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get multiple values from storage.
|
||||||
|
*
|
||||||
|
* @param array $keys
|
||||||
|
* @param ?Tenant $tenant
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function getMany(array $keys, Tenant $tenant = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put a value into storage.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param mixed $value
|
||||||
|
* @param ?Tenant $tenant
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function put(string $key, $value, Tenant $tenant = null): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put multiple values into storage.
|
||||||
|
*
|
||||||
|
* @param mixed[string] $kvPairs
|
||||||
|
* @param ?Tenant $tenant
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function putMany(array $kvPairs, Tenant $tenant = null): void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,43 +4,60 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Stancl\Tenancy\StorageDrivers;
|
namespace Stancl\Tenancy\StorageDrivers;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Redis;
|
use Illuminate\Foundation\Application;
|
||||||
use Stancl\Tenancy\Interfaces\StorageDriver;
|
use Stancl\Tenancy\Interfaces\StorageDriver;
|
||||||
|
use Illuminate\Contracts\Redis\Factory as Redis;
|
||||||
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedException;
|
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedException;
|
||||||
|
use Stancl\Tenancy\Tenant;
|
||||||
|
|
||||||
class RedisStorageDriver implements StorageDriver
|
class RedisStorageDriver implements StorageDriver
|
||||||
{
|
{
|
||||||
private $redis;
|
/** @var Application */
|
||||||
|
protected $app;
|
||||||
|
|
||||||
public function __construct()
|
/** @var Redis */
|
||||||
|
protected $redis;
|
||||||
|
|
||||||
|
public function __construct(Application $app, Redis $redis)
|
||||||
{
|
{
|
||||||
$this->redis = Redis::connection(config('tenancy.redis.connection', 'tenancy'));
|
$this->app = $app;
|
||||||
|
$this->redis = $redis->connection($app['config']['tenancy.redis.connection'] ?? 'tenancy');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function identifyTenant(string $domain): array
|
/**
|
||||||
|
* Get the current tenant.
|
||||||
|
*
|
||||||
|
* @return Tenant
|
||||||
|
*/
|
||||||
|
protected function tenant()
|
||||||
|
{
|
||||||
|
return $this->app[Tenant::class];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByDomain(string $domain): Tenant
|
||||||
{
|
{
|
||||||
$id = $this->getTenantIdByDomain($domain);
|
$id = $this->getTenantIdByDomain($domain);
|
||||||
if (! $id) {
|
if (! $id) {
|
||||||
throw new TenantCouldNotBeIdentifiedException($domain);
|
throw new TenantCouldNotBeIdentifiedException($domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->getTenantById($id);
|
return $this->find($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get information about the tenant based on his uuid.
|
* Get information about the tenant based on his id.
|
||||||
*
|
*
|
||||||
* @param string $uuid
|
* @param string $id
|
||||||
* @param array $fields
|
* @param string[] $fields
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getTenantById(string $uuid, array $fields = []): array
|
public function find(string $id, array $fields = []): array
|
||||||
{
|
{
|
||||||
if (! $fields) {
|
if (! $fields) {
|
||||||
return $this->redis->hgetall("tenants:$uuid");
|
return $this->redis->hgetall("tenants:$id");
|
||||||
}
|
}
|
||||||
|
|
||||||
return \array_combine($fields, $this->redis->hmget("tenants:$uuid", $fields));
|
return array_combine($fields, $this->redis->hmget("tenants:$id", $fields));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTenantIdByDomain(string $domain): ?string
|
public function getTenantIdByDomain(string $domain): ?string
|
||||||
|
|
@ -48,37 +65,31 @@ class RedisStorageDriver implements StorageDriver
|
||||||
return $this->redis->hget("domains:$domain", 'tenant_id') ?: null;
|
return $this->redis->hget("domains:$domain", 'tenant_id') ?: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createTenant(string $domain, string $uuid): array
|
public function createTenant(Tenant $tenant): void
|
||||||
{
|
{
|
||||||
$this->redis->hmset("domains:$domain", 'tenant_id', $uuid);
|
$id = $tenant->id;
|
||||||
$this->redis->hmset("tenants:$uuid", 'uuid', \json_encode($uuid), 'domain', \json_encode($domain));
|
|
||||||
|
|
||||||
return $this->redis->hgetall("tenants:$uuid");
|
foreach ($tenant->domains as $domain) {
|
||||||
|
$this->redis->hmset("domains:$domain", 'tenant_id', $id);
|
||||||
|
}
|
||||||
|
$this->redis->hmset("tenants:$id", 'id', json_encode($id), 'domain', json_encode($domain));
|
||||||
|
|
||||||
|
return $this->redis->hgetall("tenants:$id"); // todo
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @todo Make tenant & domain deletion atomic. */
|
||||||
* {@inheritdoc}
|
public function deleteTenant(Tenant $tenant): void
|
||||||
*
|
|
||||||
* @param string $id
|
|
||||||
* @return bool
|
|
||||||
* @todo Make tenant & domain deletion atomic.
|
|
||||||
*/
|
|
||||||
public function deleteTenant(string $id): bool
|
|
||||||
{
|
{
|
||||||
try {
|
foreach ($tenant->domains as $domain) {
|
||||||
$domain = \json_decode($this->getTenantById($id)['domain']);
|
$this->redis->del("domains:$domain");
|
||||||
} catch (\Throwable $th) {
|
|
||||||
throw new \Exception("No tenant with UUID $id exists.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->redis->del("domains:$domain");
|
$this->redis->del("tenants:{$tenant->id}");
|
||||||
|
|
||||||
return (bool) $this->redis->del("tenants:$id");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAllTenants(array $uuids = []): array
|
public function getAllTenants(array $uuids = []): array
|
||||||
{
|
{
|
||||||
$hashes = \array_map(function ($hash) {
|
$hashes = array_map(function ($hash) {
|
||||||
return "tenants:{$hash}";
|
return "tenants:{$hash}";
|
||||||
}, $uuids);
|
}, $uuids);
|
||||||
|
|
||||||
|
|
@ -93,20 +104,20 @@ class RedisStorageDriver implements StorageDriver
|
||||||
|
|
||||||
$all_keys = $this->redis->keys('tenants:*');
|
$all_keys = $this->redis->keys('tenants:*');
|
||||||
|
|
||||||
$hashes = \array_map(function ($key) use ($redis_prefix) {
|
$hashes = array_map(function ($key) use ($redis_prefix) {
|
||||||
// Left strip $redis_prefix from $key
|
// Left strip $redis_prefix from $key
|
||||||
return \substr($key, \strlen($redis_prefix));
|
return substr($key, strlen($redis_prefix));
|
||||||
}, $all_keys);
|
}, $all_keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
return \array_map(function ($tenant) {
|
return array_map(function ($tenant) {
|
||||||
return $this->redis->hgetall($tenant);
|
return $this->redis->hgetall($tenant);
|
||||||
}, $hashes);
|
}, $hashes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get(string $uuid, string $key)
|
public function get(string $uuid, string $key)
|
||||||
{
|
{
|
||||||
return $this->redis->hget("tenants:$uuid", $key);
|
return json_decode($this->redis->hget("tenants:$uuid", $key), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMany(string $uuid, array $keys): array
|
public function getMany(string $uuid, array $keys): array
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||||
namespace Stancl\Tenancy;
|
namespace Stancl\Tenancy;
|
||||||
|
|
||||||
use ArrayAccess;
|
use ArrayAccess;
|
||||||
|
use Stancl\Tenancy\Contracts\StorageDriver;
|
||||||
|
|
||||||
// todo tenant storage
|
// todo tenant storage
|
||||||
|
|
||||||
|
|
@ -27,23 +28,25 @@ class Tenant implements ArrayAccess
|
||||||
*
|
*
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
public $domains;
|
public $domains = [];
|
||||||
|
|
||||||
/**
|
/** @var TenantManager */
|
||||||
* @var TenantManager
|
protected $manager;
|
||||||
*/
|
|
||||||
private $manager;
|
/** @var StorageDriver */
|
||||||
|
protected $storage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does this tenant exist in the storage.
|
* Does this tenant exist in the storage.
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $persisted = false;
|
protected $persisted = false;
|
||||||
|
|
||||||
public function __construct(TenantManager $tenantManager)
|
public function __construct(TenantManager $tenantManager, StorageDriver $storage)
|
||||||
{
|
{
|
||||||
$this->manager = $tenantManager;
|
$this->manager = $tenantManager;
|
||||||
|
$this->storage = $storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function new(): self
|
public static function new(): self
|
||||||
|
|
@ -65,6 +68,7 @@ class Tenant implements ArrayAccess
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo move this to TenantFactory?
|
||||||
public function withDomains($domains): self
|
public function withDomains($domains): self
|
||||||
{
|
{
|
||||||
$domains = (array) $domains;
|
$domains = (array) $domains;
|
||||||
|
|
@ -128,6 +132,9 @@ class Tenant implements ArrayAccess
|
||||||
|
|
||||||
public function put($key, $value = null): self
|
public function put($key, $value = null): self
|
||||||
{
|
{
|
||||||
|
// todo something like if ($this->storage->getIdKey() === $key) throw new Exception("Can't override ID")? or should it be overridable?
|
||||||
|
// and the responsibility of not overriding domains is up to the storage driver
|
||||||
|
|
||||||
if (is_array($key)) {
|
if (is_array($key)) {
|
||||||
$this->storage->putMany($key);
|
$this->storage->putMany($key);
|
||||||
foreach ($key as $k => $v) { // Add to cache
|
foreach ($key as $k => $v) { // Add to cache
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ class TenantManagerv2
|
||||||
*/
|
*/
|
||||||
public function ensureTenantCanBeCreated(Tenant $tenant): void
|
public function ensureTenantCanBeCreated(Tenant $tenant): void
|
||||||
{
|
{
|
||||||
|
// todo move the "throw" responsibility to the canCreateTenant methods?
|
||||||
if (($e = $this->storage->canCreateTenant($tenant)) instanceof TenantCannotBeCreatedException) {
|
if (($e = $this->storage->canCreateTenant($tenant)) instanceof TenantCannotBeCreatedException) {
|
||||||
throw new $e;
|
throw new $e;
|
||||||
}
|
}
|
||||||
|
|
@ -138,6 +139,21 @@ class TenantManagerv2
|
||||||
return $this->storage->findByDomain($domain);
|
return $this->storage->findByDomain($domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all tenants.
|
||||||
|
*
|
||||||
|
* @param Tenant[]|string[] $only
|
||||||
|
* @return Tenant[]
|
||||||
|
*/
|
||||||
|
public function all($only = []): array
|
||||||
|
{
|
||||||
|
$only = array_map(function ($item) {
|
||||||
|
return $item instanceof Tenant ? $item->id : $item;
|
||||||
|
}, $only);
|
||||||
|
|
||||||
|
return $this->storage->all($only);
|
||||||
|
}
|
||||||
|
|
||||||
public function initializeTenancy(Tenant $tenant): self
|
public function initializeTenancy(Tenant $tenant): self
|
||||||
{
|
{
|
||||||
$this->bootstrapTenancy($tenant);
|
$this->bootstrapTenancy($tenant);
|
||||||
|
|
@ -175,15 +191,20 @@ class TenantManagerv2
|
||||||
/**
|
/**
|
||||||
* Get the current tenant.
|
* Get the current tenant.
|
||||||
*
|
*
|
||||||
|
* @param string $key
|
||||||
* @return Tenant
|
* @return Tenant
|
||||||
* @throws NoTenantIdentifiedException
|
* @throws NoTenantIdentifiedException
|
||||||
*/
|
*/
|
||||||
public function getTenant(): Tenant
|
public function getTenant(string $key = null): Tenant
|
||||||
{
|
{
|
||||||
if (! $this->tenant) {
|
if (! $this->tenant) {
|
||||||
throw new NoTenantIdentifiedException;
|
throw new NoTenantIdentifiedException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! is_null($key)) {
|
||||||
|
return $this->tenant[$key];
|
||||||
|
}
|
||||||
|
|
||||||
return $this->tenant;
|
return $this->tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Stancl\Tenancy\Tenant;
|
||||||
use Stancl\Tenancy\TenantManager;
|
use Stancl\Tenancy\TenantManager;
|
||||||
|
|
||||||
if (! \function_exists('tenancy')) {
|
if (! \function_exists('tenancy')) {
|
||||||
function tenancy($key = null)
|
function tenancy($key = null)
|
||||||
{
|
{
|
||||||
if ($key) {
|
if ($key) {
|
||||||
return app(TenantManager::class)->tenant[$key] ?? null;
|
return app(TenantManager::class)->getTenant($key) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return app(TenantManager::class);
|
return app(TenantManager::class);
|
||||||
|
|
@ -18,7 +19,9 @@ if (! \function_exists('tenancy')) {
|
||||||
if (! \function_exists('tenant')) {
|
if (! \function_exists('tenant')) {
|
||||||
function tenant($key = null)
|
function tenant($key = null)
|
||||||
{
|
{
|
||||||
return tenancy($key);
|
if (! is_null($key)) {
|
||||||
|
return app(Tenant::class)->get($key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue