Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Model Observer to tenant belonging models #50

Open
obrunsmann opened this issue Dec 21, 2016 · 13 comments
Open

Add Model Observer to tenant belonging models #50

obrunsmann opened this issue Dec 21, 2016 · 13 comments

Comments

@obrunsmann
Copy link
Contributor

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).

@obrunsmann
Copy link
Contributor Author

So to reproduce:

Add Landlord Trait to a model (e.g. User), add Observer to the model User::observe(UserObserver::class); and try search on it. It will ignore the tenant assignment.

@hipsterjazzbo
Copy link
Owner

This works fine for me, if I understand correctly. What exactly are you trying to do that isn't getting scoped?

@lasseeee
Copy link

lasseeee commented Mar 5, 2017

Maybe related to #45, have you tried with other models than User? Also see: #46 (comment)

@benkingcode
Copy link

Just come across this issue myself. Adding an observer to a model stops Landlord from working(!)

@pimski
Copy link

pimski commented Mar 8, 2017

It seems that the order is of essence.

If you run Landlord::addTenant() first everything works fine (the tenant conditions are applied). However if you start with MyModel::observe() the conditions aren't applied.

In the HasEvents::observe() function a new instance of the model is created (MyModel in the example above). This fires off a sequence of boot events, including the boot event used by the trait BelongsToTenants. However, the $tenants collection it needs (from TenantManager) isn't filled yet, so it can't apply the tenant conditions to the global scope. The collection is filled later, once you call Landlord::addTenant()

@hipsterjazzbo any ideas how to make this more robust? maybe adding an exception or something?

@benkingcode
Copy link

That makes sense. I set addTenant in a route middleware, and the observers are registered in AppServiceProvider as recommended by the Laravel docs.

@aluferraz
Copy link

Also happening here... Any ideas ? I would like to keep my tenants in the route middleware and the observers in the appserviceprovider as recommended ...

@hipsterjazzbo
Copy link
Owner

.@pimski is totally right about what's happening there. I agree that this should be better handled, but I'm not aware of anything I can do from within a package context to force the order like that. The only solutions I've come up with (involving various ways to set the tenants at boot time) remove the ability to add tenant dynamically, which is important functionality for me.

Ideas?

@pimski
Copy link

pimski commented Jun 7, 2017

Hi @hipsterjazzbo

An idea:
In the TenantManager add a variabele (collection/array) for models that couldn't get scope'd directly:

protected $tenants;
protected $deferredModels;

In applyTentantScopes() push a model to the deferredModels collection if the tenants haven't been set yet:

public function applyTenantScopes(Model $model)
{
	if (!$this->enabled) {
		return;
	}

	if ($this->tenants->isEmpty()) {
		// No tenants yet, defer scoping to a later stage
		$this->deferredModels->push($model);
		return;
	}

	$this->modelTenants($model)->each(function ($id, $tenant) use ($model) {
		$model->addGlobalScope($tenant, function (Builder $builder) use ($tenant, $id, $model) {
			$builder->where($model->getQualifiedTenant($tenant), '=', $id);
		});
	});
}

Finally, add a method to apply the global scope to the 'deferred models':

public function applyTenantScopesToDeferredModels()
{
	$this->deferredModels->each(function ($model) {
		$this->modelTenants($model)->each(function ($id, $tenant) use ($model) {
			$model->addGlobalScope($tenant, function (Builder $builder) use ($tenant, $id, $model) {
				$builder->where($model->getQualifiedTenant($tenant), '=', $id);
			});
		});
	});

	$this->deferredModels = collect();
}

Bit of code duplication and a lengthy name... :)

@renanwilliam
Copy link
Contributor

This solution works for me!

@OwenMelbz
Copy link

Just spent the last few hours trying to figure out why my users were not scoped - eventually commented out my User::observe(UserObserver::class); and behold - scoping worked once more!

What is the chance of the fix/workaround for this being merged soon?

Thanks :)

@hipsterjazzbo
Copy link
Owner

Okay I've tagged a beta release with the contents of #66. Could everyone please have a go, and if there are no issues after a while I'll tidy it up a bit and push it stable.

@gausam
Copy link

gausam commented Oct 1, 2017

Hey team 👋
I see mention of deferred scoping in another conversation, and it solved scoping failure for me. Works as advertised, but what's the proper use case for it (i.e ideal scenario under which I'd call (Landlord::applyTenantScopesToDeferredModels(); right after Landlord::addTenant() - can't see anything about it in the README.
Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants