hipsterjazzbo / landlord Goto Github PK
View Code? Open in Web Editor NEWA simple, single database multi-tenancy solution for Laravel 5.2+
License: MIT License
A simple, single database multi-tenancy solution for Laravel 5.2+
License: MIT License
I added a ScopeRequestByUser
middleware like so
public function handle($request, Closure $next)
{
if ($request->ajax() || $request->wantsJson()) {
Landlord::addTenant('user_id', Auth::guard('api')->id());
}
return $next($request);
}
Now this works perfectly when I add this to my global middleware stack like this.
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\ScopeRequestByUser::class,
];
However, I need it to be applied only on the api middleware group. When I add to the group in the middleware file it does not work.
protected $middlewareGroups = [
'web' => [
...
],
'api' => [
'throttle:60,1',
'bindings',
\App\Http\Middleware\ScopeRequestByUser::class,
],
];
I even tried adding it ot the route middleware and then calling it both in the routes file and in the api middleware group. Both of them are not working.
protected $routeMiddleware = [
...
'scope' => \App\Http\Middleware\ScopeRequestByUser::class,
];
Not sure, what is going wrong here.
https://github.com/HipsterJazzbo/Landlord/blob/master/src/Landlord.php#L93
here you are adding all tenants with id to the builder and the result should look like:
select * from ... where id=anton and id=lukas
and that cant work?!
I think it should be $builder->orwhere ...
Or am I wrong?
Hey guys!
I started using implicit model binding and LandLord isn't working anymore! I think it is happening because when the model binding do their job I didn't set LandLord tenant yet! Somebody know any trick to do that?
There is how my code works now:
public function __construct()
{
$this->middleware('auth:tenant');
$this->middleware(function ($request, $next) {
LandLord::addTenant('tenant_id', auth()->user()->tenant_id);
return $next($request);
});
}
public function show(Lead $lead)
{
try {
// It gives the result without filtering my Tenant!
// So I can access any tenant's lead!
return $lead;
} catch (\Exception $e) {
return response($e->getMessage(), $e->getCode() || 500);
}
}
It would be really great to see a small example application for this project to better understand how to implement it the right way, thank you very much!
Hi,
The idea is the following. A Teacher
model can belong to multiple Schools
at the same time.
Having school_id
on the teachers
table will not be enough.
Is it possible to support storing tenant values in a pivot table ?
I'm trying to test features that use Landlord but the scope is not applied unless I call $landlord->applyTenantScopes(new Model);
manually before query.
Simple failed test example:
/** @test **/
function scopeTest()
{
$user = User::create([
'name' => 'User name',
'email' =>'test@domain',
'password' => bcrypt('secret'),
'remember_token' => str_random(10),
]);
Landlord::addTenant($user);
Account::create([
'name' => 'Account name',
'user_id' => $user->id,
]);
Account::create([
'name' => 'Account name',
]);
$count = Account::count();
$this->assertEquals(1, $count);
}
I can fix easy the test calling applyTenantScopes()
but I can't fix real tests that are using controllers, repositories...
Hi, I was wondering of it possible to use tenant_id based on hostname from a table. Can this package auto detect the hostname? I am terribly in need of this package and will need a quick ans if possible. Thanks in advance 👍
only scoped model with respect to what column will be specified on model.
protected $tenant_column = ['company_id'];
Unique validation is not working with tenant_id
Hi, I'm having this error:
'Non-static method HipsterJazzbo\Landlord\Landlord::addTenant() should not be called statically, assuming $this from incompatible context'...
When I try to put that line in a middleware
Landlord::addTenant('com_id', 2);
If we ever use a booted model in the queue and we switch tenant afterwards, the tenancy is kept in place and will use the old tenant id. We need to find a way to re-boot the models.
In the documentation it says I can set a site wide default tenant column but the app/landlord.php
file doesn't seem to do anything.
'default_tenant_columns' => ['tenant_id'],
I still have to specify the $tenantColumn
in my middleware or anywhere else I want to use it.
public function handle($request, Closure $next)
{
$tenantColumn = 'tenant_id';
$tenantId = '1';
Landlord::addTenant($tenantColumn, $tenantId);
return $next($request);
}
Hey,
It seems like it's not possible to modify the Tenant ID after it's been set once.
Currently we're setting in a Scope Middleware, that's globally applied, and it works perfectly.
But we have a few instances where we'd like to change/modify the Tenant ID to be something else, if we need to run artisan commands or other console related commands.
This is what i've tested with:
`\DB::enableQueryLog();
User::whereActivated(1)->get();
$this->info(json_encode(\Landlord::getTenants()));
\Landlord::disable();
User::whereActivated(1)->get();
$this->info(json_encode(\Landlord::getTenants()));
\Landlord::removeTenant('company_id');
User::whereActivated(1)->get();
$this->info(json_encode(\Landlord::getTenants()));
\Landlord::addTenant('company_id',123);
User::whereActivated(1)->get();
$this->info(json_encode(\Landlord::getTenants()));
\Landlord::addTenant('company_id',123);
User::allTenants()->whereActivated(1)->get();
$this->info(json_encode(\Landlord::getTenants()));
dd( \DB::getQueryLog() );`
And this is the response i'm getting:
{"company_id":71}
{"company_id":71}
[]
{"company_id":123}
{"company_id":123}
array:5 [
0 => array:3 [
"query" => "select * fromusers
whereactivated
= ? andusers
.company_id
= ? andusers
.deleted_at
is null"
"bindings" => array:2 [
0 => 1
1 => 71
]
"time" => 0.72
]
1 => array:3 [
"query" => "select * fromusers
whereactivated
= ? andusers
.company_id
= ? andusers
.deleted_at
is null"
"bindings" => array:2 [
0 => 1
1 => 71
]
"time" => 0.82
]
2 => array:3 [
"query" => "select * fromusers
whereactivated
= ? andusers
.company_id
= ? andusers
.deleted_at
is null"
"bindings" => array:2 [
0 => 1
1 => 71
]
"time" => 0.44
]
3 => array:3 [
"query" => "select * fromusers
whereactivated
= ? andusers
.company_id
= ? andusers
.deleted_at
is null"
"bindings" => array:2 [
0 => 1
1 => 71
]
"time" => 0.49
]
4 => array:3 [
"query" => "select * fromusers
whereactivated
= ? andusers
.deleted_at
is null"
"bindings" => array:1 [
0 => 1
]
"time" => 0.42
]
]
As seen, the Tenant ID is the same for all of them, except after i've called "allTenants()", it correctly removed the tenant-scope, but that's not what i want to achieve.
Is this an issue with this package, or something deeper in Eloquent's way of handling the global scopes?
In addition to this, Disable() does not seem to make any difference at all either.
There is a documentation error. Docs say to use "use HipsterJazzbo\LaravelMultiTenant\Traits\BelongsToTenant;" but it returns error because directory doesn't exist. Correct usage based on current directory is "use HipsterJazzbo\Landlord\BelongsToTenant;"
I have trying to access the Landlord::getTenants() tenants in laravel command file but this returns nothing.
Hi !
How are you ? Hope you are fine !
It appears that we cannot upgrade to Lumen 5.4 because of Landlord's "illuminate/support 5.2.*" dependecy.. Any plans for supporting Lumen 5.4 ?
Thank you
It seems like the tenant id column is ignored when updating a model, it only goes by the ID of the model.
Is there any way to make it update WHERE tenant_id = x as well?
This is a real issue for me when I use multi tenant (1 db) and also have "grouped" tables, example below.
system_id | id |
---|---|
1 | 1 |
1 | 2 |
2 | 1 |
Does anyone have a work around for this problem?
Regards,
Daniel
My setup has 2 different tenant columns: site_id
and season_id
. For the large majority of things I want both of them. However there are a few queries where I actually want to ignore the season_id
tenant while still keeping the site_id
. Is there an easy way to do that that I'm just not seeing?
It looks like I might be able to pull it off but it would involve globally removing it, doing the query, and then globally adding it back in as soon as it's done. That's just not pretty.
Hi guys,
Please, can you tell me how to make the scope of my queries with the package using polymorphism relationship?
Like how to set the type (tenant class) and the id (tenant id).
Thanks in advance!
The TenantModelNotFoundException doesn't fire.
Do I need to catch it somewhere?
If so; where and how?
See service provider:
https://github.com/HipsterJazzbo/Landlord/blob/master/src/LandlordServiceProvider.php
I don't think config_path()
is available in Lumen at all.
Since 5.1 is the Long-Term Support version of Laravel, I'm wondering if it would be appropriate to support that as well? What features have been included in 5.2 that aren't in 5.1?
This is a very important concern for LTS-only organizations who don't want to upgrade Laravel every 6-12 months.
In my application, i have a permissions called "elevate-tenant" that allows an user to select any tenant from the system to be associated with the registry. This permission is usually applied on admin roles.
If i call addTenant()
from a middleware, i will get to replace the tenant calling addTenant()
again or it will create another tenant ?
Laravel 5.5 is out! 😄 Please update Package!
Argument 1 passed to Illuminate\Database\Eloquent\Model::addGlobalScope() must be an instance of Illuminate\Database\Eloquent\ScopeInterface, instance of HipsterJazzbo\Landlord\Landlord given, called in /home/vagrant/Code/lawcus-dev/vendor/hipsterjazzbo/landlord/src/BelongsToTenant.php on line 23 and defined
I'm not sure if I've made a mistake in the set up of my application, but it doesn't apply the scope when I call Landlord::addTenant() in middleware, whereas if I hardcode it in the routes file, it applies it.
It seems that this is because all the models using the BelongsToTenants trait have already been instantiated before the middleware is run, so that when TenantManager::applyTenantScopes() calls the modelTenants() method, the tenants array is always empty.
AFAIK this was not an issue in v1.
I'd be grateful of any help!
Edit: my current workaround is calling bootBelongsToTenants() on the model again after addTenant():
Landlord::addTenant('company_id', $company->id);
\App\Models\Site::bootBelongsToTenants();
Receiving the following error:
Declaration of HipsterJazzbo\Landlord\Exceptions\TenantModelNotFoundException::setModel() should be compatible with Illuminate\Database\Eloquent\ModelNotFoundException::setModel($model, $ids = Array)
Using Laravel 5.3.19 on PHP 7.0.6
attention: stupid question department
newbie wants to know if he should even attempt to use package with cakephp3 ?
ie. any advice or docs to assist
The trait to use in models is BelongsToTenant
, not BelongsToTenants
like in readme
Hello @hipsterjazzbo, how how do implement this for a subdomain tenancy? Thanks
Any plan to support for Lumen?
I just made a few change at the LandlordServiceProvider.php
, but I do not know if this is the lumen way or not.. Seems it works for me...
Here is what I've done so far:
<?php
namespace HipsterJazzbo\Landlord;
use HipsterJazzbo\Landlord\Facades\LandlordFacade;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Application as LaravelApplication;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\ServiceProvider;
use Laravel\Lumen\Application as LumenApplication;
class LandlordServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application events.
*
* @return void
*/
public function boot()
{
// setup config
$source = realpath(__DIR__ . '/../config/landlord.php');
if ($this->app instanceof LaravelApplication && $this->app->runningInConsole()) {
$this->publishes([$source => config_path('landlord.php')]);
} elseif ($this->app instanceof LumenApplication) {
$this->app->configure('landlord');
}
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton(Landlord::class, function () {
return new Landlord();
});
// Define alias 'Landlord'
if ($this->app instanceof LaravelApplication && $this->app->runningInConsole()) {
$this->app->booting(function () {
$loader = AliasLoader::getInstance();
$loader->alias('Landlord', LandlordFacade::class);
});
}
}
}
Hey Guys,
everything works fine: If I query with Model::find(123)
on my model and the resource does not belong to the current tenant (set via middleware) the request fails.
But: If I query on the User model it does not work. While asking for User::all()
I get really all results including other customers and while querying User::findOrFail()
with an ID which does not belong to the current tenant I get the result .. so everytime working with my user model I have to check if $model->tenant_id == $request->user()->tenant_id
If I call findOrFail on my model.
The documentation does not make it quite clear where you would put the call to Landlord::addTenant(), althought I assume in some sort of middleware, however It would be great if a couple of examples of real world usage would be placed in the docs.
I'd like to create my own scope based off of the Landlord scope.
Unfortunately, it seems that enabled and disabled are private so I can't bail out of apply early.
The reason I'd like this functionality is I have a scenario where I'd like optional tenant scoping on a table. I.e. there are roles in system that are shared globally (basically presets), and then there are roles that are tied to specific tenants. Roles that are shared globally do not have a tenant_id but they're on the same table.
Hi.
Just started using your package, and I really enjoy it so far!
Since Laravel 5.3 is being released soon (and I'm building a application), I tried to use landlord on the newest beta. However this package is dependent on 5.2.*, and therefore fails to install.
What's the current plan (and potential hurdles) we'll have to overcome to make this compatible with laravel 5.3?
(I have tried to make a local fork and have successfully installed Landlord on 5.3 (by changing the composer.json), but I haven't been able to test the functionality further. I will report back when I get to it.)
I ran into a route model binding issue with my Tenant Middleware so I decided to forego that. I'm also using a Sluggable package and don't want my URLs to show the index value. I tried firstOrFail with a slug condition but it simply returned a ModelNotFoundException.
Is there any way to make this work beyond doing an extra query to look up the ID based on slug?
If I add a model observer to a model which belongs to a tenant (like https://laravel.com/docs/5.3/eloquent#observers) the tenant filter is getting ignored.
Listening to events works fine (https://laravel.com/docs/5.3/eloquent#events).
Great work on Landlord, however, it does not seem to work with ORM relationships.
My member model has a tenant column called site_id. I have a another model called Profile that has a relationship to Member, but no site_id.
I'm calling Landlord::addTenant('site_id', $data['siteId']) in my middleware.
class Member extends Model
{
use BelongsToTenant;
}
class Profile extends Model
{
public function member()
{
return $this->hasOne('Models\Member');
}
}
If I tried to find a record using a profile id, the Landlord tenant code does not protect the reference to member.
$profile = Profile::find(1)
$profile->member // returns the member record regardless of the tenant column set.
Am I missing something or is there a way to address this?
...
'Landlord' => HipsterJazzbo\Landlord\Facades\Landlord::class,
...
Can we use this package with Single Database but with prefix Table name for every tenant?
I'm using laravel 5.3 :
ErrorException in Arr.php line 351:
array_flip(): Can only flip STRING and INTEGER values!
in Arr.php line 351
at HandleExceptions->handleError('2', 'array_flip(): Can only flip STRING and INTEGER values!', 'C:\wamp\www\still-reaches-22650\vendor\laravel\framework\src\Illuminate\Support\Arr.php', '351', array('array' => array('account_id' => '1'), 'keys' => array(null)))
at array_flip(array(null)) in Arr.php line 351
at Arr::only(array('account_id' => '1'), array(null)) in Collection.php line 652
at Collection->only(null) in TenantManager.php line 177
at TenantManager->modelTenants(object(Client)) in TenantManager.php line 106
at TenantManager->applyTenantScopes(object(Client)) in BelongsToTenants.php line 29
at Client::bootBelongsToTenants()
at forward_static_call(array('App\Client', 'bootBelongsToTenants')) in Model.php line 330
at Model::bootTraits() in Model.php line 316
at Model::boot() in Model.php line 303
at Model->bootIfNotBooted() in Model.php line 284
at Model->__construct() in Model.php line 647
at Model::all() in HomeController.php line 35
at HomeController->index()
... and so on
I installed and configured Landlord, and created a route to test if it was working. The use case is very simple: I have a User model and an Account model, where a User can have multiple Accounts. The Accounts table has a "user_id", and the Eloquent relationship is defined in both models as well.
// app/Http/routes.php
Route::get('test/{id}', function($id) {
Landlord::addTenant('user_id', $id);
return view('test')->with('accounts', App\Account::all());
});
// app/Account.php
namespace App;
use Illuminate\Database\Eloquent\Model;
use HipsterJazzbo\Landlord\BelongsToTenant;
class Account extends Model
{
use BelongsToTenant;
...
}
When I access that route, I'm still getting the accounts for all the users. Debugbar shows the SQL query:
select * from `accounts`
Any ideas on what's missing?
Hello,
I just started using Landlord for a new project and things were going well until I started using Laravel's route model binding. When using route model binding I have any record returned whether part of the tenant's records or not when using route model binding. If I simply pass the id into the function and perform my own findOrFail($id) then everything works as it should and trying to access those records not part of the tenant's fail. I have a middleware class that sets the tenant applied on the Route::group as only some of my routes need multi-tenant support.
My guess is that I'm implementing something incorrectly, but I'd like to know if this is a workable configuration or I simply can't used route model binding.
This is a great product.
But I don't think that this works with Implicit Binding. For eg
Doesn't work
public function show(Lead $lead)
{
return $lead;
}
This works:
public function show($id)
{
return Lead::find($id);
}
Kindly check
I have nested resources for Account -> has many Templates
I added Landlord to my Account controller and when I checked the DB log, I see the tenant condition added. When I did the same for the Template Controller, I don't see a check when I find the account. Here is my show code
public function show($account_id, $template_id)
{
DB::enableQueryLog();
$account = Account::findOrFail($account_id);
DB::disableQueryLog();
dd(DB::getQueryLog());
It returns
array:1 [
0 => array:3 [
"query" => "select * from accounts
where accounts
.id
= ? limit 1"
"bindings" => array:1 []
"time" => 0.85
]
]
My middleware that I confirmed is being passed through is the following
$tenantId = Auth::user()->id;
Landlord::addTenant('user_id', $tenantId);
Why would it work in my AccountController but not in my TemplateController. The only difference I see is the nesting.
Hi!
Just like the tittle says, validation rules are not being scoped.
This results in errors when, for example, a unique
rule is applied and there is already a same value from another Tenant.
I added Landlord::addTenant($tenantColumn, $tenantId) to the Authenticate middleware, since I wanted this on every authenticated page.
I get: Undefined variable: tenantColumn
I have a Role
model. I've publish the config file, set the model to use the trait, and also made sure that tenant_id
exists in the roles
table. I've also called Landlord::addTenant('tenant_id', $tenant->id);
in my group middleware.
When I dump getTenants()
on the page I get:
Illuminate\Support\Collection Object ( [items:protected] => Array ( [tenant_id] => 1 ) )
Well when I run this simple code:
$roles = Role::all();
foreach ($roles as $role) {
echo $role->name.'<br>';
}
It is echoing the roles that belong to any tenant. This does not even work at all.
Could it be nice to also have a trait that supports many-to-many relation between tenant and another resource?
For instance if a have a table of features
and each tenant can activate/deactivate that feature? Pivot table would hold feature_id
and tenant_id
for instance.
Would be nice when getting a list of features
that these are automatically scoped by tenant by a trait, say BelongsToManyTenants
perhaps?
… or is this better solved in some other way I have not thought of?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.