Вы находитесь на странице: 1из 19

Culttt

Business
Code
Design

M obile

Home

Code

Search...

Multi-Tenancy in Laravel 4

Multi-Tenancy in Laravel
4

Join the Culttt


Become an insider
and join the Culttt
Yo u r e ma i l ...

POSTED BY PHILIP BROWN ON MARCH 31ST, 2014

Join us

Random
S UP P O RTE D B Y

Reviews

Become a
follower

Social

Follow us on Twitter

Strategy

Like us on
Facebook

Multi-tenant applications are where you create a


single application that is used independently by many
different clients. When a client authenticates with your
application, they are only able to access the data that
they have created in your application and cannot see
any data that was created by any of your other clients.
A typical example of a multi-tenant application is
Basecamp. Basecamp customers can only see their
own projects, but Basecamp is a single application
that is used for all customers.
Multi-tenant applications have become increasingly
popular over the last couple of years due to the rise of
Software as a Service and the move towards
delivering applications through the cloud. It is now
much more common for an application to be web
based rather than installed on a clients physical
hardware.
In this tutorial Im going to walk you through setting up
the core components to allow you to create multitenant applications in Laravel 4.
You might be thinking, what has this got to do with
building Cribbb?! . You would be correct in thinking
that multi-tenancy has no place in consumer social
web applications. It would be extremely weird to build
that type of application using a multi-tenant approach.
However I think this is an important topic that would
be interesting to a lot of you building your own
Software as a Service businesses. Seeing as though

Circle us on Google
Plus
Subscribe via RSS

Im doing this in Laravel it also makes sense to


include it in this series.
So for one week only we will diverge from the path of
building a social consumer application, and instead
delve into the murky depths of Software as a Service.

How do Multi-Tenant
applications work?
As I mentioned in the introduction to this post, multitenant applications are a single installation of an
application that allows many different clients to
access their data independently.
There are a few different approaches to managing the
separation of data in multi-tenant applications.
Firstly, you could use a separate database for each
client. This means the connection to the correct
database is established on authentication. Using
separate databases ensures that data from one client
is never mixed with the data of another.
Secondly you could use a separate schema. This is
where you use one database but the database is able
to manage the separation of data through the schema.
This basically means that every client has its own set
of tables within the same database.
And thirdly, you could use a shared database with a
shared schema where you select data from the
database using a tenant_id field.
The choice between the three different approaches
really comes down to what type of application you a
building. If you are building a high security application
for a regulated industry, it makes sense to have
physical separation of data through isolated
databases.
However if you are building a low cost Software as a
Service application, you can sacrifice this security
measure for lower cost infrastructure and overheads.
Maintaining separate databases or even a separate
schema will be significantly more costly to run and
maintain.
My chosen approach is the shared database and
shared schema application. I think the benefits of
lower overheads and easier maintainability greatly
outweigh the security implications for the majority of
multi-tenant applications. However, if you were

building software for banks, hospitals or the


government you will probably want to use the
separate database approach.

How do shared database multitenant applications work?


So now that Ive established what are multi-tenant
applications and what are the different application
architecture choices you have, Ill explain how my
chosen approach works.
In a typical Software as a Service application a client
will authenticate and then be presented with their
unique dashboard filled with their data and none of
the data from any of the other clients.
When the client authenticates our application will set
a global context that will scope all database queries
to only return data for that current client. This means
that all the code of the application is unaffected
because the data is scoped at the database.
There are usually many different ways to authenticate
into an application. For example you could require
client credentials for your application but also offer
token based authentication via an API. In either case
we want a solution that will work for any type of
authentication so the database scoping logic is not
repeated and therefore less prone to errors or bugs.

Creating the Context Interface


The first thing I need to do is to create the Context
service that will act as the scope of the application.
This is basically just a global object that will be
injected into other aspects of the code (for example
the Repositories) to restrict data to only the current
client.
The Context is simply a wrapper around the entity
object of the current client. However, because
business rules often change over the life of an
application, its important to make the context open for
substitution.
To enable this substitution I will create an interface for
the context like this:
?

1
2
3
4
5
6

<?php namespace Cribbb\Contexts;


use Illuminate\Database\Eloquent\Model;
interface Context {

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

/**
* Set the context
*
* @param Illuminate\Database\Eloquent\Model
*/
public function set(Model $context);
/**
* Check to see if the context has been set
*
* @return boolean
*/
public function has();
/**
* Get the context identifier
*
* @return integer
*/
public function id();
/**
* Get the context column
*
* @return string
*/
public function column();
/**
* Get the context table name
*
* @return string
*/
public function table();
}

