در حال بارگذاری ...

Scaffolding Idea Model0:06

Now before we even get started working on the UI itself, I'd first like to get our general domain and database structure in order. So with that in mind, let's whip up some models now in real life, and this is to simulate what I would do in real life, I actually might reach for the terminal directly within my editor. So let's go ahead and run PHP Artisan. Make me a model for an idea. That's what we're working with.

So let's go ahead and run PHP Artisan. Make me a model for an idea. That's what we're working with. And what do we want here? I want a factory so I can quickly whip up dummy ideas. A form request. Sure, a migration, a policy to control authorization and a controller. That would be helpful. And that all looks good. So we'll give about a run and we get all of these files. Next, let's switch over to the migration itself. Alright, so what does an idea consist of?

Designing Ideas Migration0:49

Next, let's switch over to the migration itself. Alright, so what does an idea consist of? Well, we've talked about this a little bit in earlier videos. We probably need a title for the idea and that's required. But you know what, we're gonna set it up where everything else is optional. So next I'm gonna have a text column for the description itself, but this can be nullable.

So next I'm gonna have a text column for the description itself, but this can be nullable. Next, uh, let's go ahead and do this. Every idea belongs to one person. So let's say table, give me a foreign ID for our user model That will be constrained. And once again, cascade on delete. This is all a recap at this point. Next, we know that an idea can have a list of links. So here's how I'm gonna handle that.

Next, we know that an idea can have a list of links. So here's how I'm gonna handle that. Uh, steps are gonna be stored in their own table, whereas links are simply gonna be js. So let's use a JSON column for links. And I'm just gonna default this to, uh, an empty array. Alright, what else? Uh, a status. There needs to be a status for each, uh, idea. Is it pending? Is it in progress? Is it completed? So why don't we set that right above.

Is it pending? Is it in progress? Is it completed? So why don't we set that right above. I'm just gonna have it as a string. Uh, you could use an enum as well. Sometimes I find them a little annoying, so I'll just use a simple string. The status and the default will be pending. Okay? And then finally, anything else? Um, an image, right? A path to the featured image. So I can say tables, string image,

A path to the featured image. So I can say tables, string image, path, featured image, whatever you want. However, that can also be knowable. All right, so let's bring back our terminal and run. Migrate. All right, so now we have a new ideas table and we have a corresponding idea model. Now, while I'm here, I'm gonna do some initial project configuration. For example, when the application boots, I'm gonna say model

Configuring Eloquent Defaults2:43

project configuration. For example, when the application boots, I'm gonna say model on guard, I don't want it to provide, uh, protection against, uh, passing request all to an update method or something like that. I can be responsible. So I'm not going to, uh, require any guarding. Next, I'm gonna ask it to be strict. So this is going to enable just a handful of things. It's gonna prevent lazy loading so that if we have, um,

So this is going to enable just a handful of things. It's gonna prevent lazy loading so that if we have, um, it'll basically protect us against m plus one issues. It will prevent us from accessing, uh, eloquent model attributes that don't exist, things like that. Uh, I usually enable this for every new project. And then next on that note, we can automatically eager load relationships, which again, um, solves the n plus one problem.

we can automatically eager load relationships, which again, um, solves the n plus one problem. If it detects that we are basically in a situation where we're loading a relationship within a loop, things like that, it will handle automatically eager loading that for us, which is so cool. All right, that looks good. Next, let's return to my idea model. Let's add some casting. And as you'd expect, this allows us

Adding Enum Status Casting3:47

Let's add some casting. And as you'd expect, this allows us to cast value from the database query into the proper format. So for example, links, uh, that's basically stored as a string there, or Jason, but when I grab it, I want to interact with it as if it were an array of links. So I can use as array object for that. Um, status. You'll remember that can be pending in progress

So I can use as array object for that. Um, status. You'll remember that can be pending in progress or completed, but if you're not careful, you, you're referencing those values throughout the entire code base. And then if you add a new one, you have to update that value every single place it was referenced. So instead, I would rather create a dedicated enum. And as it turns out, if I run my make command, there should be an enum and there is cool.

And as it turns out, if I run my make command, there should be an enum and there is cool. So PHP Artisan make Enum and it's just a plain old PHP class. Uh, it's gonna be called idea status. It's a back to enum. And yeah, if these are new to you, that's okay. Real quick, I'm gonna bind this to our enum, and this means the value that, uh, exists, that field pending in progress. We're actually gonna cast that into an enum here.

that field pending in progress. We're actually gonna cast that into an enum here. Okay, so if we open this up, let's set up our various options that, uh, an idea status can be. For example, one of them was pending, right? And this will be the actual string that's in the database field. Uh, we have one for in progress and this is how we store it. And then the final one is completed.

Uh, we have one for in progress and this is how we store it. And then the final one is completed. And I think we just have three here. So now if you'll gimme 20 seconds, I'll show you how this works. Uh, in practice, if I bring up the terminal, let's boot up our tinker, uh, PHP artisan tinker, and I'll create a new idea instance. And the first thing I wanna show you is when we're starting from scratch, if I run idea status,

And the first thing I wanna show you is when we're starting from scratch, if I run idea status, that will be set to null. But remember how in our migration we wanted the default to be pending. Well, that's on the database end, but on the eloquent end, we don't have an initial value. So put a pin in that for just a second. But if I do set it to, let's say in progress, you'll notice that now if I fetch status, it's actually, uh,

But if I do set it to, let's say in progress, you'll notice that now if I fetch status, it's actually, uh, provided to us as an enum as you see right here. So I'll show you something. Cool. Let's add a method here. Maybe a specialized label that we can use, uh, in the UI or as part of a a pill or something like that. Let's call it label. They'll return a string. So what we can do is return a match on this. So I can say like if, okay, if the current value for this enum is uh, pending,

So I can say like if, okay, if the current value for this enum is uh, pending, then we're gonna set the label to pending. But this can be anything you want, right? It can be currently or now pending. I'm just gonna provide three here. Um, again, now in progress, currently in progress, in progress, however you want to format it completed. And this is also to show you that you can call methods on your enums,

And this is also to show you that you can call methods on your enums, which ends up being incredibly helpful. Alright? So check this out, bring back the terminal. I think we'll need to refresh this. I can press up to once again cycle through previous commands. Now if I grab the status, cool, but now I can also say, well give me a label for that current status and you'll see it gives us the label.

but now I can also say, well give me a label for that current status and you'll see it gives us the label. Great, this is really useful. So now if I return to my idea model, we can use an enum cast. Incredibly helpful. Alright, next, we've decided that an idea belongs to a user, right? So this is going to be a belongs to relationship. So let's return this belongs to user. Next, what about the idea of ideas and steps?

Creating Steps Relationship7:27

So let's return this belongs to user. Next, what about the idea of ideas and steps? And steps are effectively tasks that are associated with an idea, but we're gonna use this term step. What are the steps to complete and achieve this idea? Okay, well it sounds like that should be a relationship as well. And let's think, what would that relationship be? Well, can I an idea have one step? Yes. Can it have many steps? Yes.

Well, can I an idea have one step? Yes. Can it have many steps? Yes. Can each step belong to any idea? No. Each step is kind of linked to one specific idea. So of course in this case, we have a, has many relationship, an idea can have many step, but at the moment we don't have a step model. So that's our next step. Let's comment to this. Bring back our terminal, and then once again, let's make a new model for a step.

Bring back our terminal, and then once again, let's make a new model for a step. Alright, so for a step, yeah, let's go with, we can keep this a little simpler. Just a factory and a migration for now. And oh, we have a mistake. Okay, let's fix that real quick. And I'm sure you saw that. Cool. So now if we go into our steps table, we're gonna keep this very simple. This is gonna be a simple step, uh,

we're gonna keep this very simple. This is gonna be a simple step, uh, or string that represents maybe a description. And then I also want a Boolean that indicates whether or not this step has been completed. And then we will have the relevant timestamps. Cool. So once again, I can migrate my database and yeah, we should be in good shape. So now I can bring this back and let's import this. There we go. And update our type. Cool.

So now I can bring this back and let's import this. There we go. And update our type. Cool. So an idea has the necessary testing. It belongs to a specific user and it can also can have, it also can have one or more steps. Cool. Now if I go to this step end, let's do the inverse. So first step, if I want to grab the idea associated with this step, well the step belongs to the idea, right? So this would be a belongs to relationship return.

with this step, well the step belongs to the idea, right? So this would be a belongs to relationship return. This belongs to idea. And then at the moment, I don't think we have a step belonging to a user, but we might wanna add support for that at some point. For now we won't. Okay, next I'm gonna go to my user model. And if I scroll down, let's do the inverse here. A user can have many ideas, right? So this will be a has many relationship

A user can have many ideas, right? So this will be a has many relationship and I can say this has many idea. Cool. A user can have many ideas. Each idea belongs to a user represented here. Each idea can have one or more steps. And each step of course belongs to an idea. I think we have our relationships in order. So now I'd like to wrap up by adding some initial factory dummy data

Building Factories and Tests10:21

So now I'd like to wrap up by adding some initial factory dummy data and I'll paste these in so you don't have to watch. Alright, so here's what I have for you for user id. We can reference a user factory. So whenever I build up an idea, it will also build up a user and then update this to be the ID of the user that was scaffolded. Next title will be a sentence description is a paragraph. Links will start with an array with just one URL. Okay?

Next title will be a sentence description is a paragraph. Links will start with an array with just one URL. Okay? So now if we were to try this out, check this out. PHB, artisan tinker, I can say app models, idea factory. And I'm just gonna call raw to get an array of items here and let's do this idea. There we go. Alright. So sure enough we get some dummy data, but notice the status.

So sure enough we get some dummy data, but notice the status. I don't see that here. Okay, so here's one thing we can do on my idea model. I can set the attributes here. So this will be the initial attributes that are set for the model. So in the case of status, I'm gonna set this to pending, but remember we have an enum for this, right? And I wanna avoid a situation

but remember we have an enum for this, right? And I wanna avoid a situation where I keep referencing the status, um, strings all throughout my code base, uh, for reasons already discussed. So instead I'm gonna use my idea status and this will be pending. Alright? So now once again, if we try this one more time, I'm gonna run, uh, idea factory. Let's do make to create an eloquent instance.

I'm gonna run, uh, idea factory. Let's do make to create an eloquent instance. And now if I do idea status, I'm gonna get pending. Even though on our factory I didn't, uh, explicitly set one because it's being done on the model. I want that as a default no matter what for fake data or even real data, that should be the default. Cool. Finally, do we have a step factory? Yes, we do. And this is very easy. Description is a fake sentence

And this is very easy. Description is a fake sentence completed and we'll just set that to false. Finally, the idea id will reference the idea factory. All right, so now at this point I haven't even loaded a browser, but I think everything's in order, but I don't know for sure. So I'm gonna write some initial tests just to confirm that these various relationships are working as I'd expect. Now, once again, I can use the terminal

that these various relationships are working as I'd expect. Now, once again, I can use the terminal to run the make command. However, you should also be able to use a artisan make, uh, extension in code or PHP storm if you're using that or cursor. So whatever you like most, in our case, we're gonna make a test. Why don't we call this idea test? Alright, so sure enough,

Why don't we call this idea test? Alright, so sure enough, it creates a new file within my test unit directory. This is a model test, but we can call it a unit test. That's fine. So I'm gonna start by saying it belongs to a user, right? So let's say if I were to have a factory, uh, for an idea, well, I should be able to access the user like this. So I could say expect idea user to be an instance of my user model.

So I could say expect idea user to be an instance of my user model. Cool. So we are literally in this case inserting an idea into the database and then we access a relationship using eloquent. And if we did everything correctly, we should get a user instance in response. However, if I were to run this, I can just access that file directly. You'll, you'll see it fails

I can just access that file directly. You'll, you'll see it fails with facade root has not been set. This is just because I'm in a unit test, but I actually am testing eloquent. So I'm gonna go to my pest root file. And two things. I want to bootstrap the framework basically within my feature tests, but also my unit tests in this case. And I also want to refresh the database. So, um, populate the database, how I need

And I also want to refresh the database. So, um, populate the database, how I need to insert some records. But before the next test, let's wipe that clean or refresh the database so that we're always starting from scratch. Okay? So now if I were to run this again, we get green because we know it works. However, if IA, for example, did not have this relationship here, well

However, if IA, for example, did not have this relationship here, well of course if we run this, it's gonna fail. So now the test is proving that this relationship is available to us. Alright, let's go back to idea test. I'll do a couple more with you for this one. Let's say it can have steps. Alright, so initially, let's see this idea factory does not have any steps.

Alright, so initially, let's see this idea factory does not have any steps. And if I run Step factory that has a description completed and it belongs to an idea, but you know what, it just occurred to me in our migration, we forgot to set up that relationship. A step belongs to an idea. So we need to give me a foreign ID for the idea. And I bet as you were watching that, you're like, Jeff, you're forgetting an important piece of the puzzle

And I bet as you were watching that, you're like, Jeff, you're forgetting an important piece of the puzzle and we'll do once again, just constrained and cascade on delete. Cool. So yeah. Now if I were to come back initially, if I try to access the steps, that should be null. 'cause there aren't any steps at the moment or at the very least it should be an empty collection, in fact.

or at the very least it should be an empty collection, in fact. So let's do this. Uh, let's bring up my terminal. Let's run test. And yes, of course, um, we got a collection, um, an empty collection, but I actually said no, which was, which was wrong of me. So I can say to be a instance of collection and this is the database collection. Alright? So that's an option here.

collection and this is the database collection. Alright? So that's an option here. And if I were to give this a run, uh, we're both gonna get green, however, let's bring this down a little bit. Uh, another thing we could do is to say, to be empty, and this is maybe more, more likely what I would write in this situation. When I grab steps, I'm gonna get an empty collection. However, we can add some steps.

When I grab steps, I'm gonna get an empty collection. However, we can add some steps. So I can say idea and let's just interact with it the way we would in real life as part of our controller code, let's say idea steps. And let's create a new one where the description is, um, do the thing. All right? Now, if I were to check the number of steps, and I'm gonna have to refresh the idea so I get fresh, um, a fresh relationship from the database

and I'm gonna have to refresh the idea so I get fresh, um, a fresh relationship from the database that's gonna have exactly one uh, item there. Okay? So let's give this a run and oh, it fails. Let's have a look. Ah, looks like steps completed cannot be null. All right, so let's do this. Let's go back to our migration and we'll say, yeah, this is already, the tests are guiding us here. It's letting us know, hey, you thought you could ignore this

the tests are guiding us here. It's letting us know, hey, you thought you could ignore this because it would default to, to false or something, but no, it's not nullable. All right, let's set a default to false. All right? And then I'm gonna do the same thing up here with that little attributes trick. I can say attributes, and let's always say completed should initially be set to false.

and let's always say completed should initially be set to false. Alright, so we've, we wrote a test, the test failed, we made some changes. Now if we run it again, it passes. Okay, next, uh, quick little UI note, look for an extension called, uh, what is it? I think it's Better Pest from Miguel, who's very active in the Laravel space. Um, this makes it easier to run your tests.

who's very active in the Laravel space. Um, this makes it easier to run your tests. So for example, if I just put my cursor within the test method, I can press command T to instantly run this test only. Okay? So once again, this is the sort of thing I would do in real life. I'd write some code or write a test. I'd hit command T and I would run the test. Cool. Okay, so I don't wanna get bogged down too much.

I'd hit command T and I would run the test. Cool. Okay, so I don't wanna get bogged down too much. We will be running tests in this course, but you know, in real life you can spend hours working on this piece of the puzzle and we do not have that time. So I'm feeling good about our database structure and our initial domain model, uh, quote unquote, we have some tests to prove that these relationships work. And the next episode, let's start working on the UI itself.

we have some tests to prove that these relationships work. And the next episode, let's start working on the UI itself.

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