Now, we take the scenario of team multi-tenancy or company multi-tenancy, which means that every record should belong to a tenant, which will have many users inside.
So every user will have access to their team's records. There are multiple ways how you can implement all of that.
Generally, whether you use some package or not, you need to implement these things with multi-tenancy:
- Creating the tenant and assigning a user to the tenant. We will take the case of many-to-many relationships when a user may belong to multiple tenants.
- Switching between the tenants.
- Identify the tenant, which may be done by subdomain, session, or in other ways.
- Finally, the records should be limited by team: for example, with global scopes.
- As a bonus, you should probably have some invitation system to add more users to your team and tenant.
First, in this section of the course, we will implement it without any packages, only by manually creating the features above. So you would understand the logic of how it works.
In the following sections, we will perform the same or very similar features with packages. You will be able to compare and choose which package you like the most, or you will like implementing everything without any packages.
In this lesson, let's create the team or the tenant during registration. We have a default registration from Laravel Breeze, and after registration, we will create a tenant for a user.
First, let's create a tenant Model. The tenant will only have a name.
php artisan make:model Tenant -m
database/migrations/xxx_create_tenants_table.php:
Schema::create('tenants', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps();});
app/Models/Tenant.php:
class Tenant extends Model{ protected $fillable = ['name'];}
Also, we need a pivot table for a many-to-many relationship between users
and tenants
tables.
php artisan make:migration "create tenant user table"
database/migrations/xxx_create_tenant_user_table.php:
Schema::create('tenant_user', function (Blueprint $table) { $table->foreignId('tenant_id')->constrained(); $table->foreignId('user_id')->constrained();});
Next, in the Tenant Model, let's add the relationship to the User Model.
app/Models/Tenant.php:
use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Tenant extends Model{ protected $fillable = ['name']; public function users(): BelongsToMany { return $this->belongsToMany(User::class); }}
Now, we can create the tenant when the user registers. In Breeze, the login for registration is in the RegisteredUserController
Controller. We will name the tenant by the user's name and add a Team
suffix. When the tenant is created, we must attach it to a user.
app/Http/Controllers/Auth/RegisteredUserController.php:
use App\Models\Tenant; class RegisteredUserController extends Controller{ // ... public function store(Request $request): RedirectResponse { $request->validate([ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], 'password' => ['required', 'confirmed', Rules\Password::defaults()], ]); $user = User::create([ 'name' => $request->name, 'email' => $request->email, 'password' => Hash::make($request->password), ]); $tenant = Tenant::create(['name' => $request->name . ' Team']); $tenant->users()->attach($user); event(new Registered($user)); Auth::login($user); return redirect(route('dashboard', absolute: false)); }}
After registering in the database, we see that the tenant has been created and attached to the user.
In the next lesson, we will limit the tasks and projects to that tenant.
I think the RegisterUserController.php is no longer editable in breeze/jetstream and is abstracted out to the Fortify. How do you override this in a breeze/jetstream project?
I see that in the documentation that in app/Actions/Fortify/CreateNewUser.php I can add the create tenant right after the user but what if there are situations where you want to create a new user without creating a tenant? I'm thinking of creating a new action call CreateNewTenant.php and adding it to the FortifyServiceProvider is this the right way to go about it or am I wrong?
I think I figured it out for anyone else who has this question. I looked that how personal teams were added in jetstream --teams and made my code look similar.
It might not be the best way to do it but it worked.
unfortunately I'm not able to watch that video in Safari on iOS because of bad responsive design
Yeah, we will work on improving the responsiveness this week, sorry for the experience.