From 73c8102138b6710621e322a4eac0a4a0631d2710 Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 10:13:01 +1200 Subject: [PATCH 01/18] Update dependencies --- composer.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index ffad674..2ebefaa 100644 --- a/composer.json +++ b/composer.json @@ -10,13 +10,14 @@ } ], "require": { - "php": ">=5.4.0", + "php": ">=5.6.0", "illuminate/support": "5.2.*|5.3.*" }, "require-dev": { "mockery/mockery": "~0.9", - "phpunit/phpunit": "~4.0", - "laravel/framework": "5.2.*|5.3.*" + "phpunit/phpunit": "5.5.*", + "laravel/framework": "5.2.*|5.3.*", + "laravel/lumen-framework": "5.2.*|5.3.*" }, "autoload": { "psr-4": { From 61051ff56553d1eab4f57c8dd7a3c7d9e37690a3 Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 10:13:28 +1200 Subject: [PATCH 02/18] Rename --- .../{LandlordFacade.php => Landlord.php} | 6 +- src/Landlord.php | 175 ----------------- src/TenantManager.php | 179 ++++++++++++++++++ 3 files changed, 182 insertions(+), 178 deletions(-) rename src/Facades/{LandlordFacade.php => Landlord.php} (69%) delete mode 100644 src/Landlord.php create mode 100644 src/TenantManager.php diff --git a/src/Facades/LandlordFacade.php b/src/Facades/Landlord.php similarity index 69% rename from src/Facades/LandlordFacade.php rename to src/Facades/Landlord.php index 5dae864..9daed38 100644 --- a/src/Facades/LandlordFacade.php +++ b/src/Facades/Landlord.php @@ -2,10 +2,10 @@ namespace HipsterJazzbo\Landlord\Facades; -use HipsterJazzbo\Landlord\Landlord; +use HipsterJazzbo\Landlord\TenantManager; use Illuminate\Support\Facades\Facade; -class LandlordFacade extends Facade +class Landlord extends Facade { /** * Get the registered name of the component. @@ -14,6 +14,6 @@ class LandlordFacade extends Facade */ protected static function getFacadeAccessor() { - return Landlord::class; + return TenantManager::class; } } diff --git a/src/Landlord.php b/src/Landlord.php deleted file mode 100644 index 494d63f..0000000 --- a/src/Landlord.php +++ /dev/null @@ -1,175 +0,0 @@ -tenants; - } - - /** - * Add $tenantColumn => $tenantId to the current tenants array. - * - * @param string $tenantColumn - * @param mixed $tenantId - */ - public function addTenant($tenantColumn, $tenantId) - { - $this->enable(); - - $this->tenants[$tenantColumn] = $tenantId; - } - - /** - * Remove $tenantColumn => $id from the current tenants array. - * - * @param string $tenantColumn - * - * @return boolean - */ - public function removeTenant($tenantColumn) - { - if ($this->hasTenant($tenantColumn)) { - unset($this->tenants[$tenantColumn]); - - return true; - } - - return false; - } - - /** - * Test whether current tenants include a given tenant. - * - * @param string $tenantColumn - * - * @return boolean - */ - public function hasTenant($tenantColumn) - { - return isset($this->tenants[$tenantColumn]); - } - - /** - * Apply the scope to a given Eloquent query builder. - * - * @param Builder $builder - * @param Model|\HipsterJazzbo\Landlord\BelongsToTenant $model - */ - public function apply(Builder $builder, Model $model) - { - if (! $this->enabled) { - return; - } - - foreach ($this->getModelTenants($model) as $tenantColumn => $tenantId) { - $builder->where($model->getTable() . '.' . $tenantColumn, '=', $tenantId); - } - } - - /** - * @param Model|\HipsterJazzbo\Landlord\BelongsToTenant $model - */ - public function creating(Model $model) - { - // If the model has had the global scope removed, bail - if (! $model->hasGlobalScope($this)) { - return; - } - - // Otherwise, scope the new model - foreach ($this->getModelTenants($model) as $tenantColumn => $tenantId) { - $model->{$tenantColumn} = $tenantId; - } - } - - /** - * Return which tenantColumn => tenantId are really in use for this model. - * - * @param Model|\HipsterJazzbo\Landlord\BelongsToTenant $model - * - * @throws TenantColumnUnknownException - * - * @return array - */ - public function getModelTenants(Model $model) - { - $modelTenantColumns = (array)$model->getTenantColumns(); - $modelTenants = []; - - foreach ($modelTenantColumns as $tenantColumn) { - if ($this->hasTenant($tenantColumn)) { - $modelTenants[$tenantColumn] = $this->getTenantId($tenantColumn); - } - } - - return $modelTenants; - } - - /** - * Gets the Tenant ID - * - * @param $tenantColumn - * - * @throws TenantColumnUnknownException - * - * @return mixed The id of the tenant - */ - public function getTenantId($tenantColumn) - { - if (! $this->hasTenant($tenantColumn)) { - throw new TenantColumnUnknownException( - get_class($this->model) . ': tenant column "' . $tenantColumn . '" NOT found in tenants scope "' . json_encode($this->tenants) . '"' - ); - } - - return $this->tenants[$tenantColumn]; - } - - /** - * Disables the scoping of tenants - * - * @return void - */ - public function disable() - { - $this->enabled = false; - } - - /** - * Enables the scoping of tenants - * - * @return void - */ - public function enable() - { - $this->enabled = true; - } -} diff --git a/src/TenantManager.php b/src/TenantManager.php new file mode 100644 index 0000000..92a3328 --- /dev/null +++ b/src/TenantManager.php @@ -0,0 +1,179 @@ +tenants = collect(); + } + + /** + * Enable scoping by tenants. + * + * @return void + */ + public function enable() + { + $this->enabled = true; + } + + /** + * Disable scoping by tenants. + * + * @return void + */ + public function disable() + { + $this->enabled = false; + } + + /** + * Add a tenant to scope by. + * + * @param string|Model $tenant + * + * @param mixed|null $id + */ + public function addTenant($tenant, $id = null) + { + if (func_num_args() == 1) { + $id = $tenant->getKey(); + } + + $this->tenants->put($this->getTenantKey($tenant), $id); + } + + /** + * Remove a tenant so that queries are no longer scoped by it. + * + * @param string|Model $tenant + */ + public function removeTenant($tenant) + { + $this->tenants->pull($this->getTenantKey($tenant)); + } + + /** + * Whether a tenant is currently being scoped. + * + * @param string|Model $tenant + * + * @return bool + */ + public function hasTenant($tenant) + { + return $this->tenants->has($this->getTenantKey($tenant)); + } + + /** + * @return Collection + */ + public function getTenants() + { + return $this->tenants; + } + + /** + * Applies applicable tenant scopes to a model. + * + * @param Model $model + */ + public function applyTenantScopes(Model $model) + { + if (! $this->enabled) { + return; + } + + $this->modelTenants($model)->each(function ($id, $tenant) use ($model) { + $model->addGlobalScope($tenant, function (Builder $builder) use ($tenant, $id, $model) { + $builder->where($model->getTable() . '.' . $tenant, '=', $id); + }); + }); + } + + /** + * Add tenant columns as needed to a new model instance before it is created. + * + * @param Model $model + */ + public function newModel(Model $model) + { + if (! $this->enabled) { + return; + } + + $this->modelTenants($model)->each(function ($id, $tenant) use ($model) { + if (! isset($model->{$tenant})) { + $model->setAttribute($tenant, $id); + } + }); + } + + /** + * Get a new Eloquent Builder instance without any of the tenant scopes applied. + * + * @param Model $model + * + * @return \Illuminate\Database\Eloquent\Builder + */ + public function newQueryWithoutTenants(Model $model) + { + return $model->newQuery()->withoutGlobalScopes($this->tenants->keys()); + } + + /** + * Get the key for a tenant, either form a Model instance or a string. + * + * @param string|Model $tenant + * + * @return string + * @throws TenantColumnUnknownException + */ + protected function getTenantKey($tenant) + { + if ($tenant instanceof Model) { + $tenant = $tenant->getForeignKey(); + } + + if (! is_string($tenant)) { + throw new TenantColumnUnknownException( + '$tenant must be a string key or an instance of \Illuminate\Database\Eloquent\Model' + ); + } + + return $tenant; + } + + /** + * Get the tenants that are actually applicable to the given + * model, in case they've been manually specified. + * + * @param Model|BelongsToTenant $model + * + * @return Collection + */ + protected function modelTenants(Model $model) + { + return $this->tenants->only($model->getTenants()); + } +} From 265b4712abab252260c93624f0a7010c20425247 Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 10:13:53 +1200 Subject: [PATCH 03/18] Update trait --- src/BelongsToTenant.php | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/BelongsToTenant.php b/src/BelongsToTenant.php index 8afa2eb..477c736 100644 --- a/src/BelongsToTenant.php +++ b/src/BelongsToTenant.php @@ -3,33 +3,28 @@ namespace HipsterJazzbo\Landlord; use HipsterJazzbo\Landlord\Exceptions\TenantModelNotFoundException; +use HipsterJazzbo\Landlord\Facades\Landlord; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\ModelNotFoundException; -use Illuminate\Database\Eloquent\Scope; /** - * Class BelongsToTenant. - * - * @method static void addGlobalScope(Scope $scope) - * @method static void creating(callable $callback) + * @mixin Model */ trait BelongsToTenant { public static function bootBelongsToTenant() { - $tenantScope = app(Landlord::class); + // Add a global scope for each tenant this model should be scoped by. + Landlord::applyTenantScopes(new static()); - // Add the global scope that will handle all operations except create() - static::addGlobalScope($tenantScope); - - // Add an observer that will automatically add the tenant id when create()-ing - static::creating(function (Model $model) use ($tenantScope) { - $tenantScope->creating($model); + // Add tenants automatically when creating models + static::creating(function (Model $model) { + Landlord::newModel($model); }); } /** - * Returns a new builder without the tenant scope applied. + * Returns a new query builder without any of the tenant scopes applied. * * $allUsers = User::allTenants()->get(); * @@ -37,32 +32,34 @@ public static function bootBelongsToTenant() */ public static function allTenants() { - return (new static())->newQueryWithoutScope(Landlord::class); + return Landlord::newQueryWithoutTenants(new static()); } /** - * Get the name of the "tenant id" column(s). + * Get the tenants for this model. * - * @return string + * @return array */ - public function getTenantColumns() + public function getTenants() { - return isset($this->tenantColumns) ? $this->tenantColumns : config('landlord.default_tenant_columns'); + return isset($this->tenants) ? $this->tenants : config('landlord.default_tenants'); } /** - * Override the default findOrFail method so that we can rethrow a more useful exception. - * Otherwise it can be very confusing why queries don't work because of tenant scoping issues. + * Override the default findOrFail method so that we can re-throw + * a more useful exception. Otherwise it can be very confusing + * why queries don't work because of tenant scoping issues. * - * @param $id + * @param mixed $id * @param array $columns * + * @return \Illuminate\Database\Eloquent\Collection|Model * @throws TenantModelNotFoundException */ public static function findOrFail($id, $columns = ['*']) { try { - return parent::query()->findOrFail($id, $columns); + return static::query()->findOrFail($id, $columns); } catch (ModelNotFoundException $e) { throw (new TenantModelNotFoundException())->setModel(get_called_class()); } From 1c6380df359b5beb371048b15ec4406beb21941f Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 10:14:09 +1200 Subject: [PATCH 04/18] =?UTF-8?q?Don=E2=80=99t=20auto-register=20alias=20(?= =?UTF-8?q?to=20support=20Lumen)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/LandlordServiceProvider.php | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/LandlordServiceProvider.php b/src/LandlordServiceProvider.php index 919c8c7..60397cf 100644 --- a/src/LandlordServiceProvider.php +++ b/src/LandlordServiceProvider.php @@ -2,9 +2,6 @@ namespace HipsterJazzbo\Landlord; -use HipsterJazzbo\Landlord\Facades\LandlordFacade; -use Illuminate\Contracts\Foundation\Application; -use Illuminate\Foundation\AliasLoader; use Illuminate\Support\ServiceProvider; class LandlordServiceProvider extends ServiceProvider @@ -28,15 +25,8 @@ public function boot() */ public function register() { - $this->app->singleton(Landlord::class, function () { - return new Landlord(); - }); - - // Define alias 'Landlord' - $this->app->booting(function () { - $loader = AliasLoader::getInstance(); - - $loader->alias('Landlord', LandlordFacade::class); + $this->app->singleton(TenantManager::class, function () { + return new TenantManager(); }); } } From 69dd4cd5fc17b74cd0a90386af3d35a9767799a3 Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 10:14:24 +1200 Subject: [PATCH 05/18] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2f64b6..9c35df5 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Add the ServiceProvider and Alias to their relative arrays in `config/app.php`: 'aliases' => [ ... - 'Landlord' => HipsterJazzbo\Landlord\Facades\LandlordFacade::class, + 'Landlord' => HipsterJazzbo\Landlord\Facades\Landlord::class, ], ``` From 2cb39b19c4a05f16ec94417b55adc65670582286 Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 10:14:30 +1200 Subject: [PATCH 06/18] Tests --- phpunit.xml.dist | 2 +- tests/LandlordTest.php | 117 +++++++++++++++++++++++++++ tests/TenantScopeTest.php | 106 ------------------------ tests/TenantScopedModelTraitTest.php | 53 ------------ 4 files changed, 118 insertions(+), 160 deletions(-) create mode 100644 tests/LandlordTest.php delete mode 100644 tests/TenantScopeTest.php delete mode 100644 tests/TenantScopedModelTraitTest.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 1406a16..b94cf2b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -11,7 +11,7 @@ syntaxCheck="false" > - + ./tests/ diff --git a/tests/LandlordTest.php b/tests/LandlordTest.php new file mode 100644 index 0000000..bab1e6b --- /dev/null +++ b/tests/LandlordTest.php @@ -0,0 +1,117 @@ +addTenant('tenant_a_id', 1); + + $this->assertEquals(['tenant_a_id' => 1], $landlord->getTenants()->toArray()); + + $landlord->addTenant('tenant_b_id', 2); + + $this->assertEquals(['tenant_a_id' => 1, 'tenant_b_id' => 2], $landlord->getTenants()->toArray()); + + $landlord->removeTenant('tenant_a_id'); + + $this->assertEquals(['tenant_b_id' => 2], $landlord->getTenants()->toArray()); + + $this->assertTrue($landlord->hasTenant('tenant_b_id')); + + $this->assertFalse($landlord->hasTenant('tenant_a_id')); + } + + public function testTenantsWithModels() + { + Landlord::shouldReceive('applyTenantScopes'); + + $tenantA = new TenantA(); + + $tenantA->id = 1; + + $tenantB = new TenantB(); + + $tenantB->id = 2; + + $landlord = new TenantManager(); + + $landlord->addTenant($tenantA); + + $this->assertEquals(['tenant_a_id' => 1], $landlord->getTenants()->toArray()); + + $landlord->addTenant($tenantB); + + $this->assertEquals(['tenant_a_id' => 1, 'tenant_b_id' => 2], $landlord->getTenants()->toArray()); + + $landlord->removeTenant($tenantA); + + $this->assertEquals(['tenant_b_id' => 2], $landlord->getTenants()->toArray()); + + $this->assertTrue($landlord->hasTenant('tenant_b_id')); + + $this->assertFalse($landlord->hasTenant('tenant_a_id')); + } + + public function testApplyTenantScopes() + { + $landlord = new TenantManager(); + + $landlord->addTenant('tenant_a_id', 1); + + $landlord->addTenant('tenant_b_id', 2); + + Landlord::shouldReceive('applyTenantScopes'); + + $model = new ModelStub(); + + $landlord->applyTenantScopes($model); + + $this->assertArrayHasKey('tenant_a_id', $model->getGlobalScopes()); + + $this->assertArrayNotHasKey('tenant_b_id', $model->getGlobalScopes()); + } + + public function testNewModel() + { + $landlord = new TenantManager(); + + $landlord->addTenant('tenant_a_id', 1); + + $landlord->addTenant('tenant_b_id', 2); + + Landlord::shouldReceive('applyTenantScopes'); + + $model = new ModelStub(); + + $landlord->newModel($model); + + $this->assertEquals(1, $model->tenant_a_id); + + $this->assertNull($model->tenant_b_id); + } +} + +class ModelStub extends Model +{ + use BelongsToTenant; + + public $tenants = ['tenant_a_id']; +} + +class TenantA extends Model +{ + use BelongsToTenant; +} + +class TenantB extends Model +{ + use BelongsToTenant; +} diff --git a/tests/TenantScopeTest.php b/tests/TenantScopeTest.php deleted file mode 100644 index 1031e15..0000000 --- a/tests/TenantScopeTest.php +++ /dev/null @@ -1,106 +0,0 @@ -addTenant('column', 1); - - $tenants = $tenantScope->getTenants(); - $this->assertEquals(['column' => 1], $tenants); - - $this->assertTrue($tenantScope->hasTenant('column')); - - $tenantScope->removeTenant('column'); - - $tenants = $tenantScope->getTenants(); - $this->assertEquals([], $tenants); - } - - public function testApply() - { - $scope = m::mock(\HipsterJazzbo\Landlord\Landlord::class); - $builder = m::mock(\Illuminate\Database\Eloquent\Builder::class); - $model = m::mock(\Illuminate\Database\Eloquent\Model::class); - - $scope->shouldDeferMissing(); - $scope->shouldReceive('getModelTenants')->once()->with($model)->andReturn(['column' => 1]); - - $builder->shouldReceive('getModel')->andReturn($model); - $builder->shouldReceive('where')->once()->with('table.column', '=', '1'); - - $model->shouldReceive('getTable')->andReturn('table'); - - $scope->apply($builder, $model); - } - - public function testCreating() - { - $scope = m::mock(\HipsterJazzbo\Landlord\Landlord::class); - $model = m::mock(\Illuminate\Database\Eloquent\Model::class); - - $scope->shouldDeferMissing(); - $scope->shouldReceive('getModelTenants')->with($model)->andReturn(['column' => 1]); - - $model->shouldDeferMissing(); - $model->shouldReceive('hasGlobalScope')->andReturn(true); - - $scope->creating($model); - - $this->assertEquals(1, $model->column); - } - - public function testGetModelTenants() - { - $scope = m::mock(\HipsterJazzbo\Landlord\Landlord::class); - $model = m::mock(\Illuminate\Database\Eloquent\Model::class); - - $scope->shouldDeferMissing(); - $scope->shouldReceive('getTenantId')->once()->andReturn(1); - $scope->shouldReceive('hasTenant')->once()->andReturn(true); - - $model->shouldReceive('getTenantColumns')->once()->andReturn(['column']); - - $modelTenants = $scope->getModelTenants($model); - - $this->assertEquals(['column' => 1], $modelTenants); - } - - /** - * @expectedException \HipsterJazzbo\Landlord\Exceptions\TenantColumnUnknownException - */ - public function testGetTenantIdThrowsException() - { - $scope = new Landlord(); - - $scope->getTenantId('column'); - } - - public function testDisable() - { - $scope = m::mock(\HipsterJazzbo\Landlord\Landlord::class); - $builder = m::mock(\Illuminate\Database\Eloquent\Builder::class); - $model = m::mock(\Illuminate\Database\Eloquent\Model::class); - - $scope->shouldDeferMissing(); - $scope->shouldReceive('getModelTenants')->with($model)->andReturn(['column' => 1])->never(); - - $builder->shouldReceive('getModel')->andReturn($model)->never(); - $builder->shouldReceive('whereRaw')->with("table.column = '1'")->never(); - - $model->shouldReceive('getTenantWhereClause')->with('column', 1)->andReturn("table.column = '1'")->never(); - - $scope->disable(); - $scope->apply($builder, $model); - } -} diff --git a/tests/TenantScopedModelTraitTest.php b/tests/TenantScopedModelTraitTest.php deleted file mode 100644 index 789d533..0000000 --- a/tests/TenantScopedModelTraitTest.php +++ /dev/null @@ -1,53 +0,0 @@ - Date: Tue, 13 Sep 2016 11:07:23 +1200 Subject: [PATCH 07/18] Style fixes --- composer.json | 7 ++++++- src/LandlordServiceProvider.php | 2 +- src/TenantManager.php | 5 ++--- tests/LandlordTest.php | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 2ebefaa..b63f1ff 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,12 @@ "name": "hipsterjazzbo/landlord", "description": "A simple, single database multi-tenancy solution for Laravel 5.2+", "license": "MIT", - "keywords": ["tenant", "tenancy", "multitenant", "multitenancy"], + "keywords": [ + "tenant", + "tenancy", + "multitenant", + "multitenancy" + ], "authors": [ { "name": "Caleb Fidecaro", diff --git a/src/LandlordServiceProvider.php b/src/LandlordServiceProvider.php index 60397cf..ed69629 100644 --- a/src/LandlordServiceProvider.php +++ b/src/LandlordServiceProvider.php @@ -14,7 +14,7 @@ class LandlordServiceProvider extends ServiceProvider public function boot() { $this->publishes([ - realpath(__DIR__ . '/../config/landlord.php') => config_path('landlord.php') + realpath(__DIR__.'/../config/landlord.php') => config_path('landlord.php') ]); } diff --git a/src/TenantManager.php b/src/TenantManager.php index 92a3328..62af872 100644 --- a/src/TenantManager.php +++ b/src/TenantManager.php @@ -51,8 +51,7 @@ public function disable() * Add a tenant to scope by. * * @param string|Model $tenant - * - * @param mixed|null $id + * @param mixed|null $id */ public function addTenant($tenant, $id = null) { @@ -106,7 +105,7 @@ public function applyTenantScopes(Model $model) $this->modelTenants($model)->each(function ($id, $tenant) use ($model) { $model->addGlobalScope($tenant, function (Builder $builder) use ($tenant, $id, $model) { - $builder->where($model->getTable() . '.' . $tenant, '=', $id); + $builder->where($model->getTable().'.'.$tenant, '=', $id); }); }); } diff --git a/tests/LandlordTest.php b/tests/LandlordTest.php index bab1e6b..db7c3c9 100644 --- a/tests/LandlordTest.php +++ b/tests/LandlordTest.php @@ -92,7 +92,7 @@ public function testNewModel() $model = new ModelStub(); $landlord->newModel($model); - + $this->assertEquals(1, $model->tenant_a_id); $this->assertNull($model->tenant_b_id); From 6df88dd230075b6afdbe43a9d4f4a36fc03d8a4b Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 11:11:13 +1200 Subject: [PATCH 08/18] Style fixes --- src/BelongsToTenant.php | 3 ++- src/LandlordServiceProvider.php | 2 +- src/TenantManager.php | 11 ++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/BelongsToTenant.php b/src/BelongsToTenant.php index 477c736..917079d 100644 --- a/src/BelongsToTenant.php +++ b/src/BelongsToTenant.php @@ -53,8 +53,9 @@ public function getTenants() * @param mixed $id * @param array $columns * - * @return \Illuminate\Database\Eloquent\Collection|Model * @throws TenantModelNotFoundException + * + * @return \Illuminate\Database\Eloquent\Collection|Model */ public static function findOrFail($id, $columns = ['*']) { diff --git a/src/LandlordServiceProvider.php b/src/LandlordServiceProvider.php index ed69629..6d3c4f7 100644 --- a/src/LandlordServiceProvider.php +++ b/src/LandlordServiceProvider.php @@ -14,7 +14,7 @@ class LandlordServiceProvider extends ServiceProvider public function boot() { $this->publishes([ - realpath(__DIR__.'/../config/landlord.php') => config_path('landlord.php') + realpath(__DIR__.'/../config/landlord.php') => config_path('landlord.php'), ]); } diff --git a/src/TenantManager.php b/src/TenantManager.php index 62af872..1251155 100644 --- a/src/TenantManager.php +++ b/src/TenantManager.php @@ -99,7 +99,7 @@ public function getTenants() */ public function applyTenantScopes(Model $model) { - if (! $this->enabled) { + if (!$this->enabled) { return; } @@ -117,12 +117,12 @@ public function applyTenantScopes(Model $model) */ public function newModel(Model $model) { - if (! $this->enabled) { + if (!$this->enabled) { return; } $this->modelTenants($model)->each(function ($id, $tenant) use ($model) { - if (! isset($model->{$tenant})) { + if (!isset($model->{$tenant})) { $model->setAttribute($tenant, $id); } }); @@ -145,8 +145,9 @@ public function newQueryWithoutTenants(Model $model) * * @param string|Model $tenant * - * @return string * @throws TenantColumnUnknownException + * + * @return string */ protected function getTenantKey($tenant) { @@ -154,7 +155,7 @@ protected function getTenantKey($tenant) $tenant = $tenant->getForeignKey(); } - if (! is_string($tenant)) { + if (!is_string($tenant)) { throw new TenantColumnUnknownException( '$tenant must be a string key or an instance of \Illuminate\Database\Eloquent\Model' ); From cc57b5a38b211b97bbcbaf4f867cb052aec64ff5 Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 11:36:12 +1200 Subject: [PATCH 09/18] Pluralize Trait Name --- src/{BelongsToTenant.php => BelongsToTenants.php} | 2 +- src/TenantManager.php | 2 +- tests/LandlordTest.php | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/{BelongsToTenant.php => BelongsToTenants.php} (98%) diff --git a/src/BelongsToTenant.php b/src/BelongsToTenants.php similarity index 98% rename from src/BelongsToTenant.php rename to src/BelongsToTenants.php index 917079d..78161d3 100644 --- a/src/BelongsToTenant.php +++ b/src/BelongsToTenants.php @@ -10,7 +10,7 @@ /** * @mixin Model */ -trait BelongsToTenant +trait BelongsToTenants { public static function bootBelongsToTenant() { diff --git a/src/TenantManager.php b/src/TenantManager.php index 1251155..0b16c08 100644 --- a/src/TenantManager.php +++ b/src/TenantManager.php @@ -168,7 +168,7 @@ protected function getTenantKey($tenant) * Get the tenants that are actually applicable to the given * model, in case they've been manually specified. * - * @param Model|BelongsToTenant $model + * @param Model|BelongsToTenants $model * * @return Collection */ diff --git a/tests/LandlordTest.php b/tests/LandlordTest.php index db7c3c9..a49a4ad 100644 --- a/tests/LandlordTest.php +++ b/tests/LandlordTest.php @@ -1,6 +1,6 @@ Date: Tue, 13 Sep 2016 12:31:04 +1200 Subject: [PATCH 10/18] Make the findOrFail method better, and rename exception --- src/BelongsToTenants.php | 9 +++++++-- ...Exception.php => ModelNotFoundForTenantException.php} | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) rename src/Exceptions/{TenantModelNotFoundException.php => ModelNotFoundForTenantException.php} (78%) diff --git a/src/BelongsToTenants.php b/src/BelongsToTenants.php index 78161d3..8c8032c 100644 --- a/src/BelongsToTenants.php +++ b/src/BelongsToTenants.php @@ -53,7 +53,7 @@ public function getTenants() * @param mixed $id * @param array $columns * - * @throws TenantModelNotFoundException + * @throws ModelNotFoundForTenantException * * @return \Illuminate\Database\Eloquent\Collection|Model */ @@ -62,7 +62,12 @@ public static function findOrFail($id, $columns = ['*']) try { return static::query()->findOrFail($id, $columns); } catch (ModelNotFoundException $e) { - throw (new TenantModelNotFoundException())->setModel(get_called_class()); + // If it DOES exist, just not for this tenant, throw a nicer exception + if (! is_null(static::allTenants()->find($id, $columns))) { + throw (new ModelNotFoundForTenantException())->setModel(get_called_class()); + } + + throw $e; } } } diff --git a/src/Exceptions/TenantModelNotFoundException.php b/src/Exceptions/ModelNotFoundForTenantException.php similarity index 78% rename from src/Exceptions/TenantModelNotFoundException.php rename to src/Exceptions/ModelNotFoundForTenantException.php index b02331b..9855326 100644 --- a/src/Exceptions/TenantModelNotFoundException.php +++ b/src/Exceptions/ModelNotFoundForTenantException.php @@ -4,7 +4,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException; -class TenantModelNotFoundException extends ModelNotFoundException implements TenantExceptionInterface +class ModelNotFoundForTenantException extends ModelNotFoundException implements TenantExceptionInterface { /** * @param string $model From c175d492c3d6766777070f9b81c1322976b6f926 Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 12:31:45 +1200 Subject: [PATCH 11/18] Rename tenantColumns to be more clear --- src/BelongsToTenants.php | 40 ++++++++++++++++++++++++++-------------- src/TenantManager.php | 14 +++++++------- tests/LandlordTest.php | 2 +- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/BelongsToTenants.php b/src/BelongsToTenants.php index 8c8032c..f0931f8 100644 --- a/src/BelongsToTenants.php +++ b/src/BelongsToTenants.php @@ -2,7 +2,7 @@ namespace HipsterJazzbo\Landlord; -use HipsterJazzbo\Landlord\Exceptions\TenantModelNotFoundException; +use HipsterJazzbo\Landlord\Exceptions\ModelNotFoundForTenantException; use HipsterJazzbo\Landlord\Facades\Landlord; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\ModelNotFoundException; @@ -12,37 +12,49 @@ */ trait BelongsToTenants { + /** + * @var TenantManager + */ + protected static $landlord; + + /** + * Boot the trait. Will apply any scopes currently set, and + * register a listener for when new models are created. + */ public static function bootBelongsToTenant() { + // Grab our singleton from the container + static::$landlord = app(TenantManager::class); + // Add a global scope for each tenant this model should be scoped by. - Landlord::applyTenantScopes(new static()); + static::$landlord->applyTenantScopes(new static()); - // Add tenants automatically when creating models + // Add tenantColumns automatically when creating models static::creating(function (Model $model) { - Landlord::newModel($model); + static::$landlord->newModel($model); }); } /** - * Returns a new query builder without any of the tenant scopes applied. - * - * $allUsers = User::allTenants()->get(); + * Get the tenantColumns for this model. * - * @return \Illuminate\Database\Eloquent\Builder + * @return array */ - public static function allTenants() + public function getTenantColumns() { - return Landlord::newQueryWithoutTenants(new static()); + return isset($this->tenantColumns) ? $this->tenantColumns : config('landlord.default_tenant_columns'); } /** - * Get the tenants for this model. + * Returns a new query builder without any of the tenant scopes applied. * - * @return array + * $allUsers = User::allTenants()->get(); + * + * @return \Illuminate\Database\Eloquent\Builder */ - public function getTenants() + public static function allTenants() { - return isset($this->tenants) ? $this->tenants : config('landlord.default_tenants'); + return static::$landlord->newQueryWithoutTenants(new static()); } /** diff --git a/src/TenantManager.php b/src/TenantManager.php index 0b16c08..0a308f3 100644 --- a/src/TenantManager.php +++ b/src/TenantManager.php @@ -28,7 +28,7 @@ public function __construct() } /** - * Enable scoping by tenants. + * Enable scoping by tenantColumns. * * @return void */ @@ -38,7 +38,7 @@ public function enable() } /** - * Disable scoping by tenants. + * Disable scoping by tenantColumns. * * @return void */ @@ -121,9 +121,9 @@ public function newModel(Model $model) return; } - $this->modelTenants($model)->each(function ($id, $tenant) use ($model) { - if (!isset($model->{$tenant})) { - $model->setAttribute($tenant, $id); + $this->modelTenants($model)->each(function ($tenantId, $tenantColumn) use ($model) { + if (!isset($model->{$tenantColumn})) { + $model->setAttribute($tenantColumn, $tenantId); } }); } @@ -165,7 +165,7 @@ protected function getTenantKey($tenant) } /** - * Get the tenants that are actually applicable to the given + * Get the tenantColumns that are actually applicable to the given * model, in case they've been manually specified. * * @param Model|BelongsToTenants $model @@ -174,6 +174,6 @@ protected function getTenantKey($tenant) */ protected function modelTenants(Model $model) { - return $this->tenants->only($model->getTenants()); + return $this->tenants->only($model->getTenantColumns()); } } diff --git a/tests/LandlordTest.php b/tests/LandlordTest.php index a49a4ad..68ae609 100644 --- a/tests/LandlordTest.php +++ b/tests/LandlordTest.php @@ -103,7 +103,7 @@ class ModelStub extends Model { use BelongsToTenants; - public $tenants = ['tenant_a_id']; + public $tenantColumns = ['tenant_a_id']; } class TenantA extends Model From 7639c7df5d4a0cb44e639254161fce278143dfeb Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 12:41:40 +1200 Subject: [PATCH 12/18] Update README --- README.md | 148 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 120 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 9c35df5..1e3e7b3 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ -# Landlord for Laravel 5.2 +# Landlord for Laravel & Lumen 5.2+ -![Landlord for Laravel 5.2](readme-header.jpg) +![Landlord for Laravel & Lumen 5.2+](https://github.com/HipsterJazzbo/Landlord/raw/master/readme-header.jpg) -![Build Status](https://travis-ci.org/HipsterJazzbo/Landlord.svg?branch=master) +![StyleCI Status](https://styleci.io/repos/49851417/shield?branch=v2.0-wip) +![Build Status](https://travis-ci.org/HipsterJazzbo/Landlord.svg?branch=v2.0-wip) -A single database multi-tenancy package for Laravel 5.2+. (Formerly https://github.com/AuraEQ/laravel-multi-tenant) +A single database multi-tenancy package for Laravel & Lumen 5.2+. ## Installation @@ -14,16 +15,20 @@ To get started, require this package: composer require hipsterjazzbo/landlord ``` -Add the ServiceProvider and Alias to their relative arrays in `config/app.php`: +### Laravel + +Add the ServiceProvider in `config/app.php`: ```php 'providers' => [ ... HipsterJazzbo\Landlord\LandlordServiceProvider::class, ], +``` -... +Register the Facade if you’d like: +```php 'aliases' => [ ... 'Landlord' => HipsterJazzbo\Landlord\Facades\Landlord::class, @@ -36,58 +41,145 @@ You could also publish the config file: php artisan vendor:publish --provider="HipsterJazzbo\Landlord\LandlordServiceProvider" ``` -and set up your `tenant_column` setting, if you have an app-wide default. +and set your `default_tenant_columns` setting, if you have an app-wide default. LandLord will use this setting to scope models that don’t have a `$tenantColumns` property set. + +### Lumen + +You'll need to set the service provider in your `bootstrap/app.php`: + +```php +$app->register(HipsterJazzbo\Landlord\LandlordServiceProvider::class); +``` + +And make sure you've un-commented `$app->withEloquent()`. ## Usage -First off, this package assumes that you have at least one column on all of your tenant-scoped tables that references which tenant each row belongs to. +This package assumes that you have at least one column on all of your Tenant scoped tables that references which tenant each row belongs to. -For example, you might have a `companies` table, and all your other tables might have a `company_id` column (with a foreign key, right?). +For example, you might have a `companies` table, and a bunch of other tables that have a `company_id` column. -Next, you'll have to call `Landlord::addTenant($tenantColumn, $tenantId)`. It doesn't matter where, **as long as it happens on every request**. This is important; if you only set the tenant in your login method for example, that won't run for subsequent requests and queries will no longer be scoped. You almost certainly will want to do this in a middleware. +### Adding and Removing Tenants -Some examples of good places to call `Landlord::addTenant($tenantColumn, $tenantId)` might be: +You can tell Landlord to automatically scope by a given Tenant by calling `addTenant()`, either from the `Landlord` facade, or by injecting an instance of `TenantManager()`. -- In a global Middleware -- In an oauth system, wherever you're checking the token on each request -- In the constructor of a base controller +You can pass in either a tenant column and id: -Once you've got that all worked out, simply `use` the trait in all your models that you'd like to scope by tenant: +```php +Landlord::addTenant('tenant_id', 1); +``` + +Or an instance of a Tenant model: + +```php +$tenant = Tenant::find(1); + +Landlord::addTenant($tenant); +``` + +If you pass a Model instance, Landlord will use Eloquent’s `getForeignKey()` method to decide the tenant column name. + +You can add as many tenants as you need to, however Landlord will only allow **one** of each type of tenant at a time. + +To remove a tenant and stop scoping by it, simply call `removeTenant()`: + +```php +Landlord::removeTenant('tenant_id'); + +// Or you can again pass a Model instance: +$tenant = Tenant::find(1); + +Landlord::removeTenant($tenant); +``` + +You can also check whether Landlord currently is scoping by a given tenant: + +```php +// As you would expect by now, $tenant can be either a string column name or a Model instance +Landlord::hasTenant($tenant); +``` + +And if for some reason you need to, you can retrieve Landlord's tenants: + +```php +// $tenants is a Laravel Collection object, in the format 'tenant_id' => 1 +$tenants = Landlord::getTenants(); +``` + +> **IMPORTANT NOTE:** Landlord is stateless. This means that when you call `addTenant()`, it will only scope the *current request*. +> +> Make sure that you are adding your tenants in such a way that it happens on every request, and before you need Models scoped, like in a middleware or as part of a stateless authentication method like OAuth. + +### Setting up your Models + +To set up a model to be scoped automatically, simply use the `BelongsToTenants` trait: + +```php + +use Illuminate\Database\Eloquent\Model; +use HipsterJazzbo\Landlord\BelongsToTenants; + +class ExampleModel extends Model +{ + use BelongsToTenants; +} +``` + +If you’d like to override the tenants that apply to a particular model, you can set the `$tenantColumns` property: ```php - 'whatever']); +``` + +### Querying Tenant scoped Models -$model = Model::find(1); // Will fail if the Model with `id` 1 belongs to a different tenant +After you've added tenants, all queries against a Model which uses `BelongsToTenant` will be scoped automatically: -$newModel = Model::create(); // Will have the tenant id added automatically +```php +// This will only include Models belonging to the current tenant(s) +ExampleModel::all(); + +// This will fail with a ModelNotFoundForTenantException if it belongs to the wrong tenant +ExampleModel::find(2); ``` -If you need to run queries across all tenants, you can do it easily: +> **Note:** When you are developing a multi tenanted application, it can be confusing sometimes why you keep getting `ModelNotFound` exceptions for rows that DO exist, because they belong to the wrong tenant. +> +> Landlord will catch those exceptions, and re-throw them as `ModelNotFoundForTenantException`, to help you out :) + +If you need to query across all tenants, you can use `allTenants()`: ```php -$allModels = Model::allTenants()->get(); //You can run any fluent query builder methods here, and they will not be scoped by tenant +// Will include results from ALL tenants, just for this query +ExampleModel::allTenants()->get() ``` -When you are developing a multi tenanted application, it can be confusing sometimes why you keep getting `ModelNotFound` exceptions. +Under the hood, Landlord uses Laravel's [anonymous global scopes](https://laravel.com/docs/5.3/eloquent#global-scopes). This means that if you are scoping by multiple tenants simultaneously, and you want to exclude one of the for a single query, you can do so: + +```php +// Will not scope by 'tenant_id', but will continue to scope by any other tenants that have been set +ExampleModel::withoutGlobalScope('tenant_id')->get(); +``` -Landlord will catch those exceptions, and re-throw them as `TenantModelNotFoundException`, to help you out :) ## Contributing -Please! This is not yet a complete solution, but there's no point in all of us re-inventing this wheel over and over. If you find an issue, or have a better way to do something, open an issue or a pull request. +If you find an issue, or have a better way to do something, feel free to open an issue or a pull request. From 6923aed447d5c6c84b926f660ed802ecf3e2ff94 Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 00:41:51 +0000 Subject: [PATCH 13/18] Applied fixes from StyleCI --- src/BelongsToTenants.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BelongsToTenants.php b/src/BelongsToTenants.php index f0931f8..252fd62 100644 --- a/src/BelongsToTenants.php +++ b/src/BelongsToTenants.php @@ -75,7 +75,7 @@ public static function findOrFail($id, $columns = ['*']) return static::query()->findOrFail($id, $columns); } catch (ModelNotFoundException $e) { // If it DOES exist, just not for this tenant, throw a nicer exception - if (! is_null(static::allTenants()->find($id, $columns))) { + if (!is_null(static::allTenants()->find($id, $columns))) { throw (new ModelNotFoundForTenantException())->setModel(get_called_class()); } From cbf179bab773f57d6a03cc4a7854f284b5a6eef3 Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 15:22:39 +1200 Subject: [PATCH 14/18] Fix trait booting --- src/BelongsToTenants.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BelongsToTenants.php b/src/BelongsToTenants.php index 252fd62..c89e911 100644 --- a/src/BelongsToTenants.php +++ b/src/BelongsToTenants.php @@ -21,7 +21,7 @@ trait BelongsToTenants * Boot the trait. Will apply any scopes currently set, and * register a listener for when new models are created. */ - public static function bootBelongsToTenant() + public static function bootBelongsToTenants() { // Grab our singleton from the container static::$landlord = app(TenantManager::class); From 7aa1677ccb526817243ff2a8d4fe10adda1a76dd Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 15:36:09 +1200 Subject: [PATCH 15/18] Added a changelog --- CHANGELOG.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f4fdf91 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,43 @@ +# Change log + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). + +## [v2.0] - 2016-09-13 +### Added +- Landlord now supports Lumen (5.2+) out of the box. +- Landlord now uses Laravel's anonymous global scopes, so you can disable scoping for one or more individual tenants for a single query using `Model::withoutGlobalScope('tenant_column')`, or `Model::withoutGlobalScopes(['tenant_column_a', 'tenant_column_b'])`. + + **Note:** `Model::allTenants()` still returns a query with *none* of the tenant scopes applied. + +- You can now pass a Model instance to `addTenant()`. Landlord will use Eloquent's `getForeignKey()` method as the tenant column name. + +### Changed +- Renamed `LandlordFacade` → `Landlord`. +- Renamed `BelongsToTenant` → `BelongsToTenants` (plural). + + **Note:** You will have to update your use statements in scoped models. + +- Renamed `TenantModelNotFoundException` → `ModelNotFoundForTenantException`. Make sure to update any `catch` statements. +- Renamed `Landlord` → `TenantManager`. + + **Note:** You will have to update any places you're injecting an instance: + +```php +//Before +public function __construct(\HipsterJazzbo\Landlord\Landlord $landlord) { + $this->landlord = $landlord; +} + +// After +public function __construct(\HipsterJazzbo\Landlord\TenantManager $landlord) { + $this->landlord = $landlord; +} +``` + +- `TenantManager` now uses an `\Illuminate\Support\Collection` instance to manage tenants internally. This has cleaned up the code a lot. + + **Note** `getTenants()` now returns the `Collection` instance instead of an array. If you need a plain array of tenants, you may call `Landlord::getTenants()->all()`. +- The service provider no longer registers the `Landlord` facade for you. You'll need to do it in your `config/app.php` if you weren't already. +- Landlord now actually checks for non-tenanted existence before throwing a `ModelNotFoundForTenantException`. \ No newline at end of file From 6ba9a3797050d8d5343c3646857df59d09f27065 Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 15:38:22 +1200 Subject: [PATCH 16/18] Update readme to reference change log --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1e3e7b3..a83f207 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # Landlord for Laravel & Lumen 5.2+ -![Landlord for Laravel & Lumen 5.2+](https://github.com/HipsterJazzbo/Landlord/raw/master/readme-header.jpg) +![Landlord for Laravel & Lumen 5.2+](readme-header.jpg) -![StyleCI Status](https://styleci.io/repos/49851417/shield?branch=v2.0-wip) +![StyleCI Status](https://styleci.io/repos/49851417/shield?branch=v2.0-wip) ![Build Status](https://travis-ci.org/HipsterJazzbo/Landlord.svg?branch=v2.0-wip) A single database multi-tenancy package for Laravel & Lumen 5.2+. +> **Upgrading from Landlord v1?** Make sure to read the [change log](CHANGELOG.md) to see what needs updating. + ## Installation To get started, require this package: From dcd40fa63bf3782053cfd485d6c479ee0bdf83a1 Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 15:52:43 +1200 Subject: [PATCH 17/18] =?UTF-8?q?Don=E2=80=99t=20use=20trait=20in=20tenant?= =?UTF-8?q?=20stubs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 3 +-- tests/LandlordTest.php | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index b63f1ff..ff0a3d4 100644 --- a/composer.json +++ b/composer.json @@ -21,8 +21,7 @@ "require-dev": { "mockery/mockery": "~0.9", "phpunit/phpunit": "5.5.*", - "laravel/framework": "5.2.*|5.3.*", - "laravel/lumen-framework": "5.2.*|5.3.*" + "laravel/framework": "5.2.*|5.3.*" }, "autoload": { "psr-4": { diff --git a/tests/LandlordTest.php b/tests/LandlordTest.php index 68ae609..a9b9696 100644 --- a/tests/LandlordTest.php +++ b/tests/LandlordTest.php @@ -108,10 +108,10 @@ class ModelStub extends Model class TenantA extends Model { - use BelongsToTenants; + // } class TenantB extends Model { - use BelongsToTenants; + // } From f27cea2c67941fc6394ee4d267b2e6f94bf96d43 Mon Sep 17 00:00:00 2001 From: Caleb Fidecaro Date: Tue, 13 Sep 2016 16:02:08 +1200 Subject: [PATCH 18/18] Move note about statelessness --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a83f207..0ecfdd0 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,10 @@ For example, you might have a `companies` table, and a bunch of other tables tha ### Adding and Removing Tenants +> **IMPORTANT NOTE:** Landlord is stateless. This means that when you call `addTenant()`, it will only scope the *current request*. +> +> Make sure that you are adding your tenants in such a way that it happens on every request, and before you need Models scoped, like in a middleware or as part of a stateless authentication method like OAuth. + You can tell Landlord to automatically scope by a given Tenant by calling `addTenant()`, either from the `Landlord` facade, or by injecting an instance of `TenantManager()`. You can pass in either a tenant column and id: @@ -108,10 +112,6 @@ And if for some reason you need to, you can retrieve Landlord's tenants: $tenants = Landlord::getTenants(); ``` -> **IMPORTANT NOTE:** Landlord is stateless. This means that when you call `addTenant()`, it will only scope the *current request*. -> -> Make sure that you are adding your tenants in such a way that it happens on every request, and before you need Models scoped, like in a middleware or as part of a stateless authentication method like OAuth. - ### Setting up your Models To set up a model to be scoped automatically, simply use the `BelongsToTenants` trait: