Академический Документы
Профессиональный Документы
Культура Документы
Business
Code
Design
M obile
Home
Code
Search...
Multi-Tenancy in Laravel 4
Multi-Tenancy in Laravel
4
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
Circle us on Google
Plus
Subscribe via RSS
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
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
/**
* 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();
}
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
/**
* 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';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$this->app->bind('Cribbb\Contexts\Context',
{
return $app['context'];
});
'Cribbb\Contexts\ContextServiceProvider'
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());
});
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
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
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(
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
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,
}
}
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);
}
});
Conclusion
Phew! We covered a lot in this tutorial. Hopefully if
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
29
33
Like
Tweet
Comments Community
Recommend 3
Sort by Best
Login
a year ago
Reply Share
Philip Brown
Mod
a year ago
Eduardo WB
a year ago
Reply Share
Philip Brown
a year ago
Mod
> Eduardo WB
vince
a year ago
Reply Share
2 months ago
Philip Brown
Mod
2 months ago
> Neel
Philip Brown
a year ago
Mod
> vince
a year ago
Philip Brown
Mod
>
a year ago
Philip Brown
Mod
>
Daniel Bethel
a year ago
Reply Share
Philip Brown
Mod
a year ago
Mike Fuller
a year ago
Reply Share
Philip Brown
Mod
a year ago
Thanks Mike :)
Hmm, I'm not 100% sure. I know that
global scopes are used for soft deletes,
Fasil K K Cholakkara
a year ago
Reply Share
Philip Brown
Reply Share
Reply Share
Philip Brown
Mod
Jhaura Wachsman
a year ago
>
Philip Brown
Mod
Fasil K K Cholakkara
a year ago
>
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
Reply Share
Philip Brown
Mod
Fasil K K Cholakkara
a year ago
>
Guest
a year ago
Reply Share
a year ago
Philip Brown
a year ago
Mod
> Guest
John Hamman
a year ago
Reply Share
Philip Brown
Mod
a year ago
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
a year ago
Amazing, thanks!
Reply Share
Carlos
a year ago
Reply Share
Philip Brown
Mod
a year ago
> Carlos
Reply Share
2 months ago
Philip Brown
Mod
2 months ago
> Michael
moimichel
5 months ago
Philip Brown
Mod
5 months ago
> moimichel
Sanket Sahu
10 months ago
Philip Brown
Mod
10 months ago
YELLOW FLAG