As you can see above, the client entity will be


injected into the service. The methods on the service
are available as a generic public API for working with
the context in our application. This is important
because, for example, if instead we need to substitute
a $client entity for a $organisation entity, we
would have to replace every instance of client_id
for organisation_id .

Creating the Context


implementation
Next I can create the specific Context implementation.
In this case my context is a Client entity. The
implementation is just a class that fulfils the
requirements of the Context interface:
?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

<?php namespace Cribbb\Contexts;


use Illuminate\Database\Eloquent\Model;
class ClientContext implements Context {
/**
* The current context
*
* @var Illuminate\Database\Eloquent\Model
*/
protected $context;
/**
* Set the context
*
* @param Illuminate\Database\Eloquent\Model
*/
public function set(Model $context)
{
$this->context = $context;
}

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

/**
* Check to see if the context has been set
*
* @return boolean
*/
public function has()
{
if($this->context) return true;
}

return false;

/**
* Get the context identifier
*
* @return integer
*/
public function id()
{
return $this->context->id;
}
/**
* Get the context column
*
* @return string
*/
public function column()
{
return 'client_id';
}
/**
* Get the context table name
*
* @return string
*/
public function table()
{
return 'clients';
}
}

The importance of the IoC


container
Now that Ive created the generic Context interface
and a specific implementation for the Client
model, I now need to bind the two together in
Laravels IoC container so that when I call for an
instance of Cribbb\Contexts\Context I will be
returned an instance of
Cribbb\Contexts\ClientContext . This is
exactly the same as how the
EloquentUserRepository is returned when you
write UserRepository in your Controllers.
However, there is one more important detail. The
Context object should be shared through the
application as a single instance. When I set the
context through the authentication, I want the same
object to be available in the Repositories. To ensure
that the same object is shared throughout the
application for each request I will use the shared
method of the IoC container.
Here is my ContextServiceProvider :
?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

<?php namespace Cribbb\Contexts;


