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

Domain model & resolver test

This commit is contained in:
Samuel Štancl 2020-05-08 18:16:18 +02:00
parent 08ed5084d5
commit e1a4054743
11 changed files with 158 additions and 8 deletions

View file

@ -2,10 +2,12 @@
declare(strict_types=1);
use Stancl\Tenancy\Database\Models\Domain;
use Stancl\Tenancy\Database\Models\Tenant;
return [
'tenant_model' => Tenant::class,
'domain_model' => Domain::class,
'internal_prefix' => 'tenancy_',
'central_connection' => 'central',

View file

@ -16,9 +16,11 @@ class CreateDomainsTable extends Migration
public function up(): void
{
Schema::create('domains', function (Blueprint $table) {
$table->string('domain', 255)->primary();
$table->increments('id');
$table->string('domain', 255)->unique();
$table->string('tenant_id', 36);
$table->timestamps();
$table->foreign('tenant_id')->references('id')->on('tenants')->onUpdate('cascade')->onDelete('cascade');
});
}

6
src/Contracts/Tenant.php Normal file
View file

@ -0,0 +1,6 @@
<?php
namespace Stancl\Tenancy\Contracts;
interface Tenant
{}

View file

@ -0,0 +1,9 @@
<?php
namespace Stancl\Tenancy\Contracts;
use Exception;
abstract class TenantCouldNotBeIdentifiedException extends Exception
{
}

View file

@ -0,0 +1,13 @@
<?php
namespace Stancl\Tenancy\Contracts;
interface TenantResolver
{
/**
* Resolve a tenant using some value.
*
* @throws TenantCouldNotBeIdentifiedException
*/
public function resolve(...$args): Tenant;
}

View file

@ -3,19 +3,42 @@
namespace Stancl\Tenancy\Database\Models;
use Illuminate\Database\Eloquent\Model;
use Stancl\Tenancy\Contracts\Tenant;
use Stancl\Tenancy\Events\DomainCreated;
use Stancl\Tenancy\Events\DomainDeleted;
use Stancl\Tenancy\Events\DomainSaved;
use Stancl\Tenancy\Events\DomainUpdated;
use Stancl\Tenancy\Exceptions\DomainsOccupiedByOtherTenantException;
/**
* @property string $domain
* @property string $tenant_id
*
* @property-read Tenant $tenant
*/
class Domain extends Model
{
public function tenant()
public $guarded = [];
public static function booted()
{
return $this->belongsTo(Tenant::class);
$ensureDomainIsNotOccupied = function (Domain $self) {
if ($domain = Domain::where('domain', $self->domain)->first()) {
if ($domain->getKey() !== $self->getKey()) {
throw new DomainsOccupiedByOtherTenantException;
}
}
};
static::saving($ensureDomainIsNotOccupied);
}
protected $dispatchEvents = [
public function tenant()
{
return $this->belongsTo(config('tenancy.tenant_model'));
}
public $dispatchEvents = [
'saved' => DomainSaved::class,
'created' => DomainCreated::class,
'updated' => DomainUpdated::class,

View file

@ -5,9 +5,11 @@ namespace Stancl\Tenancy\Database\Models;
use Illuminate\Database\Eloquent\Model;
use Stancl\Tenancy\DatabaseConfig;
use Stancl\Tenancy\Events;
use Stancl\Tenancy\Contracts;
// todo use a contract
class Tenant extends Model
// todo @property
class Tenant extends Model implements Contracts\Tenant
{
use Concerns\CentralConnection, Concerns\HasADataColumn, Concerns\GeneratesIds, Concerns\HasADataColumn {
Concerns\HasADataColumn::getCasts as dataColumnCasts;
@ -80,7 +82,7 @@ class Tenant extends Model
return $result;
}
protected $dispatchesEvents = [
public $dispatchesEvents = [
'saved' => Events\TenantSaved::class,
'created' => Events\TenantCreated::class,
'updated' => Events\TenantUpdated::class,

View file

@ -7,8 +7,9 @@ namespace Stancl\Tenancy\Exceptions;
use Facade\IgnitionContracts\BaseSolution;
use Facade\IgnitionContracts\ProvidesSolution;
use Facade\IgnitionContracts\Solution;
use Stancl\Tenancy\Contracts\TenantCouldNotBeIdentifiedException;
class TenantCouldNotBeIdentifiedException extends \Exception implements ProvidesSolution
class TenantCouldNotBeIdentifiedOnDomainException extends TenantCouldNotBeIdentifiedException implements ProvidesSolution
{
public function __construct($domain)
{
@ -20,7 +21,7 @@ class TenantCouldNotBeIdentifiedException extends \Exception implements Provides
return BaseSolution::create('Tenant could not be identified on this domain')
->setSolutionDescription('Did you forget to create a tenant for this domain?')
->setDocumentationLinks([
'Creating Tenants' => 'https://tenancy.samuelstancl.me/docs/v2/creating-tenants/',
'Creating Tenants' => 'https://tenancyforlaravel.com/docs/v2/creating-tenants/', // todo update link for v3
]);
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace Stancl\Tenancy\Resolvers;
use Stancl\Tenancy\Contracts\Tenant;
use Stancl\Tenancy\Contracts\TenantResolver;
use Stancl\Tenancy\Database\Models\Domain;
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException;
class DomainTenantResolver implements TenantResolver
{
public function resolve(...$args): Tenant
{
$domain = app(config('tenancy.domain_model'))->where('domain', $args[0])->first();
if ($domain) {
return $domain->tenant;
}
throw new TenantCouldNotBeIdentifiedOnDomainException($domain);
}
}

View file

@ -0,0 +1,70 @@
<?php
namespace Stancl\Tenancy\Tests\v3;
use Stancl\Tenancy\Database\Models;
use Stancl\Tenancy\Database\Models\Domain;
use Stancl\Tenancy\Exceptions\DomainsOccupiedByOtherTenantException;
use Stancl\Tenancy\Exceptions\TenantCouldNotBeIdentifiedOnDomainException;
use Stancl\Tenancy\Resolvers\DomainTenantResolver;
use Stancl\Tenancy\Tests\TestCase;
class DomainTest extends TestCase
{
public function setUp(): void
{
parent::setUp();
config(['tenancy.tenant_model' => Tenant::class]);
}
/** @test */
public function tenant_can_be_identified_using_hostname()
{
$tenant = Tenant::create();
$id = $tenant->id;
$tenant->domains()->create([
'domain' => 'foo.localhost',
]);
$resolvedTenant = app(DomainTenantResolver::class)->resolve('foo.localhost');
$this->assertSame($id, $resolvedTenant->id);
$this->assertSame(['foo.localhost'], $resolvedTenant->domains->pluck('domain')->toArray());
}
/** @test */
public function a_domain_can_belong_to_only_one_tenant()
{
$tenant = Tenant::create();
$tenant->domains()->create([
'domain' => 'foo.localhost',
]);
$tenant2 = Tenant::create();
$this->expectException(DomainsOccupiedByOtherTenantException::class);
$tenant2->domains()->create([
'domain' => 'foo.localhost',
]);
}
/** @test */
public function an_exception_is_thrown_if_tenant_cannot_be_identified()
{
$this->expectException(TenantCouldNotBeIdentifiedOnDomainException::class);
app(DomainTenantResolver::class)->resolve('foo.localhost');
}
}
class Tenant extends Models\Tenant
{
public function domains()
{
return $this->hasMany(Domain::class);
}
}