Creating Puppy Record0:00
Okay, let's tackle this next step of creating a new Puppy instance and persisted to the database. So let's remove the dd image URL. So the code flow continues and assuming that we get to this point, we are going to take the Puppy model that I'm going to import and call the create methods to create a new Puppy. And so exactly like suggested, we want to assign the name to request.name the trade to request.trade
And so exactly like suggested, we want to assign the name to request, name the trade to request, trade the image URL to imageUrl that we've created here and the user ID relationship to the currently logged in User. So maybe let's store this into a puppy variable and so that we can dd the puppy after its successful creation. So Simon, cool. And choose the image that we always choose. And so when I try to create the puppy, aha, good.
Fixing Mass Assignment1:05
So Simon, cool. And choose the image that we always choose. And so when I try to create the puppy, aha, good. Wait, what? An error is good. Yes, in this case this is a really good error and this is Laravel trying to help us, preventing us from shooting ourself in the foot with some risky mistakes that we might not understand. As you can see, it's trying to protect us from mass assignments because we haven't added the fillable property on the Puppy model.
assignments because we haven't added the fillable property on the Puppy model. So let's get in there and discuss further. And what Laravel is trying to do here is protect us against the ability of mass assigning multiple properties when doing crowd operations. And so we can say, hey, this is safe. I allow you to fill the following properties with the protected fillable properties and do not do this.
I allow you to fill the following properties with the protected fillable properties and do not do this. But let's accept that and discuss. So here it's really important that you understand that you should not do this. And when I say this, I am particularly pointing to the userId field. Think about it. If you allow the userId relationship field to be mass assigned, potentially somewhere in your application,
to be mass assigned, potentially somewhere in your application, a User could add the user_id hidden input and change it to another User. So that means that someone could create a Puppy that belongs to another User, uh, which is sort of weird and off limits in my opinion. So we wanna allow name, trait, and image for sure, but we definitely don't want to allow user_id. Alright, so let's submit the form one more time now
but we definitely don't want to allow userId. Alright, so let's submit the form one more time now that we have allowed these three properties for mass assignment, SimonCrew for the 60000th time select the image and submit. Okay, so it looks like I've let copilot trick me, it's trying to save the image fields, but it should be imageUrl. I'm sure you had seen this and was yelling at the screen, but this should be imageUrl here.
I'm sure you had seen this and was yelling at the screen, but this should be image URL here. And so let's try again. Oh, I'm tired of Simons, I'll go Tony fast. But for the image I will keep selecting the same. So let's try this. And now we hitting an interesting problem. You can see that it's saying, hey, you forgot to fill a null table row, which is puppies.user_id. It looks like we've inserted a name trade update.
to fill a nuts nulled table row, which is puppies user_id. It looks like we've inserted a name trade update that created that values with Tony Fast and the timestamps, but we haven't filled the puppies user_id, which is required. And this is actually intentional. We have refused to allow the mass assignment of the user_id field for reasons that I've explained before. We don't want someone to be able to create a puppy that belongs to another user than themselves,
Assigning User via Relationship3:45
We don't want someone to be able to create a Puppy that belongs to another User than themselves, but now we can are blocked into creating a new Puppy because we are not feeling that. So we need to find a way to not pass userId as one of the fields in the create method. So there are a few ways to get around it. Let's remove that from here. And so we could create this and then append the userId later.
And so we could create this and then append the user ID later and save the Puppy that's already created. But I think a really elegant way to go about this is instead of directly calling Puppy::create, we can here get the User with $request->user() and then you might remember that the User has a puppies hasMany relationship attached to it, which we can tap into to directly create a new Puppy like so.
to it, which we can tap into to directly create a new Puppy like so. So the reason this works here is if I go to the User model, we have a puppies hasMany relationship. So Laravel understands that puppies have a user_id field that is making them related to the puppies. And so we can use this really elegant syntax to from the User reach for the puppies and then create a new one. That means that the user_id field is going
and then create a new one. That means that the user ID field is going to be attached coming from that information instead of having to pass it in the create array. That's pretty cool, right? So we only allow mass assignments to the safe values here. Remember we did not allow the user ID to be filled, but we can still pass the user ID field by creating the puppy directly from the logged in users, puppies.
by creating the puppy directly from the logged in users, puppies. Okay, let's see if we can get our puppy in the dying dump here. So for the millionth time, let's go Alex, this time she's funny and we'll select that image and try submit the puppy. And I thought it would work. But obviously we have another issue that we haven't filled. Image URL.
But obviously we have another issue that we haven't filled. image_url. So I think once again I've trusted co-pilot it made the mistake here and probably made the mistake in the fillable property as well. Yes, here what we want to allow is image_url. All right, let's go again Alex, sweet, select the image and this time let's get our puppy and we do. Very cool. So it's created a new Puppy.
and this time let's get our puppy and we do. Very cool. So it's created a new puppy. You can probably see all the attributes here. We've created Alex. She's sweet. So this was a pretty underwhelming way to say tell her success you've created the puppy. But because we see this, the puppy is in the database. So if I refresh the page, I should be able to search for Alex or Sweet and see the new puppy in our list of puppies.
Redirect and Sort Latest6:20
for Alex or Sweet and see the new puppy in our list of puppies. All right, refresh the page, go to the top and let's search for Alex and ta. There's Alex. Definitely liking that puppy. Pretty cool, huh? I think we're pretty close to being done here. We need to obviously finish up the controller. So instead of die and dumping the result, it redirects to the same page and then maybe we can implement the
So instead of die and dumping the result, it redirects to the same page and then maybe we can implement the processing, uh, pending state UI by adding a little sleep and handling it properly. Let's finish that off and we will be done for this lesson. We passed this point. We are not die and dumping anymore. Instead we want to return back and maybe yes, with Success Puppy created successfully. And thank you copilot for noticing that I have forgot the K, this is what I meant.
And thank you copilot for noticing that I have forgot the K, this is what I meant. And by the way, we are not going to show this right away, but this is going to make it super easy for us to show a flash message, maybe a toast notification of success. So let's save that and go to the front end. And I can tell you right now that the redirect to the same page UX is gonna feel a bit underwhelming because you can see we have 14 pages.
to the same page UX is gonna feel a bit underwhelming because you can see we have 14 pages and the last puppy created, which is Alex, is going to show up at the end of page 14 as the last entry here. So what I'm thinking here is we reverse the order in which we show the puppies. So the last puppy created is the first one to show at the top of page one. That will make more sense whenever we create a puppy. We can scroll the user back to the top of the puppies list.
That will make more sense whenever we create a Puppy. We can scroll the User back to the top of the puppies list and the new Puppy is gonna show to the top left here. Reversing the order of the puppies list in the index method couldn't be simpler before we paginate. We simply get the latest to get them ordered by the latest. And so if I visit the homepage again and go to the first page, I should see Alex as the latest and greatest first in place.
and go to the first page, I should see Alex as the latest and greatest first in place. Alright, so because we've implemented the redirect to back on success, let's try create Tony, Sophie. And I don't know why I'm creating my children instead of puppies, but that will do, let's go with this image and create Aha. The image is too big. This is good that we have this error. So let's try another image. Okay, we'll go with that one.
The image is too big. This is good that we have this error. So let's try another image. Okay, we'll go with that one. It's 3.2 megs. Um, and I'm going to add the puppy. And so whoa, what happened? This is sort of weird, this is typically where having a toast notification would help, but what would also help is to scroll to the top and reveal what I believe is the new creation of TonySurf. So remember how we use the Unsuccessful method in the router that gets in the previous lesson.
Reset Form After Submit9:08
So remember how we use the unsuccess method in the router that gets in the previous lesson. Well the form post also has an unsuccess, so why don't we unsuccess add a little bit of JavaScript to scroll back to the top of the list. That should make for much nicer experience. Oh, and while we are in there unsuccess, we should also clear the form because you can see that the fields have remained inside the form, which we probably don't want to happen.
because you can see that the fields have remained inside the form, which we probably don't want to happen. Alright, new PuppyForm is where this is going to happen in the post action here. So in the options after the preserveScroll, we are going to target the onSuccess callback and on success, the first thing we want to do is reset the form. Would it be nice if we could just say reset like this? Well we can.
Would it be nice if we could just say reset like this? Well we can. We just have to import the reset helper from useForm. And now this will do exactly that, reset the fields in saying that this is going to work perfectly for text inputs, but file inputs are a little bit different. And here we sort of need to use a ref so we can manually manipulate the DOM and clear the upload fields.
so we can manually manipulate the DOM and clear the upload fields. Uh, but for other field inputs it's going to work by itself with the reset method. So let me show you what I mean. I'll have some gibberish here and upload that image again. And so when I submit, I expect these two fields to be reset, but this is not going to be reset. Ready? Yep. Exactly like I said. And again, it has created a gibberish puppy.
Ready? Yep. Exactly like I said. And again, it has created a gibberish puppy. Okay, let's go into the database real quick and I will delete the last three puppies that have been created because we're going to create some more and notice that we have deleted the puppies, but we have not deleted the images in the database, which is something you should take care of. So I'll delete these images.
which is something you should take care of. So I'll delete these images and I think I've seen quite a few more. So let me select them all and make sure that they are gone as well. All right, so let's tackle the reset of the file. Upload input. I'm going to create a rev here. Cons, file input Rev. And we are going to use Rev from React and set it to an HTML input element.
And we are going to use useRev from React and set it to an HTML input element. Indeed. So I need to import useRev from React. And then we are going to assign this file input rev to the input field. That is the file upload here. inputRev equals fileInputRev whoop, sorry, rev equals fileInputRev. And so now in our unsuccessCallback we can target this fileInputRev and clear it.
And so now in our unsuccess callback we can target this file input rev and clear it. Alright, so back in the onSuccess, let's maybe check if file input rev has a current property. And if so, are we going to set its value to an empty string? So that is going to blank out the file upload field and make our form completely reset, which is good. And then let's add the cherry on top, which is scrolling the user back to the top.
And then let's add the cherry on top, which is scrolling the user back to the top of the list if the puppy was successfully created. So we want to do window.scrollTo, but we need to check first if typeof window is not undefined. So we check if we have a window object before we do window.scrollTo. And here we wanna go to top: 0 and behavior: 'smooth', which really is the key.
And here we wanna go to top zero and behavior smooth, which really is the key to make a smooth crawl back to the top. And now ladies and gentlemen prepare to amaze. I think this is going to feel really good. I'm going to create a new Puppy. It's going to clear the form and scroll me to the top to reveal the newly created Puppy. Let's check it out. Let's go down there. We'll create an actual Puppy. What's a cool name Buster.
Let's check it out. Let's go down there. We'll create an actual puppy. What's a cool name Buster. Woo. And then I don't really know what to do for the picture. Well that is going to work. So when I click submit, the form's going to be cleared and I should scroll to the top. Ready, 3, 2, 1, go. Woohoohoo, that is pretty cool. We should probably not scroll to the top.
Scroll to Main Ref13:12
Woohoohoo, that is pretty cool. We should probably not scroll to the top of the window, but scroll to here. And so to achieve this we can give a ref to the main tag in the HTML and then we can pass this ref to the new puppy form component. And then instead of doing window.scrollTo here, we can use scrollIntoView and we scroll to the point where the ref is.
we can use scrollIntoView and we scroll to the point where the rev is where the Main tag is instead of the top of the page. So let's go in index.tsx. And the Main tag here is where we wanna scroll to. So let's give it a rev of, and we call it mainRev, but we need to create this. So const mainRev = useRev, it's not a div, it's just an element. And we need to import useRev from React.
Use Rev, it's not a div, it's just an element. And we need to import, use Rev from React. So we've set a rev that we assign to the main tag. And here the new puppy form here is going to receive that rev. So we pass the main rev to it. It doesn't know what to do with it because we have to update the component to receive that main rev, which is typed as a rev object of an HTML element.
that mainRef, which is typed as a Rev object of an HTML element. So now we've created the Rev and we passed it to a new PuppyForm. And so I can go here and instead of checking if we have type window undefined, I'm going to check if the mainRef has a current property. And if it does, I'm going to do the following. Instead of using window.scrollTo, I will use mainRef.current
Instead of using window that scroll to, I will use mainRef.current and then use scrollIntoView. That behaves similarly, but it doesn't take a top value, it takes a start, finish or so positioning. So I'll accept that. And so what I'm hoping for now is instead of scrolling to the top, I'm going to scroll to the top of the main tag. It's not going to look perfect just now,
to the top, I'm going to scroll to the top of the main tag. It's not going to look perfect just now, but this should already be much better and then we can go and tweak it. All right, so down we go and we going to create wolfie wolf's and let's select ah, this image, whatever. So now check this out. I'm hoping to scroll to the top of the main tag, not to the top of the page.
I'm hoping to scroll to the top of the main tag, not to the top of the page. Hey, that worked. So as you can see, we are flush against the top of the element, which is what I meant by, it's not going to look great. Now we can't really add some gutter, like some offset top value. That would be nice. But we can actually use CSS for that. There is a scroll-margin-top property.
That would be nice. But we can actually use CSS for that. There is a scroll-margin-top property that doesn't actually create margin to the top on the block level elements, but it applies it whenever there is a scroll two mechanism. So we are going to use that. And Tailwind has scroll-margin-top out of the box. So let's use that. And so back in the index file on our main tag, let's give a class name of scroll-mt for margin top,
so back in the index file on our main tag, let's give a class name of scroll-mt for margin top, but 16 is way too much. Let's go scroll-margin-top of 6. And so you can see that this applies a scroll-margin-top. Okay, and so this is where we had scroll too. And now with the scroll-margin, I'm hoping that it's going to add a little bit of gutter, which should be very nice. Let's try it again. Although the image might be too big. 3, 2, 1, go. And the image is too big.
Let's try it again. Although the image might be too big. 3, 2, 1, go. And the image is too big. So let's take this one and 3, 2, 1, go. Hey, perfect scroll. This is very nice.