use Illuminate\Support\ServiceProvider;
class ContextServiceProvider extends ServiceProvider {
/**
* Register
*/
public function register()
{
$this->app['context'] = $this->app->share(function
{
return new ClientContext;
});

$this->app->bind('Cribbb\Contexts\Context',
{
return $app['context'];
});

Finally add the following line to your list of Service


Providers in app/config/app.php :
?

'Cribbb\Contexts\ContextServiceProvider'

Authenticating and setting the


context
Now that you have the global Context object in your
application you can set the context when you
authenticate the request by injecting the context and
setting the database entity into the Context object.
This could be as simple as extending the inbuilt
Laravel authentication filter to set the context, or
creating your own API filter to authenticate requests
based upon a token and a secret. The choice of how
you implement authentication is really up to you.
For example, you could modify the existing Laravel
auth filter like this:
?

1
2
3
4
5
6
7
8

Route::filter('auth', function()
{
$context = App::make('Cribbb\Contexts\Context'
if (Auth::guest()) return Redirect::guest('login'
$context->set(Auth::user());
});

Here Im resolving the context out of the IoC container


and setting the user entity.
This is really open to work however you need it to
work. If you authenticate against an organisation, just
set the organisation entity instead of the user entity.

Creating the Tenant Repository

Now that the Context is set we can use it to scope the


database requests in the Repositories. To do this I
will create another Repository named
TenantRepository which extends the base
AbstractRepository .
For each repository that needs to be scoped per client
I then extend the TenantRepository rather than
the AbstractRepository directly. This enables
my repositories to all inherit the basic blueprint, but
the repositories that need to be scoped can inherit an
extra layer of logic.
In the TenantRepository I create a couple of
methods that wrap my basic building block methods
to scope the query if applicable. A scoped
relationship could either be any of the different types
of database relationship. For example it could be
based upon a foreign id column or it could be through
a pivot table. In the majority of cases you will only
need to scope through a column. I think it is rare that a
multi-tenant application would have resources that
are available to many clients. However Ive included
those methods in the example below to show you
how you would achieve that using Laravels query
builder.
I also dont want to create different repositories for
scoped access and not scoped access. For example,
if I wanted to create a dashboard to monitor the
application I dont want to have to create a second set
of repositories that werent scoped per client. You will
notice that I check to see if the scope has been set to
achieve this:
?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

<?php namespace Cribbb\Repositories;


use StdClass;
use Illuminate\Database\Eloquent\Builder;
abstract class TenantRepository extends AbstractRepository {
/**
* Scope a query based upon a column name
*
* @param Illuminate\Database\Eloquent\Builder
* @return Illuminate\Database\Eloquent\Builder
*/
public function scopeColumn(Builder $model)
{
if($this->scope->has())
{
return $model->where($this->scope->column(),
}
}

return $model;

/**
* Scope the query based upon a relationship
*
* @param Illuminate\Database\Eloquent\Builder
* @return Illuminate\Database\Eloquent\Builder
*/

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

public function scopeRelationship(Builder $model


{
if($this->scope->has())
{
return $model->whereHas($this->scope->table(),
{
$q->where($this->scope->column(), '=',
});
}
}

return $model;

/**
* Retrieve all entities through a scoped column
*
* @param array $with
* @return Illuminate\Database\Eloquent\Collection
*/
public function allThroughColumn(array $with
{
$entity = $this->make($with);
}

return $this->scopeColumn($entity)->get();

/**
* Retrieve all entities through a scoped relationship
*
* @param array $with
* @return Illuminate\Database\Eloquent\Collection
*/
public function allThroughRelationship(array
{
$entity = $this->make($with);
}

return $this->scopeRelationship($entity)->get();

/**
* Find a single entity through a scoped column
*
* @param int $id
* @param array $with
* @return Illuminate\Database\Eloquent\Model
*/
public function findThroughColumn($id, array
{
$entity = $this->make($with);
}

return $this->scopeColumn($entity)->find($id

/**
* Find a single entity through a scoped relationship
*
* @param int $id
* @param array $with
* @return Illuminate\Database\Eloquent\Model
*/
public function findThroughRelationship($id,
{
$entity = $this->make($with);
}

return $this->scopeRelationship($entity)->find(

/**
* Get Results by Page through scoped column
*
* @param int $page
* @param int $limit
* @param array $with
* @return StdClass Object with $items and $totalItems for pagination
*/
public function getByPageThroughColumn($page
{
$result
= new StdClass;
$result->page
= $page;
$result->limit
= $limit;
$result->totalItems = 0;
$result->items
= array();
$query = $this->scopeColumn($this->make($with
$users = $query->skip($limit * ($page - 1))
->take($limit)
->get();
$result->totalItems = $this->model->count();
$result->items
= $users->all();
}

return $result;

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

/**
* Get Results by Page through scoped relationship
*
* @param int $page
* @param int $limit
* @param array $with
* @return StdClass Object with $items and $totalItems for pagination
*/
public function getByPageThroughRelationship(
{
$result
= new StdClass;
$result->page
= $page;
$result->limit
= $limit;
$result->totalItems = 0;
$result->items
= array();
$query = $this->scopeRelationship($this->make(
$users = $query->skip($limit * ($page - 1))
->take($limit)
->get();
$result->totalItems = $this->model->count();
$result->items
= $users->all();
}

return $result;

/**
* Search for a single result by key and value through a scoped column
*
* @param string $key
* @param mixed $value
* @param array $with
* @return Illuminate\Database\Eloquent\Model
*/
public function getFirstByThroughColumn($key
{
$entity = $this->make($with);
}

return $this->scopeColumn($entity)->where(

/**
* Search for a single result by key and value through a scoped relationship
*
* @param string $key
* @param mixed $value
* @param array $with
* @return Illuminate\Database\Eloquent\Model
*/
public function getFirstByThroughRelationship(
{
$entity = $this->make($with);
}

return $this->scopeRelationship($entity)->where(

/**
* Search for many results by key and value through a scoped column
*
* @param string $key
* @param mixed $value
* @param array $with
* @return Illuminate\Database\Eloquent\Collection
*/
public function getManyByThroughColumn($key,
{
$entity = $this->make($with);
}

return $this->scopeColumn($entity)->where(

/**
* Search for many results by key and value through a scoped relationship
*
* @param string $key
* @param mixed $value
* @param array $with
* @return Illuminate\Database\Eloquent\Collection
*/
public function getManyByThroughRelationship(
{
$entity = $this->make($with);
}
}

return $this->scopeRelationship($entity)->where(

Extending the Tenant Repository


Now that Ive got the abstract TenantRepository
set up I can extend it to create my individual
repositories.
For example, if this was an application like Basecamp
I might have a ProjectRepository that was
scoped through a column. My repository might look
something like this:
?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

<?php namespace Cribbb\Repositories\Project;


use Cribbb\Contexts\Context;
use Illuminate\Database\Eloquent\Model;
use Cribbb\Repositories\TenantRepository;
class EloquentProjectRepository extends TenantRepository
/**
* @var Model
*/
protected $model;
/**
* @var Context
*/
protected $scope;
/**
* Construct
*
* @param Cribbb\Contexts\Context $scope
* @param Illuminate\Database\Eloquent\Model $model
*/
public function __construct(Model $model, Context
{
$this->model = $model;
$this->scope = $scope;
}
/**
* Return all projects
*
* @param array $with
* @return Illuminate\Database\Eloquent\Collection
*/
public function all(array $with = array())
{
return $this->allThroughColumn($with);
}
/**
* Return a single project
*
* @param array $with
* @return Illuminate\Database\Eloquent\Model
*/
public function find($id, array $with = array
{
return $this->findThroughColumn($id, $with);
}
/**
* Get Results by Page
*
* @param int $page
* @param int $limit
* @param array $with
* @return StdClass Object with $items and $totalItems for pagination
*/
public function getByPage($page = 1, $limit = 10,
{
return $this->getByPageThroughColumn($page,
}
/**
* Search for a single result by key and value
*
* @param string $key
* @param mixed $value
* @param array $with
* @return Illuminate\Database\Eloquent\Model
*/
public function getFirstBy($key, $value, array

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

{
}

return $this->getFirstByThroughColumn($key,

/**
* Search for many results by key and value
*
* @param string $key
* @param mixed $value
* @param array $with
* @return Illuminate\Database\Eloquent\Collection
*/
public function getManyBy($key, $value, array
{
return $this->getManyByThroughColumn($key,
}
}

Testing the context


Now that you have set up everything, we can do a
dirty little test to ensure everything is working
correctly. First seed your database with enough data
to clearly show that your tests are working correctly.
By this I mean a couple of clients and lots of projects.
Next create a new test route you can hit and copy the
following code:
?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Route::get('/test', function()
{
$client = Client::find(1);
$context = App::make('Cribbb\Contexts\Context'
$context->set($client);
$repository = App::make('Cribbb\Repositories\Project\ProjectRepository'
$projects = $repository->all();
foreach($projects as $project)
{
var_dump($project->title);
}
});

In the example above Im grabbing the first client and


resolving the Context out of the IoC container. Next
I resolve the ProjectRepository out of the IoC
container and call the all method to select all the
available projects.
Now when you hit the test route in the browser you
should see a list of available projects. However, when
you change which client you select from the database
and hit the route again the project list will
automatically change.
Congratulations! Youve just set up automatically
scoping repositories for your new Software as a
Service application!

Conclusion
Phew! We covered a lot in this tutorial. Hopefully if

youve been following a long with this series you will


see that the individual building blocks of what Ive
implemented in this tutorial have mostly been covered
in previous tutorials. It just goes to show that if we
learn and understand the individual building blocks,
we can build some pretty powerful applications.
In this tutorial weve covered a couple of interesting
things. Firstly, creating a Context that wraps an
existing object and provides an easily accessible
public API. This approach makes it really easy to
substitute how you want to scope your application if
the business rules of your company change in the
future.
Secondly we looked at setting the scope through
authentication. There are many different ways to
authenticate an application. You now have the basic
simple building blocks for authenticating and setting a
global scope that can be used through the
application.
And thirdly I showed you how to add another layer of
inheritance to add scoping logic to the repositories in
your application that need to be scoped. This can
optionally be applied to whichever repositories that
you want to be scoped, but also offers you the
flexibility to ignore the scope or to scope different
types of database relationships.
As I mentioned at the top of this post, this multi-tenant
architecture has no place in Cribbb. However
hopefully you can use this as a building block or
inspiration to build your own multi-tenant applications
for your company or for your clients.
This is a series of posts on building an entire Open
Source application called Cribbb. All of the tutorials
will be free to web, and all of the code is available on
GitHub.
To view a full listing of the tutorials in this series, click
here.

Philip Brown
Hey, I'm Philip Brown , a designer and developer from
Durham, England. I create websites and web based
applications from the ground up. In 2011 I founded a
company called Yellow Flag. If you want to find out more

about me, you can follow me on Twitter or Google Plus.

29

33

Like

Tweet

Join the Culttt


Become an insider and join the Culttt
Yo u r e ma i l ...
Join us

Comments Community

Recommend 3

Sort by Best

Login

Join the discussion


John Carter

a year ago

Please Please Please Please Please Please with


a watermelon and cherry on top of the
watermelon, create a tutorial that dives into multitenancy for multi databases! I want to use
Laravel fluently with Multitenancy with testing,
etc. I will pray that God makes you live the best
life every if you do this!!!
4

Reply Share

Philip Brown

Mod

a year ago

> John Carter

Hi John, sorry no plans to write tutorial(s)


on multi tenancy with multiple databases.
Perhaps in the future.
Reply Share

Eduardo WB

a year ago

This post came in good time as we will build a


Software as Service application. Nice approach
to solve the multi-user problem. Thank you!
4

Reply Share

Philip Brown

a year ago

Mod

> Eduardo WB

Thank you Eduardo :)


Reply Share

vince

a year ago

I really must have missed something big in this


tutorial. I do not understand all the complexity
when all you want to do is isolate the data that
belongs to a specific tenant based on their login
credentials. What is meant by "Context" - do you
mean the id of the user and their data when you
might have 200 people all called John Smith ? I
got thoroughly lost on this tutorial. Regardless I
would really, really like to be put straight as this is
a very important topic for me. Thanks !!

a very important topic for me. Thanks !!


1

Reply Share

Neel > vince

2 months ago

I agree with vince.. The first 2 para made


sense and I though geez its easy. When
the context part came, my head started
spinning and didnt make much sense.
Unfortunately for a newbie in laravel, it
was a bit too hard to understand. Thanks
for the tutorial though since I got the
overview. However, I feel its not newbie
friendly though.
Reply Share

Philip Brown

Mod

2 months ago

> Neel

Yeah Multi-tenancy isn't really a


newbie topic. I'd come back to this
once you've spent more time with
Laravel.
Reply Share

Philip Brown

a year ago

Mod

> vince

Yes the "Context" is the current


authenticated User / Organisation.
By using this approach you scope the
data at the data access layer so only data
for the current Context is returned. This
means you don't have to have any logic in
any of your Controllers or Services for
restricting data retrieval.
Reply Share

vince > Philip Brown

a year ago

ok got it. Can you give me a


bullited list of things I should learn
in order to understand this topic to
it's best. Thanks !
Reply Share

Philip Brown

Mod

vince a year ago

>

Hmm, well everything you


need is in this blog post.
I would take a look at this
talk

The code is Ruby, but it's


the exact same principle.
Reply Share

vince > Philip Brown

a year ago

heah Philip, that's great . I


am watching it already.
However, as I am a
Laravel Newbie. I have
already done a lot of
studying on Laravel,
however, as a best and
braces, can you please
give be a list of core
**Laravel** technologies I
need to understand well in
order to get the best out of
your article.
thanks !
Reply Share

Philip Brown

Mod

vince a year ago

>

Well there isn't anything


that is specific to Laravel
really. I guess you just
need to understand that
the Context is set during
authentication. You then
resolve the Context from
the IoC container and
inject it into the Repository.
The Repository then
scopes all database
queries so you don't have
to deal with that logic in
your Controllers or
Services.
Reply Share

Daniel Bethel

a year ago

Where is the AbstractRepository that the tenant


repo extends?
1

Reply Share

Philip Brown

Mod

a year ago

> Daniel Bethel

It would just be a generic abstract


repository, like the one in this tutorial
http://culttt.com/2014/03/17/e...
Reply Share

Mike Fuller

a year ago

Thank you for post, very helpful. However, I just


started working with Laravel so pardon me if this
is a stupid question. Does Laravel's global
scopes http://laravel.com/docs/eloque... make
some of this code unnecessary?
Thanks
1

Reply Share

Philip Brown

Mod

a year ago

> Mike Fuller

Thanks Mike :)
Hmm, I'm not 100% sure. I know that
global scopes are used for soft deletes,

global scopes are used for soft deletes,


but I haven't really looked into them too
much to be honest.
Reply Share

Fasil K K Cholakkara

a year ago

This is what im looking for.Thank you very


much..
Why cant put a sample application in github? + a
demo. All can use this as a base for their simple
SAAS applicatins.
1

Reply Share

Philip Brown

Mod > Fasil K K


Cholakkara a year ago

Hmm, I guess I could. Everything you


need is in this post though :)
1

Reply Share

Jhaura Wachsman > Philip


Brown a year ago

Nice post :-) I'd like to see the


schema or migrations. I think that
would be super helpful.
1

Reply Share

Philip Brown

Mod

Jhaura Wachsman
a year ago

>

Hmm, well it would just be


the normal schema and
migrations but with a
tenant_id, nothing
complicated :)
Reply Share

Fasil K K Cholakkara > Philip


Brown a year ago

Trouble in username validation.:( I


have to keep it like unique
username for whole application.
(Cant do for each tenancy in
shared db)
Reply Share

Philip Brown

Mod

Fasil K K Cholakkara
a year ago

>

Hmm, yeah you would


have to either write your
own validation rule (could
be tricky) or implement it
as a separate check in
your code.
1

Reply Share

Fasil K K Cholakkara
> Philip Brown
a year ago

I cant do uniqueness
based on tenant. Because
there is a chance for
getting same username in
other tenancy. Accidentally
if those 2 users(same
username) used same
password, then the big

password, then the big


issue will get while login.
The only solutions is i think
is to pass Tenant id also,
while login.
Reply Share

bmadigan > Fasil K K


Cholakkara
a year ago

I thought of this as well, but


I think if you used
subdomains for each
tenant that may help
1

Reply Share

Philip Brown

Mod

Fasil K K Cholakkara
a year ago

>

Well when you


authenticate users you
need to be checking to
ensure that they are a
member of the current
tenant. You wouldn't want
to allow a user to sign into
to another customer's
account.
Reply Share

Guest

a year ago

Anyone have any guidance on going down the


road of custom domains for clients under this
model? For example if I give every user a
subdomain what would be the best method for
allow them to set a DNS record on their own
domain to be masked back to the SaaS app
subdomain? CNAME record on their end but any
ideas on how to handle it on the server side?
1

Reply Share

Justin > Guest

a year ago

A wildcard vhost/nginx configuration is


what you're looking for. So that your web
server/app responds to ANY domain
trying to access it and then setting which
tenant/customer it is. Hopefully that helps.
:)
Reply Share

Philip Brown

a year ago

Mod

> Guest

Hmm, I'm not sure actually. I guess you


could just set the scope in the same way
as you would for a subdomain, but you
are using the whole domain instead.
Reply Share

John Hamman

a year ago

Above and beyond outstanding series! I have


learned so much...
So my question is: instead of a client_id, I have
an account_id with an account holding multiple
users (many users to one account). And so I
would like to put the account ID in the url as in
domain.com/123456/ and use that to identify the

domain.com/123456/ and use that to identify the


scope. How do you suggest I go about doing that
after handling the context in auth?
1

Reply Share

Philip Brown

Mod

a year ago

> John Hamman

Thanks John :D
I normally create a filter and attach it to
the routes I want to set the scope on. The
filter will just take the id from the URL, and
find the entity from the database and then
set the scope.
I've only ever done that using
subdomains, but it will work in the exact
same way for url params.
Something like this perhaps...
Route::filter('auth.scope',
function($route, $request)
{
$repository = App::make('your
repository');
$id = Request::segment(1);
$entity = $repository->find($id)
if(! $entity)
{
return Redirect::route('login');
}
$scope = App::make('your scope');
$scope->set($entity);
});
Reply Share

John Hamman > Philip Brown

a year ago

Amazing, thanks!
Reply Share

Carlos

a year ago

This is exactly what i was looking for. Thanks a


bunch!
1

Reply Share

Philip Brown

Mod

a year ago

> Carlos

No problem, glad it helped you out!


1
Michael

Reply Share

2 months ago

Maybe I'm missing something here but why don't


you just use eloquent relationships all tied back
to your User model? The foreign key
relationships will already scope your data for you.
Reply Share

Philip Brown

Mod

2 months ago

> Michael

This is essentially what is happening


here. This approach means you don't
have to be constantly scoping each
query.
Reply Share

moimichel

5 months ago

Just want to thank you for this "rich" tutorial.


Reply Share

Philip Brown

Mod

5 months ago

> moimichel

No problem, glad you found it useful!


Reply Share

Sanket Sahu

10 months ago

Great Article there! It helped me a lot. I did a


quick hack, instead of overloading all the
functions, I just overloaded the newQuery()
function.
Reply Share

Philip Brown

Mod

10 months ago

> Sanket Sahu

Thanks Sanket :) Glad you liked it :)


Reply Share

YELLOW FLAG

Вам также может понравиться