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

Fix #998, properly replace ALL tenant_id literals

This commit is contained in:
Samuel Štancl 2022-11-10 16:44:52 +01:00
parent 8a00a105d0
commit 2a39b0526a
11 changed files with 35 additions and 22 deletions

View file

@ -10,10 +10,16 @@ Run `composer docker-up` to start the containers. Then run `composer test` to ru
If you need to pass additional flags to phpunit, use `./test --foo` instead of `composer test --foo`. Composer scripts unfortunately don't pass CLI arguments. If you need to pass additional flags to phpunit, use `./test --foo` instead of `composer test --foo`. Composer scripts unfortunately don't pass CLI arguments.
If you want to run a specific test (or test file), you can also use `./t 'name of the test'`. This is equivalent to `./test --no-coverage --filter 'name of the test'`. If you want to run a specific test (or test file), you can also use `./t 'name of the test'`. This is equivalent to `./test --no-coverage --filter 'name of the test'` (`--no-coverage` speeds up the execution time).
When you're done testing, run `composer docker-down` to shut down the containers. When you're done testing, run `composer docker-down` to shut down the containers.
### Debugging tests
If you're developing some feature and you encounter `SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry` errors, it's likely that some PHP errors were thrown in past test runs and prevented the test cleanup from running properly.
To fix this, simply delete the database memory by shutting down containers and starting them again: `composer docker-down && composer docker-up`.
### Docker on M1 ### Docker on M1
Run `composer docker-m1` to symlink `docker-compose-m1.override.yml` to `docker-compose.override.yml`. This will reconfigure a few services in the docker compose config to be compatible with M1. Run `composer docker-m1` to symlink `docker-compose-m1.override.yml` to `docker-compose.override.yml`. This will reconfigure a few services in the docker compose config to be compatible with M1.

View file

@ -14,7 +14,7 @@ return [
'domain' => Stancl\Tenancy\Database\Models\Domain::class, 'domain' => Stancl\Tenancy\Database\Models\Domain::class,
/** /**
* Name of the column used to for ->tenant() relationships. * Name of the column used to relate models to tenants.
* *
* This is used by the HasDomains trait, and models that use the BelongsToTenant trait (used in single-database tenancy). * This is used by the HasDomains trait, and models that use the BelongsToTenant trait (used in single-database tenancy).
*/ */

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Stancl\Tenancy\Tenancy;
class CreateTenantUserImpersonationTokensTable extends Migration class CreateTenantUserImpersonationTokensTable extends Migration
{ {
@ -17,13 +18,13 @@ class CreateTenantUserImpersonationTokensTable extends Migration
{ {
Schema::create('tenant_user_impersonation_tokens', function (Blueprint $table) { Schema::create('tenant_user_impersonation_tokens', function (Blueprint $table) {
$table->string('token', 128)->primary(); $table->string('token', 128)->primary();
$table->string('tenant_id'); $table->string(Tenancy::tenantKeyColumn());
$table->string('user_id'); $table->string('user_id');
$table->string('auth_guard'); $table->string('auth_guard');
$table->string('redirect_url'); $table->string('redirect_url');
$table->timestamp('created_at'); $table->timestamp('created_at');
$table->foreign('tenant_id')->references('id')->on('tenants')->onUpdate('cascade')->onDelete('cascade'); $table->foreign(Tenancy::tenantKeyColumn())->references('id')->on('tenants')->onUpdate('cascade')->onDelete('cascade');
}); });
} }

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Stancl\Tenancy\Tenancy;
class CreateDomainsTable extends Migration class CreateDomainsTable extends Migration
{ {
@ -18,10 +19,10 @@ class CreateDomainsTable extends Migration
Schema::create('domains', function (Blueprint $table) { Schema::create('domains', function (Blueprint $table) {
$table->increments('id'); $table->increments('id');
$table->string('domain', 255)->unique(); $table->string('domain', 255)->unique();
$table->string('tenant_id'); $table->string(Tenancy::tenantKeyColumn());
$table->timestamps(); $table->timestamps();
$table->foreign('tenant_id')->references('id')->on('tenants')->onUpdate('cascade'); $table->foreign(Tenancy::tenantKeyColumn())->references('id')->on('tenants')->onUpdate('cascade');
}); });
} }

View file

@ -6,6 +6,7 @@ namespace Stancl\Tenancy\Database\Concerns;
use Stancl\Tenancy\Contracts\Tenant; use Stancl\Tenancy\Contracts\Tenant;
use Stancl\Tenancy\Database\TenantScope; use Stancl\Tenancy\Database\TenantScope;
use Stancl\Tenancy\Tenancy;
/** /**
* @property-read Tenant $tenant * @property-read Tenant $tenant
@ -14,12 +15,7 @@ trait BelongsToTenant
{ {
public function tenant() public function tenant()
{ {
return $this->belongsTo(config('tenancy.models.tenant'), static::tenantIdColumn()); return $this->belongsTo(config('tenancy.models.tenant'), Tenancy::tenantKeyColumn());
}
public static function tenantIdColumn(): string
{
return config('tenancy.models.tenant_key_column');
} }
public static function bootBelongsToTenant(): void public static function bootBelongsToTenant(): void
@ -27,9 +23,9 @@ trait BelongsToTenant
static::addGlobalScope(new TenantScope); static::addGlobalScope(new TenantScope);
static::creating(function ($model) { static::creating(function ($model) {
if (! $model->getAttribute(static::tenantIdColumn()) && ! $model->relationLoaded('tenant')) { if (! $model->getAttribute(Tenancy::tenantKeyColumn()) && ! $model->relationLoaded('tenant')) {
if (tenancy()->initialized) { if (tenancy()->initialized) {
$model->setAttribute(static::tenantIdColumn(), tenant()->getTenantKey()); $model->setAttribute(Tenancy::tenantKeyColumn(), tenant()->getTenantKey());
$model->setRelation('tenant', tenant()); $model->setRelation('tenant', tenant());
} }
} }

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Database\Concerns; namespace Stancl\Tenancy\Database\Concerns;
use Stancl\Tenancy\Contracts\Domain; use Stancl\Tenancy\Contracts\Domain;
use Stancl\Tenancy\Tenancy;
/** /**
* @property-read Domain[]|\Illuminate\Database\Eloquent\Collection $domains * @property-read Domain[]|\Illuminate\Database\Eloquent\Collection $domains
@ -15,7 +16,7 @@ trait HasDomains
{ {
public function domains() public function domains()
{ {
return $this->hasMany(config('tenancy.models.domain'), 'tenant_id'); return $this->hasMany(config('tenancy.models.domain'), Tenancy::tenantKeyColumn());
} }
public function createDomain($data): Domain public function createDomain($data): Domain

View file

@ -6,16 +6,17 @@ namespace Stancl\Tenancy\Database\Concerns;
use Illuminate\Validation\Rules\Exists; use Illuminate\Validation\Rules\Exists;
use Illuminate\Validation\Rules\Unique; use Illuminate\Validation\Rules\Unique;
use Stancl\Tenancy\Tenancy;
trait HasScopedValidationRules trait HasScopedValidationRules
{ {
public function unique($table, $column = 'NULL') public function unique($table, $column = 'NULL')
{ {
return (new Unique($table, $column))->where(BelongsToTenant::tenantIdColumn(), $this->getTenantKey()); return (new Unique($table, $column))->where(Tenancy::tenantKeyColumn(), $this->getTenantKey());
} }
public function exists($table, $column = 'NULL') public function exists($table, $column = 'NULL')
{ {
return (new Exists($table, $column))->where(BelongsToTenant::tenantIdColumn(), $this->getTenantKey()); return (new Exists($table, $column))->where(Tenancy::tenantKeyColumn(), $this->getTenantKey());
} }
} }

View file

@ -7,7 +7,7 @@ namespace Stancl\Tenancy\Database;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope; use Illuminate\Database\Eloquent\Scope;
use Stancl\Tenancy\Database\Concerns\BelongsToTenant; use Stancl\Tenancy\Tenancy;
class TenantScope implements Scope class TenantScope implements Scope
{ {
@ -17,7 +17,7 @@ class TenantScope implements Scope
return; return;
} }
$builder->where($model->qualifyColumn(BelongsToTenant::tenantIdColumn()), tenant()->getTenantKey()); $builder->where($model->qualifyColumn(Tenancy::tenantKeyColumn()), tenant()->getTenantKey());
} }
public function extend(Builder $builder): void public function extend(Builder $builder): void

View file

@ -20,7 +20,7 @@ class UserImpersonation implements Feature
{ {
$tenancy->macro('impersonate', function (Tenant $tenant, string $userId, string $redirectUrl, string $authGuard = null): ImpersonationToken { $tenancy->macro('impersonate', function (Tenant $tenant, string $userId, string $redirectUrl, string $authGuard = null): ImpersonationToken {
return ImpersonationToken::create([ return ImpersonationToken::create([
'tenant_id' => $tenant->getTenantKey(), Tenancy::tenantKeyColumn() => $tenant->getTenantKey(),
'user_id' => $userId, 'user_id' => $userId,
'redirect_url' => $redirectUrl, 'redirect_url' => $redirectUrl,
'auth_guard' => $authGuard, 'auth_guard' => $authGuard,
@ -39,7 +39,7 @@ class UserImpersonation implements Feature
abort_if($tokenExpired, 403); abort_if($tokenExpired, 403);
$tokenTenantId = (string) $token->tenant_id; $tokenTenantId = (string) $token->getAttribute(Tenancy::tenantKeyColumn());
$currentTenantId = (string) tenant()->getTenantKey(); $currentTenantId = (string) tenant()->getTenantKey();
abort_unless($tokenTenantId === $currentTenantId, 403); abort_unless($tokenTenantId === $currentTenantId, 403);

View file

@ -14,6 +14,7 @@ use Stancl\Tenancy\Database\TenantCollection;
use Stancl\Tenancy\Events\SyncedResourceChangedInForeignDatabase; use Stancl\Tenancy\Events\SyncedResourceChangedInForeignDatabase;
use Stancl\Tenancy\Events\SyncedResourceSaved; use Stancl\Tenancy\Events\SyncedResourceSaved;
use Stancl\Tenancy\Exceptions\ModelNotSyncMasterException; use Stancl\Tenancy\Exceptions\ModelNotSyncMasterException;
use Stancl\Tenancy\Tenancy;
// todo@v4 review all code related to resource syncing // todo@v4 review all code related to resource syncing
@ -77,7 +78,7 @@ class UpdateSyncedResource extends QueueableListener
/** @var Tenant */ /** @var Tenant */
$tenant = $event->tenant; $tenant = $event->tenant;
return ((string) $model->pivot->tenant_id) === ((string) $tenant->getTenantKey()); return ((string) $model->pivot->getAttribute(Tenancy::tenantKeyColumn())) === ((string) $tenant->getTenantKey());
}; };
$mappingExists = $centralModel->tenants->contains($currentTenantMapping); $mappingExists = $centralModel->tenants->contains($currentTenantMapping);

View file

@ -105,6 +105,12 @@ class Tenancy
return $model; return $model;
} }
/** Name of the column used to relate models to tenants. */
public static function tenantKeyColumn(): string
{
return config('tenancy.models.tenant_key_column') ?? 'tenant_id';
}
/** /**
* Try to find a tenant using an ID. * Try to find a tenant using an ID.
* *