Model factories overview0:01
So, let's turn our attention to those model factories that we created in the last episode. They're going to come in useful not only for creating seed data for our application when we're clicking around in the browser, but also for tests, where we don't want to have to keep manually building out models when we're checking a certain flow. Now, as an example, Laravel ships with the UserFactory. We can use this as a basis for our own model factories as well. So, here's the definition. Here are all the different columns that a User has. You can see they're using the faker library in order to grab a fake name, for example, in order to grab an email address, for example. By default, well, all users are verified, but if we scroll down to the unverified method here,
in order to grab an email address, for example. By default, well, all users are verified, but if we scroll down to the unverified method here, we could chain this method on during usage to create an unverified User. So, you see how powerful model factories are for being able to quickly build up states in our application. Super useful for testing. So, what do we have? Well, of course, we have the PostFactory. So, why don't we concentrate on that first? Now, the first column we'll come to is the user_id. That is, a Post belongs to a User, and of course, at the end of the day,
Factory relationships setup1:13
Now, the first column we'll come to is the userId. That is, a Post belongs to a User, and of course, at the end of the day, this is going to be an integer, a foreign key lookup. But we don't know ahead of time what this is going to be. There might not even be a User in the database to do a lookup against. So, we need to ask Laravel, or rather we need to ask this PostFactory to create a User if we haven't specified one manually. In order to do that, we can ask for an instance of a UserFactory. You do not need to call create on this factory. If you call create on this factory, you're actually going to end up with additional Users that you didn't expect.
You do not need to call create on this factory. If you call create on this factory, you're actually going to end up with additional Users that you didn't expect. Laravel will call create for us if it's necessary. So, in this case, we can absolutely leave this just like so, and only when necessary will it be turned into a User, and then Laravel is intelligently going to extract the primary key from this User and insert it as the user ID. That's how we handle relationships inside factories. Okay, a Post also has a title. Now, we could hard code this the title, for example,
Generating post content2:17
Okay, a Post also has a title. Now, we could hard code this title, for example, but then, of course, every Post is going to have the same title, which is probably not desirable. Instead, I'm going to reach for faker, and there's a global fake function that you can make use of. So, we'll say faker, and let's say sentence to generate ourselves a nice random sentence. Now, I'm actually going to wrap this in a string helper because the sentence is going to have a full stop at the end of it, and for a title of a Post, we likely don't want that.
because the sentence is going to have a full stop at the end of it, and for a title of a post, we likely don't want that. So, I'll wrap it in the str helper, and I'll say beforeLast, and we'll search for that little dot there, and that is absolutely fine. So, now we have a sentence structure, but it's going to remove the full stop from the end. Tell you what, we could also say title, right, to turn it into title case so that it capitalizes the letters. That'll be nicely formatted. Okay, what's next?
That'll be nicely formatted. Okay, what's next? Well, a Post has a body. So, we could here say fake, and we could use something like paragraphs, and paragraphs would give us, yeah, three paragraphs in this case of fake text, but the text won't make any sense. It won't be real text. It will be lorem ipsum. It won't read properly. So, I actually prefer to reach for a method called realText,
It won't read properly. So, I actually prefer to reach for a method called realText, and you can set the number of characters. So, let's say 600 characters, okay, and now this text will actually be taken from excerpts of books. It won't make sense, but it will be real English sentences. So, it's nicer to look at, particularly when you're seeding a real database. You don't have to worry about the created_at and updated_at timestamps. Laravel is going to handle those for us.
Building CommentFactory3:52
You don't have to worry about the created at and updated at timestamps. Laravel is going to handle those for us. We do not have to specify them inside our factory. So, I think for now we are done here with the PostFactory, and we can dive into our CommentFactory. Let's handle the relationships first. So, again, we have a user_id. Well, as before, this is going to be an instance of a UserFactory. Allow Laravel to do the creation as and when necessary, and we also have a post_id.
Allow Laravel to do the creation as and when necessary, and we also have a postId. Well, now we've created our PostFactory. We can use the PostFactory. Quick side note, if you're wondering where this static factory method comes from, well, jump into your model, and you'll see we're making use of HasFactory as a trait. If you open HasFactory, sure enough, there's a static factory method on there,
If you open hasFactory, sure enough, there's a static factory method on there, and that method is in charge of doing the lookup and finding the relevant factory inside your application. So, if you don't like this way of working, well, you can access the factory directly, so PostFactory, and I'd call the new method, but I really like going through the model because it keeps in mind the fact that, yes, this is related to the Post model.
because it keeps in mind the fact that, yes, this is related to the Post model. That is the end result. I'm going to create an instance of a Post. So, there's our relationship set up. Of course, a Comment also has a body. Again, I'm going to reach for real text, and let's say 250 characters this time to keep it a little bit smaller when it comes to comments. Okay, well, with all of the columns defined on our factories,
Seeding users and login5:12
to keep it a little bit smaller when it comes to comments. Okay, well, with all of the columns defined on our factories, I think we're about ready to jump into the database seeder and, well, begin using our factories to create dummy data for our application. You can see that our database seeder already has some commented outlines. This is what ships by default with Laravel, and this first one is quite useful. So, it's going to create 10 random User instances for us.
and this first one is quite useful. So, it's going to create 10 random Users for us in our application code. I'm going to import the User so that we can clean this up a little, and there we go. So, if we run just this alone, there will be 10 Users in the database. Let's see if that's the case. I'm going to jump into my terminal,
Let's see if that's the case. I'm going to jump into my terminal, and I'm going to run php artisan db:seed. Okay, it says that the database seeding is complete. Now, if we jump into the users table and rerun this, I already have Luke Downing in there. Luke Downing already existed from our previous registration, but now note that we have 10 additional users that have been created, and if you take a look at the actual data for these users,
that have been created, and if you take a look at the actual data for these users, it is very much fake random data, but pretty cool that we've been able to fill this out that quickly. Okay, back to our application. See down here, we're creating a very specific User, so it has a set name and a set email. This is going to be super useful for being able to quickly log in to our application. So, I'm going to uncomment that as well.
for being able to quickly log in to our application. So, I'm going to uncomment that as well. I'll actually call the variable Luke, and let's set the name to my own name. I'll leave the email at test@example.com. Now, of course, if we run php artisan db:seed again, it will work, but if I run it a third time, it's going to fail, and it's going to fail because of that email constraint. The email has to be unique.
and it's going to fail because of that email constraint. The email has to be unique. So, if I have my database seeder set up correctly, I like to just do a completely fresh seed each time I change this. So, php artisan migrate:fresh, and then I'll use the seed flag to automatically seed the database at the same time. And now you can see, yeah, we have 11 users. Here are the fake 10 users, and here's the very specific 11th user with my name,
Seeding posts per user7:18
Here are the fake 10 users, and here's the very specific 11th user with my name, and test@example.com as the email address. Now, it would make sense if the users in our application had created some Posts. So, why don't we give all of these users a few Posts each? We can attach Posts to users using the has method. So, each user will have a certain number of Posts, and, of course, we create Posts using the post factory. I can actually pass a count into the factory method itself.
and, of course, we create posts using the PostFactory. I can actually pass a count into the factory method itself. So, let's say that each User has created, I don't know, 20 posts. Now, if we run php artisan migrate:fresh --seed again and open our database, if I go into posts, you can see we have 200 posts in total, and you can see the user_ids line up with the fact that each User has created 20 posts. If you take a look at the titles and the bodies for these posts,
Avoiding extra posts8:08
that each User has created 20 Posts. If you take a look at the titles and the bodies for these Posts, that's exactly as we expect, title case, and the body is real text. Obviously, it doesn't quite make sense, but it is real English text. So, we're definitely getting somewhere here. Let's now turn our attention to the comments that should be on each Post. So, obviously, we can reach for the Comment factory here.
that should be on each Post. So, obviously, we can reach for the CommentFactory here. Let's say we want 100 comments. Of course, if we just call create at this point and then go ahead and seed our database, yeah, it's going to work, but now we actually have 300 Posts. So, what's happening? What's happening is we've not said which Posts these comments belong to,
What's happening is we've not said which Post these comments belong to, so Laravel is going to jump into the comment factory. It's going to say, yep, I don't know which post it's going to be, so I'm going to create a brand new Post and assign it to that instead, and because we've asked for 100 comments, as you'd expect, we have 100 extra Posts in our database. I don't really want this.
as you'd expect, we have 100 extra Posts in our database. I don't really want this. I want the Comments to be on these Posts here, so we might just have to think about rearranging this seeder to allow that to happen. The first thing I'm going to do is I'm going to remove this hasMany. I'm going to go back to what we had before. We'll create 10 Users. Now let's declare the Posts separately, so I'll make use of the Post factory for that.
Recycling related models9:30
Now let's declare the posts separately, so I'll make use of the PostFactory for that. We still want 200 posts, and I'm going to call create, but I want to reuse or recycle the users here rather than allowing the PostFactory to create its own users, and as you would expect, Laravel has a method for that, so I'm going to call recycle, and I'm going to pass in this collection. Now what Laravel will do is it will say,
and I'm going to pass in this collection. Now what Laravel will do is it will say, okay, look, this Post expects a User. Rather than creating my own User, I'm going to use a random User from this collection you've given me, so by doing that, we automatically reuse the relationship defined here, and we don't have to worry that additional Users are going to be added into our database. Of course, when you think about it,
are going to be added into our database. Of course, when you think about it, there's nothing stopping us doing much the same with our comments, so I can say comments equals, and we have a CommentFactory, but then I'm going to recycle the users, and I'm also going to recycle the posts, so now the comments won't create any additional posts or users in our database. Shall we see if it works?
or users in our database. Shall we see if it works? php artisan migrate:fresh --seed. It says that seeding is complete. Jumping into our database, well, we only have 11 users, so that's worked. We have 200 posts, exactly as expected, and we have 100 comments. Again, exactly as expected.
and we have 100 comments. Again, exactly as expected. So the recycle method is incredibly useful, particularly when seeding, so that you don't end up with data that you didn't expect. Now for our custom User for Luke, I'm actually going to reach for those has methods again, so I'm going to say that Luke has created a bunch of Posts, let's say 45 Posts, and I've also created a bunch of comments,
let's say 45 Posts, and I've also created a bunch of Comments, so CommentFactory. Maybe I've done 120 Comments in my time, and I want to recycle the Posts, so again, rather than creating a new Post for each Comment, I'm going to pluck a random Post from this collection up here so that we're not filling our database with unexpected data. Let's rerun our seeder once more, and hopefully now, if I go to comments,
Let's rerun our seeder once more, and hopefully now, if I go to comments, and I select user ID, here is Luke, and you can see he's done loads of comments, and if I go to posts, we should see very much the same thing. Here are all of Luke's posts. Brilliant, I think we're there. We now have an application that we can immediately seed on any device. We're able to test in the browser.
Why seed before building12:02
that we can immediately seed on any device. We're able to test in the browser and see pretty much real data. We have a User that we can log in as, and we know that that will work, and the nice thing about doing things this way around, having the data in place before we start building, is that if we don't do that, we're tempted to build form first. That is, to build all of the forms in the application.
we're tempted to build form first. That is, to build all of the forms in the application so that we can create data for our database. The issue is that that doesn't follow the natural path of an application. The natural path is that we'd have our index page, then our show page, then the edit page, the create page, so by creating the data up front using seeders, using model factories, we can follow that natural path.
using model factories, we can follow that natural path, and development is going to be much smoother. Now, of course, as I've said, factories are also used for writing tests, and before we finish off this chapter, I'd very much like to take a look at the test suite that we already have running in the application. I'll see you in the next episode.
I'll see you in the next episode.
