تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Creating Department Model0:00

In the last video, we made sure that we would only be able to see data that belongs to our tenant using a global scope. Now we're going to implement the rest of this functionality by making sure that all data is segmented and a user can only create data in his or her tenant. To do that, I want to add another model to this app. Now an Employee probably works in some Department, so why don't we create a Department's model. So let's go to the terminal and say php artisan make:model Department. And I want a migration with this. So let's run that command and take a look at what we get in our editor. So we have our Department model here, but I want to look at the migration that was created.

Enforcing TenantId via Stubs0:35

So let's run that command and take a look at what we get in our editor. So we have our Department model here, but I want to look at the migration that was created. And if we look at this here, one of the things that you're going to notice is we don't have the tenantId here on this model. So we could always add it, which would be an unsigned big integer for tenantId. However, what if we forget to add the tenantId to one of our models? Or what if some new developer comes in and doesn't know he needs to add this? Before Laravel 7, there really wasn't a good way to do this, but with Laravel 7, we can use Laravel Stubs to make sure that this is on all of our models. Let's take a look at the Laravel docs.

use Laravel Stubs to make sure that this is on all of our models. Let's take a look at the Laravel docs. I'm just going to get the docs for Stubs. And as this comes up, we can publish the Stubs. And all this does is it gives you basically the template that we're going to use when we create new files. So let's grab this command, copy it, and we'll run it here. And the Stubs were published successfully. So if we go back to our project, let's take a look in here and find our Stubs that were just created.

So if we go back to our project, let's take a look in here and find our Stubs that were just created. It's in the Stubs folder. You can see all of these files were created. Now specifically what I want is when I make a model, I want the migration to include an unsignedBigInteger for tenantId. So if I look at this Stub here, the migration create, this is the file that's used. So I'm going to go back to this. I want to grab this, copy it, and put it right back here in my Stub. So now let's delete the Department model and the migration and create it again.

I want to grab this, copy it, and put it right back here in my Stub. So now let's delete the Department model and the migration and create it again. So I'll go back here, delete this file, find my Department model, delete that file, and let's go back to the terminal. So if I run this command again, php artisan make:model Department --migration, and go back to phpStorm, let me go into my migrations folder. I just deleted this file, but now you can see it was created and the Stub took this and put it in the file already. So now every time I make a new model and add the migration when I create the model, I'm going to get my tenantId attached to it.

So now every time I make a new model and add the migration when I create the model, I'm going to get my tenant ID attached to it. Now if for some reason I don't need a tenant ID on this model, I can always delete it. But in this case I do, and in most cases I will. So using Stubs is a great way in this app to make sure we always have a tenant ID added to the migration when we create a model. So let's finish this up. I just also want just a string for a name of the department. And now I'm just going to migrate this. So php artisan migrate.

Building Livewire Department Form3:03

And now I'm just going to migrate this. So php artisan migrate. All that looks good. And now I just want to throw a quick form on the page so that we can create a new Department, and I'm going to do that using Livewire. So let's do php artisan make:livewire. We'll just call this the DepartmentFormComponent, because I'm going to use this for both creating a Department and editing one. Okay, now that that's created, let's go back to phpStorm. Let's find those two files that were created.

Okay, now that that's created, let's go back to phpStorm. Let's find those two files that were created. So we'll have a class that was created up here in the HTTP folder. There's now a Livewire directory. And I've got the departmentForm here. And if we go down into our resources/views, we're going to see Livewire here. We've added the departmentForm. So let's go ahead first and create a mount method on this. So this is going to take in a departmentId. And that will be null if nothing is passed in.

So this is going to take in a departmentId. And that will be null if nothing is passed in. We'll say if departmentId, then we want to set this name to department. Let's import that. findOrFail departmentId name. And lastly, we just need to add a public name variable. And lastly, I want to pull up the department form. And in here, we're just going to add a nice little input for the name that we just created. So to do that with Livewire, I'm just going to wire:model equals name. And just to make sure that this is working, let's go back to the department form.

