Prompting ChatGPT Reviews0:00
Okay, before we wire this up, if I jump into posts and we go into any of these posts that have been generated by our seeder, oh, it's not going to be good in the long run, is it? This is going to look so out of date, and I don't want to have to create posts manually each and every time I want a realistic-looking post. So, we either need to create a load of real posts ourselves and then use those to seed our database, or we could ask maybe a large language model to do the work for us. I am in no way a ChatGPT expert, but I have come up with the following prompt that is reasonably good, so I'll go through how I did it. First of all, I tell ChatGPT that it's a professional movie critic. This primes it for the type of content it should produce.
First of all, I tell ChatGPT that it's a professional movie critic. This primes it for the type of content it should produce. I then tell it exactly what it will be used for, in this case, posting posts on Framerate. And finally, I give it a list of rules that it has to follow. And if you don't give it a list of rules, then your results will vary quite wildly and won't be consistent. So, for example, I say, look, it has to use Markdown. It has to have a level one heading. And what we'll do is we'll separate that level one heading from the body to create the title and the body of our posts. It includes a bullet point of takeaways. It includes a popular quote from the movie, and it has to be formatted as a Markdown block quote.
It includes a bullet point of takeaways. It includes a popular quote from the movie, and it has to be formatted as a Markdown block quote. So, just adding these little flares that will make the articles interesting. What else do I do? I say it can't include images. I ask it to link, if possible, to the IMDB posting or to the character posting on Wikipedia. And then, finally, I say, well, write me a review for the following movie. Why don't we go ahead and say The Incredibles? So, here we are, ChatGPT writing me a review for The Incredibles. And while that's working, we'll go into our database directory, Factories.
Creating Markdown Fixtures1:56
So, here we are, ChatGPT writing me a review for The Incredibles. And while that's working, we'll go into our database directory, Factories. Let's create a new directory under here called Fixtures. And then maybe another subdirectory called Posts. And then in there we can have a file called the_incredibles.md. Okay, so maybe ChatGPT's done by now. Looks great. Let's copy it. Paste it in. And there we go. We have our first real fake fixture post.
Implementing withFixture Method2:26
And there we go. We have our first real fake fixture post. I'm going to use the magic of editing to create a bunch more. And just like that, we have all of these posts to be used by our database Cedar. Currently, our database Cedar calls PostFactory here and here. And I'm thinking we could create a new method, perhaps called withFixture, that will go ahead and grab these posts and make use of them for these 200 factories or for these 45 factories. Let's think about how we can implement this. So, here's the withFixture method on the PostFactory. And, well, the first thing we need to do is grab the contents of all of these posts here.
So, here's the WithFixture method on the PostFactory. And, well, the first thing we need to do is grab the contents of all of these posts here. So, let's say posts equals, and we can collect. We could use the File facade for this. Illuminate\Support\Facades\File, files, database_path. And we're looking, obviously, for factories/fixtures/posts. If we dump and die that out and I go ahead and run php artisan db:seed. Yeah, there we go. Here's our collection. And each item is an SPLFileInfo.
Here's our collection. And each item is an SPLFileInfo. It's using the Symfony component under the hood. So, instead of dump and die, why don't we map that and we'll grab the SPLFileInfo. And I'm pretty sure SPLFileInfo has getContents. Yes, it does. So, now, if we dump and die this out, what we're actually going to receive is an array or a collection with all of the text content, the string content of each of those markdown files. So, we'll stop there. And let's go ahead and grab a random Post.
So, we'll stop there. And let's go ahead and grab a random post. So, $post equals Post::random(). And then we want the title and the body. And we've already said that the title is going to be this first line here, obviously, minus the # space seen as it's not markdown. And then the rest of it is going to form the body of our post. So, we should be able to wrap the post in the Str helper. And then we could call explode. And we'll delimit by a new line seen as this here is a new line with a limit of 2.
And then we could call explode. And we'll delimit by a new line seen as this here is a new line with a limit of two. So, in other words, the only two items in this exploded array will be the title and the body. And once we have those in place, well, we could say return $this->state. And that accepts an array. We'll set the title to a string helper wrapping the title component we exploded. We want to, first of all, trim it. And once we've trimmed it, well, we don't want the # or the space. So, we'll use the after helper there. And then we also want the body.
So, we'll use the after helper there. And then we also want the body. And the body we can wrap again with the string helper. And then we'll just call trim to get rid of any space around it that happens to be there. Okay, let's see if this works. I'll jump into our terminal, php artisan. And I'll use migrate:fresh --seed. Database is being seeded. Let's go back to posts, refresh. Yeah, here we go.
Fixing Repeated Post Issue5:23
Let's go back to posts, refresh. Yeah, here we go. We have a dive into the Noir Depth Chinatown, and you have a markdown post. Obviously, it's not been converted to HTML yet, but it's there. The problem is it's used the exact same post for every single entry, which is definitely not what we wanted. Why is it doing that? Well, in the database seeder, we asked for 200 posts in this case, but we only call with fixture once. So, you only call that method once for as many posts as you create,
but we only call with fixture once. So, you only call that method once for as many posts as you create, meaning that when postRandom is executed, it will always use this one post no matter how many factories, how many model instances you're actually generating. Instead of creating a single post, we need a sequence of posts. And, well, you'll probably not be surprised to know that model factories have a sequence method. And we can pass as many array instances that have title and body in here as we'd like to be used across all of the models.
And we can pass as many array instances that have title and body in here as we'd like to be used across all of the models that you generate with the factory. So, let's go ahead and make the needed alterations to move from a single Post to a sequence of Posts. Let's add another map onto the end of this post collection. And, obviously, now we're going to be receiving the string contents of the file. So, once we have that in place, we could wrap that contents in the string helper,
So, once we have that in place, we could wrap that contents in the string helper, and I'm going to perform that same explode that we used for a single Post down here, which is going to give us a collection. So, now we have a collection of parts, let's call it, and parts can return an array, and that array is essentially going to be this array that we've used down here. So, let's cut that out.
that we've used down here. So, let's cut that out. We'll paste it in here. And, yeah, we don't want to use title, we want to use parts[0], and we don't want to use body, we want to use parts[1]. And, hmm, if we get rid of this now, which we don't need, and we get rid of this, which we don't need,
which we don't need, and we get rid of this, which we don't need, and because sequence expects a veradic number of arguments, we can use dot, dot, dot to spread out the post data. Pass posts in, like so. That should actually work without issue. So, let's go to our terminal. Again, I'll run php artisan migrate:fresh --seed, and let's go back to the browser, refresh. Look at that, much better.
and let's go back to the browser, refresh. Look at that, much better. Now, we use all of the posts inside our IDE. All of these fixtures that we've created are now being utilized. And, eventually, the sequence will loop back around. I imagine on page two, yeah, we have singing in the rain here. So, you do eventually loop back around and reuse, but it's much closer to reality. And, of course, you could add as many fixtures as you wanted if you wanted a more varied data set.
Caching Loaded Fixtures8:07
And, of course, you could add as many fixtures as you wanted if you wanted a more varied data set. Now, there's a little micro-optimization we could make here. Obviously, we call fixture twice inside this database seeder, meaning that these files are loaded in twice unnecessarily. They only really need to be loaded in once. So, let's take these two lines, and we could create a new private static function called getFixtures. And this is going to return a collection, obviously. And we'll just say, for now, return collect(files);
And this is going to return a collection, obviously. And we'll just say, for now, return collect files and map them over to the file contents, and we'll replace this with static::getFixtures. But then what I'm going to do is introduce a static variable at the top here. So, private static, and we can actually type into now in php. So, private static $fixtures, and then down here, I'm going to use a little magic assignment operator. I'll say return self::$fixtures, and then I'll use ??=.
I'll say return self::$fixtures, and then I'll use ??. What this is going to do is say, look, if $fixtures already has a value, just return it immediately and do nothing else. But if it's empty, then set it to this and then return it. So, basically, it's like a mini caching mechanism you can make use of. And, of course, in this case, it will only work if we use a static property because we want it to persist across the lifetime of the request or the test or the command that we're executing. But with that in mind, it doesn't matter how many times we call with $fixture,
or the test or the command that we're executing. But with that in mind, it doesn't matter how many times we call withFixture, it will always use a cached instance of the files that are generated. It's only a small micro-optimization, but it's one of those things that you might notice as you start using withFixture in more and more places, particularly if you want to use it in tests at any point. So, with these much more realistic Post instances being generated by our database seeder, now we can turn our attention to turning the markdown that's being outputted into HTML.
Converting Markdown to HTML10:05
now we can turn our attention to turning the markdown that's being outputted into HTML.
