Starting Post Show Tests0:00
So obviously we need to pad out this index with additional details, but I think if we concentrate on the show page for a Post first, that will actually inform our decision on what should appear in the index in the first place. So yeah, we'll focus on at least the basics of the show page, and then we'll come back to the index in the next episode. Much as we did with the index, we'll start with a test. So this will be the show test file, and there's our opening php tags, and what's our first test going to be? Well, it can show a Post. So we'll have our opening closure, we'll need a Post to actually show, well we can use our
Well, it can show a Post. So we'll have our opening closure, we'll need a Post to actually show, well we can use our postFactory for that, right? So $post equals postFactory and create, and then we can use the get helper, grab the correct route, which will be post.show, passing the $post as a parameter, and I'm going to do the same thing I did before, which is assertInertia, and it gives an assertable Inertia instance, and then we can say Inertia component post.show passing true. Now thinking about it, and in line with what we discussed in the last episode, we're doing this a lot, and we will be doing this a lot as we build our controllers out. Let's think about a nicer syntax for this check.
Adding assertComponent Macro1:16
this a lot, and we will be doing this a lot as we build our controllers out. Let's think about a nicer syntax for this check. So something like assertComponent maybe, and then we just have to pass postShow, and this would basically act as an alias for this. I think that would be a much nicer way of writing this without all the verbosity. So let's see if we can make this work. We'll go to the TestingServiceProvider again. It's going to be at the top level, so that'll be a testResponseMacro. assertComponent, and that opens up with a closure, which is past the component you're interested in, and then, yeah, GitHub Copilot gets that mostly correct, other than the fact
Assert component, and that opens up with a closure, which is past the component you're interested in, and then, yeah, GitHub Copilot gets that mostly correct, other than the fact we want to pass true as a parameter here to ensure that it actually exists. Okay. So hopefully if we go back to our show test, and we get rid of this, and we run this test, yeah, there we go. Route post.show not defined. So this is working, and this is going to be much easier to play with in the future. While we're on the topic, let's go ahead and refactor the index test. So assert component posts / index like that, and we can get rid of all of these
While we're on the topic, let's go ahead and refactor the index test. So assert component posts/index like that, and we can get rid of all of these lines here now. Let's just rerun that test to make sure it works. It does. Nice. Right. So back to the matter at hand. Let's jump into the routes file. I'll duplicate this line here.
Creating Show Route2:41
Let's jump into the routes file. I'll duplicate this line here. Rather than going to just posts, it will go to the posts/{post} endpoint, and this is our $post variable that will change. The ID of the post will be placed here, and that will be resolved by Laravel for us, and it will hit the show method, and obviously the name is going to be posts.show. Let's jump into that method. We already inject the post correctly, thanks to building using the php artisan command at the start of the series. So why don't we go ahead and just dd($post) to make sure that this works as
start of the series. So why don't we go ahead and just dd $posts to make sure that this works as we'd expect. Go back to our show test and run this once again, and there we go. There is the Post that's been injected with all of the attributes that we'd expect. Awesome. I would expect as well if we go to the browser, and let's just go to the endpoint here. Yeah, we see it in the browser as a dd variable. So the route is working correctly. We just need to wire Inertia up to show the correct page.
Rendering Inertia Show Page3:32
So the route is working correctly. We just need to wire Inertia up to show the correct page. So we'll get rid of that. We'll return Inertia::render('posts/show'), and I'm not going to pass anything for now because we'll do that using test driven development, but if we go back down to our components/pages, here's posts, and we'll create a new file called show.view. Awesome. Now obviously we have our template tag, and we also have our script tag, which uses the setup in Vue, and then we're going to need our app layout, was it? Yeah, there we go, app layout.
setup in view, and then we're going to need our app layout, was it? Yeah, there we go, app layout. Let's just say hello world for a second to make sure this all works. Yeah, there we go, hello world. So that is working correctly. If we go back to our show test and run this again, it passes. Awesome. So we have a passing test. We have correctly configured this view to load. Obviously we can't do anything unless we pass a post down, so let's create a second test.
Passing Post Resource4:22
We have correctly configured this view to load. Obviously we can't do anything unless we pass a post down, so let's create a second test. It passes a post to the view, and in this case the test is going to look quite similar. In fact, I could steal these lines here, but then I can use our brand new helper, assertResource or assertHasResource. Obviously the key will be post, and then we want a PostResource, which wraps the post that we create at the top of the test, like so. Okay, let's run it. Property post does not exist. That's exactly what I would expect.
Property post does not exist. That's exactly what I would expect. So if we go to this route, I should be able to pass a new property called post, and we wrap this in PostResource, we'll call the make method, and I'm simply going to literally pass the post in like that, and then we'll go back to our test and run it, and we have a passing test again. So now we know that the backend is correctly configured. The post is being passed down to the view, and we're ready to implement that in view on the frontend. Awesome.
on the frontend. Awesome. In the frontend file, why don't we define our props? So const props equals defineProps(), and we'll just grab that post, and then I think there's a title property on AppLayout we can make use of, and we'll set that to post.title. Let's see if that loads correctly. Yep, there we go. It's got the title of our post in the browser now, which is nice. Okay, let's just get everything out on the frontend first. So maybe we have a h1, which has the post.title as well.
Okay, let's just get everything out on the frontend first. So maybe we have a h1, which has the post.title as well. Then we could have a div, which is going to be kind of the body section. I think actually you have an article. Yeah, maybe article would be better for this using HTML5, and we'll output the post body. So there we go. Here's the title. Here is the post body. What else does a post have? Well, you have the created_at time for a post.
Formatting Dates on Frontend6:13
What else does a Post have? Well, you have the createdAt time for a Post. So we could introduce a span here, and let's just say postCreatedAt like so. Obviously it's going to output that date in an ISO format that we're not exactly happy with. We don't want to show that on the frontend. So it might be the right time to start thinking about a frontend library that would convert that into a human readable time. Again, if you're working in Livewire or in standard Blade, you can do that using Carbon. My preferred library for this kind of thing is date functions.
Again, if you're working in Livewire or in standard Blade, you can do that using Carbon. My preferred library for this kind of thing is date functions. You can use something like Moment.js as well. This is just what I'm used to. So I'm going to jump to the installation step, and we should be able to install this using NPM. Let's go ahead and do that. And once that's installed, well, we can make use of it. So I'll create a constant called formatDate, and we'll make this a computed property. And I believe the function is formatDistance.
So I'll create a constant called formatDate, and we'll make this a computed property. And I believe the function is formatDistance. So this is going to give us kind of a nice "three days ago" kind of thing rather than an exact timestamp, which I much prefer for reading as a human being. Then we need to pass it a date, and there is a passISO method available. So we'll grab passISO, and that's going to come from props.post.createdAt, like so. Let's see if that works. I'm going to output the formatted date here at the top, and it looks like we have an error. Okay. I have to pass the comparison date to formatDistance, which is just going to be the current.
Okay. I have to pass the comparison date to format distance, which is just going to be the current date, right? There we go. And currently it says 14 days, so we'll just need to update that with maybe a string like ago. You might want to think about translation strings at this point, but for now, I'm going to keep it simple. We'll come back to that a little later on. All right.
Loading Post Author7:57
We'll come back to that a little later on. All right. We have our title. We have the publish date. We have our body. And we probably want to know the user's name who published it. This is going to require going back to the backend in order to load that user in. So first of all, let's go to our show test. And in our show test, I do want to make sure that we've loaded the post resource, but I want to load the correct relationship, so I want to load the user relationship on that.
And in our show test, I do want to make sure that we've loaded the post resource, but I want to load the correct relationship, so I want to load the user relationship on that post resource. And when we do that and run this test, it's now going to fail because in our test, we are also loading the user because of how we set up our resources, right? So this is the really powerful, cool thing about using resources. All we have to do is load the correct relationship, and now the post resource will include a user. And our tests are super easy to infer that information in a single line. So we can go back into our PostController. And here we have our post.
So we can go back into our PostController. And here we have our post. And all I'm going to do is the same thing. I'm going to say post->load in order to load the user relationship on that post. And now the show test should pass again. And it does, which means on the frontend, we have access to post->user. So we should be able to say post->user->name, like so. And there we are. Simple as that. 14 days ago by Norval Lindgren.
Styling the Show Page9:20
Simple as that. 14 days ago by Norval Lindgren. What a great name. And at least for now, that's about as much information as the show page needs. We need to load in all the comments, but we'll come to that in a separate episode. I do want to spend a little bit of time just cleaning this up, pulling things in and making it look somewhat decent. So if you're not interested in watching me do that, feel free to skip ahead. If you are interested, well, let's go and make this look pretty. The first thing I'll do is wrap everything in a container as we've done before.
If you are interested, well, let's go and make this look pretty. The first thing I'll do is wrap everything in a container as we've done before. Here we go. That pulls things in nicely. The H1 will need a little bit of styling. Did we create a heading component? No, we didn't. We probably want to look at doing that at some point. So now we'll say text-2xl maybe. And font-bold.
So now we'll say text-2xl maybe. And font-bold. There we go. Makes it look a little bit more like a title, doesn't it? Maybe here we'll say text-small. And we could say text-gray-400. A bit more than that. 600. Yeah, there we are. That's nice.
Yeah, there we are. That's nice. Okay, for the article, I want to put a little bit of margin. That's too much. What about 6? Nice. That's a little bit better. So now we have a nicely formatted title. One thing I will do is I will make this act like a block and I'm just going to put a little margin above so we have a bit more space up there.
One thing I will do is I will make this act like a block and I'm just going to put a little margin above so we have a bit more space up there. Nice. Okay. I'm quite happy with that. Let's just make sure it looks okay in mobile as well. And our container has a little issue here. Look. There is no padding in mobile for it. So I think we just maybe forgot about that.
There is no padding in mobile for it. So I think we just maybe forgot about that. So we'll say by default, you're going to have maybe a px of 4. There we go. That just pulls it in nicely. And then obviously as we get larger, that automatically sets itself to px 6 and px 8. In the future, I'd like to make the body of each post markdown. I think that would make a lot of sense because it would mean the user could format their post and they could make certain things bold, add links, whatever. But we need to give that more thought than this one episode will allow for because there
Improving Seeded Post Body11:30
post and they could make certain things bold, add links, whatever. But we need to give that more thought than this one episode will allow for because there are some security concerns when it comes to markdown that we just need to take into consideration. But I will say that most posts I'd expect would be longer than one paragraph. So why don't we just quickly update our cda to make sure that when we generate this fake data, it does create longer posts for us to look at in the front end. I think I'll use a collection for this. So collection times, we'll have four paragraphs and each paragraph uses this realText method. We'll just reduce the number of characters per paragraph. And then we can join using PHP_EOL and we'll use a second PHP_EOL to
We'll just reduce the number of characters per paragraph. And then we can join using php end of line and we'll use a second php end of line to actually add the correct amount of spacing. Okay. We should be able to just generate our database once again. I would expect that this doesn't actually show on the front end because obviously HTML doesn't respect line endings. But we can trick it into doing that for now until we come back to this later and clean it up. So I'll come into our view file and I'm going to wrap the post body in the <pre> tag.
it up. So I'll come into our view file and I'm going to wrap the post body in the <pre> tag. So that should mean, yeah, we have our paragraphs. We now just need to format the <pre> tag using Tailwind. So again, let's add a few classes here. And I believe if we do white-space: pre-wrap and we'll also have to set the font to sans. There we go. Awesome. So now we have a nicely formatted post where we can have paragraphs as well. All right.
Previewing Next Episode13:01
So now we have a nicely formatted post where we can have paragraphs as well. All right. I think we're there. That's a great start. Before we go into comments and functionality like that, why don't we spend the next episode wiring up our post index so that we can click these as links and be taken to the respective show page for each post? And we'll also add just a little more detail to each item, perhaps the user and also the publish date so that you can see that at a glance on the index. I'll see you there.
publish date so that you can see that at a glance on the index. I'll see you there.