So to do that with Livewire, I'm just going to wire:model equals name. And just to make sure that this is working, let's go back to the department form. Let's initialize this actually to be Kevin. And lastly, we're just going to put this onto our welcome.blade.php template just so we can see it. So we'll go to the welcome template. And right here where we are showing these counts, let's get rid of that. And let's just throw in Livewire DepartmentForm. And in this case, we're not going to pass anything through because we want this to be a create form.

And in this case, we're not going to pass anything through because we want this to be a create form. So if we go to the browser, let's see if it's working. If I refresh, I've got this input. It's a little bit hard to see, but it's there. And it initialized with the variable Kevin, which is what I set. So everything should be working. We do need a submit button. So let's go back to our department form blade template. And let's add a button, wire:click, we'll submit, we'll say submit.

So let's go back to our department form Blade template. And let's add a button, wire:click, we'll submit, we'll say submit. And lastly, just to make sure we can see everything's working, let's just have a div saved. And we're going to wrap this in if, success, and endif. This is going to look really ugly, but this is going to give us the functionality we want. Lastly, let's go to our department form. And we need to add that saved method. So I'm going to put that right here. And I think I actually named it submit. Let me check.

And I think I actually named it submit. Let me check. Yeah, the wire:click is submit. So I want to name this submit. And when we submit the form, we want to create a Department. So let's just, we'll skip validation for now. We'll just say Department::create(name => this->name). And of course, Kevin doesn't really make sense as a department. Let's default to accounting. So we go back to our UI and try to reload and I'm getting an undefined variable.

Let's default to accounting. So we go back to our UI and try to reload and I'm getting an undefined variable. So let's go fix that. We also have a public $success equals false. And after the submit is done, I just want to say $success equals true. So now if we reload our browser, we can see accounting here, we've got a submit button. We haven't actually put this in the database yet. So if we submit, we do have an error because our department is not fillable. So let's go to our Department model really quickly. And add protected $guarded equals array().

So let's go to our Department model really quickly. And add protected $guarded equals nothing. And let's go back here and give it one more refresh and try to submit accounting. And this is the error we would expect because every Department needs to have a tenantId, but we didn't set it. So of course, if we go back to our department form, Livewire class, we could of course add another thing here where it says tenantId is session()->get(). And we want the tenantId in the session. And so if we go back to here, let's give it a quick refresh, submit, and it was saved. So in that case, it worked.

And so if we go back to here, let's give it a quick refresh, submit, and it was saved. So in that case, it worked. But again, we don't want the User to have to remember to add this every time they are going to save any model in our application. That's going to get really repetitive and people are going to forget and keep running into that error that I just saw. So let's take this out of the developers hands and let's make sure it happens every time. So let's go to our Department model. And here, one of the things that we haven't even done yet is applied our tenant scope. So we're going to want to do that easiest way to do that would be to just go to my User

Auto-Setting TenantId on Create8:17

And here, one of the things that we haven't even done yet is applied our tenant scope. So we're going to want to do that easiest way to do that would be to just go to my User model, grab this booted function, copy it, and throw it right back in there, we'll import the tenant scope. And everything works. And the other thing we can do inside this booted method, which is really nice is hook into some lifecycle events on the model itself. So if I do a static creating, this will allow me to pass in a callback. And within this callback, I can do something before the model is actually created. So let's create our callback with function, this would be my department.

And within this callback, I can do something before the model is actually created. So let's create our callback with function, this would be my department. Let's close it up. And let's say the departmentTenantId equals session, get our tenantId. Okay, so now as long as the user is logged in, and this tenantId is set in the session, and actually, we can probably put a conditional here. So if session has tenantId, and let's just grab that and move it up. So one of the really important things to notice here is I am always setting this when the tenantId is set in the session. So even if someone tried to manually set this tenantId to something else, you know, somehow

the tenantId is set in the session. So even if someone tried to manually set this tenantId to something else, you know, somehow in the code, whether it was a mistake in the code, or some type of hacking from the front end, it doesn't matter that you could set the tenantId to be different than the User. But this is going to override it. So now let's go back to our Department model, sorry, the Livewire class. And let's delete this tenantId here. So we're back to, I just want to create the Department with the name. And we're going to let the creating model event set that tenantId for us.

So we're back to, I just want to create the Department with the name. And we're going to let the creating model event set that tenantId for us. So let's go back to the front end. And let's change this to humanResources. And when I hit submit, the saved button still works. And if you just want to see that that actually worked, if we go to our departments table, you can see humanResources here and the tenantId is now 1. And just to show you one more time, let's do to hit submit. And as I refresh, there we go, the tenantId is set. So just to show you how this is foolproof, let's go back to the code.

And as I refresh, there we go, the tenantId is set. So just to show you how this is foolproof, let's go back to the code. And like I said, let's bring the tenantId back. And let's hard code it to four or something that's wrong. It's still going to work if I put three submit. If I look at my database, still tenantId one. We're going to override whatever value was set and make sure it aligns to the logged in user. Now we have a little bit more to clean up here. Let's go back to our code.

Now we have a little bit more to clean up here. Let's go back to our code. As you might remember, we had a User model. And if we compare the User model, we just have the tenant scope here. Whereas in the Department, we have this creating. So we need to make sure that gets over to the User model. So let's copy and paste that over here. Although you can see now this is Department. So I might want to change this to be User. But I really want to avoid all of this copy pasting.

Updating Model and Factory Stubs11:21

So I might want to change this to be User. But I really want to avoid all of this copy pasting. So actually, why don't we take this change it to just be Model. So we'll take a generic Model. And in the same way we did the migrations, let's actually take this whole booted function and go put it in a stub. So if we go back to our stubs, we go down here and find the model stub. As we talked about before, every Model we really want to have this booted function. So I'm just going to put this in here. So now we're going to add the tenant scope, which we do need to import.

So I'm just going to put this in here. So now we're going to add the tenant scope, which we do need to import. So let's go back here, grab this, throw that in there. So now we've got the tenant scope applied to each new model. And when we're creating the model, we're going to set the tenantId whenever a new entry is created. And then one more thing I forgot to do is create a factory when I created my Department model. So we're going to create a new model just to test and make sure everything's working. But I also want to change this factory stub to make sure that we're always associating

So we're going to create a new model just to test and make sure everything's working. But I also want to change this factory stub to make sure that we're always associating these models to a Tenant. So let's go to my UserFactory. And I have this tenantId line here. So let's go back to our factory stub. And let's just throw that in there as well. So now let's create a new model. Let's say every employee at my company gets issued a CompanyPhone. So let's create a Phone model.

Verifying with Phone Model12:39

Let's say every employee at my company gets issued a company phone. So let's create a Phone model. We'll say php artisan make:model Phone, I want a migration and a factory. So if we do this, let's take a look at all three of those files and make sure that they're set up the way we want them to based on the stubs. If I go to the Phone model right here, you can see I've created the booted, we've got the tenant scope imported and applied. And we're going to every time a new Phone is created, apply it to this tenant, which is great. Let's go ahead and check our PhoneFactory.

is great. Let's go ahead and check our PhoneFactory. And it does have a tenantId. Now obviously, we're going to have to add more to this as we build out the Phone model. But that looks good. And lastly, create phones table. And we have the tenantId here. So this is a huge step forward. Now every time we create a model, it's structured automatically in a way that uses the design that we've created to implement this multi tenancy.

Now every time we create a model, it's structured automatically in a way that uses the design that we've created to implement this multi tenancy. So if we go back to our list of things that we were looking at before, data should be segmented by tenant in the database. Every time we create a model that's happening through the stubs, users should only see the data that belongs to his or her tenant, thanks to the global scope, and can only create data in his or her tenant, thanks to the creating model event. So in a short period of time, we've really built out our whole multi tenancy for this application. I do want to do a little bit of refactoring and add some tests in the next video.

application. I do want to do a little bit of refactoring and add some tests in the next video.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