مرور ساخت اینترفیسهای پایه: حذف و ویرایش0:00
In the last video, we talked a lot about jobs to be done and what a User wants to get from our application. In doing so, we built out the create page and we built out the index page and the show page for Talks in Symposium. But we didn't build them out the whole way. There's lots of little missing pieces and nuances because we're moving pretty fast. And also, we have a lot of other interactions that users will naturally want to have with our app in response to having that create and index page we also didn't take a look at. So in this video, we're going to cover some of those. So while it would be nice to assume that all of our users always put their data correctly into the system, in reality, even though no users sit around thinking, you know, what I want to do is delete Talks, you know, that's the job for me. When they're using a system like ours, there are baseline assumptions that they can do certain things. And one of them is, if I put something in there, let me take it back out or let me change it if I made a mistake. So if you're allowing someone to create data in the system, baseline modern applications, they're going to want to delete or edit those things.
Implementing Talk Deletion0:53
And one of them is, if I put something in there, let me take it back out or let me change it if I made a mistake. So if you're allowing someone to create data in the system, baseline modern applications, they're going to want to delete or edit those things. We got to build them. It's not that interesting, but both of those do have some mild technical challenges that we're talking about. And delete is simpler, so we'll go there. By default, you cannot do a delete method in a browser. You have to use JavaScript to do that. So there's a little trick that we often use in Laravel to make a button that still sends a delete request over. You could also just make your delete method or your destroy method be a get. It's maybe a little bit less safe.
Delete Link Form Trick1:29
You could also just make your delete method or your destroy method be a get. It's maybe a little bit less safe. It's not the end of the world, but I just want to show a real quick trick on how to do this. So you can actually take a look at the logout form in Breeze to get a trick for how this works. You've got a form with a method of post because you can only do get or post. You've got your CSRF, but down here on the link itself, you're going to capture the click, event.preventDefault on your onClick, and then this.closestFormSubmit. So when you click on this, it submits this form. So this is going to give you basically a post. Even though you can't do post with a link click, it's forcing clicking this link to submit a form of type post. And one of the things you can do with that is you can also say if you want it to be something other than post,
Even though you can't do post with a link click, it's forcing clicking this link to submit a form of type post. And one of the things you can do with that is you can also say if you want it to be something other than post, Laravel has a trick for that where you can change the method that Laravel should count this form as being. So let's build that out really quickly. We're going to create a really quick link right here with a component. And so let's go to our components and we're going to make a just a DeleteItem component. And we're going to copy that code right there directly from the navigation logout link. So it's going to stay a post and we're going to allow you to pass in your own route. So we're just going to say route there. And this is just going to be an ahref.
So we're just going to say route there. And this is just going to be an <a href>. So <a href>, just simple link. And everything else should stay the same. We're going to allow you to define your text here. And then close the link out. Oh, this is a delete item, so we also need to say method delete. And this puts a special hidden input in for Laravel. It says even though this is a post method because you can't pass things like delete along through traditional forms, treat it like it's a delete.
It says even though this is a POST method because you can't pass things like DELETE along through traditional forms, treat it like it's a DELETE. So then we want to use it. We say x delete item. We say our route is one thing and then we say our text is another. Our text will be delete this talk. And our route will be talks.delete. Talk is talk. And let's go over here. I realize we should probably give this a class.
And let's go over here. I realize we should probably give this a class. Just underline for now. So we've got a route defined. We've got our text defined. So let's see how it looks. Routestalk::delete not defined. That's fine. We can work with that. So we've got the get for that one.
We can work with that. So we've got the get for that one. And now we want to define the delete for that one. Right. And even though I always call it a delete, if you look at the method, it's actually destroy by default. So where we're saying delete, let's do destroy. All right. So let's take a look at the HTML we just generated. We've got a form targeting the correct URL, input hidden, and it's got this _method, which is our way of telling Laravel this should be a delete call, not a post.
Wiring Destroy Controller4:30
We've got a form targeting the correct URL, input hidden, and it's got this _method, which is our way of telling Laravel this should be a delete call, not a post. CSRF and then a link when you on click handles it and submits the form. So in theory, that's going to delete it. In theory, what it's really going to do is going to take us to the TalkController at destroy, which doesn't do anything right now. destroy. Right. So we can probably first validate to make sure that this User has access to this particular Talk. And there's a couple of different ways to do that. But the simplest thing that I'll probably do here is if talk_user_id.
And there's a couple of different ways to do that. But the simplest thing that I'll probably do here is if $userId is off $userId. And then we'll just say Talk::delete. And then return redirect()->route('ox.index'). So if we had more time to work together, we do a few things here. We'd set up a Gate for deciding who has access to certain things. We would set up a toast messaging system in JavaScript so we can send a flash message along and say, you just deleted this thing successfully. And we'd be testing these things as we went.
Building Edit and Update5:41
We would set up a toast messaging system in JavaScript so we can send a flash message along and say, you just deleted this thing successfully. And we'd be testing these things as we went. But as it is, let's just see if it works. Let's see it with a click. And it's gone. So we're destroy method works, even though it's not quite as robust as we would want it to be eventually. So what remains is the most complicated, which is the update. And I like to start with an update that uses the same form include that the create page used. And sometimes that's viable and sometimes it's not. So let's go take a look.
And sometimes that's viable and sometimes it's not. So let's go take a look. I just realized one of the things that's missing. Look at that still says create talk at the top of this page. Let's go to the talks.index page. And change that. All right. So if we were to go to the talks.create page, one thing I realized I was missing is. Perfect. This is great.
Perfect. This is great. So the order of these matters, and I'm just going to tell you right now, I get it wrong every time. I always make a mistake. So you probably guessed already that the problem is because this show page is before our create page. It's trying to match for a talk with the idea of create, which doesn't exist. So it's got to go first. All right. So when we create a talk, also, I just thought about it. The fact that if I have some data in here and then we have a validation error, the data doesn't come back.
Preserving Old Form Values7:04
So when we create a Talk, also, I just thought about it. The fact that if I have some data in here and then we have a validation error, the data doesn't come back. So that's one of the things we need to do for this one that will also be valuable in this edit page. So if we were to let's close all this stuff out. If we were to go to talks, and I think I call it template in each of these, you can put the old values. You can say value equals old and then the name. And what old means is, oh, I should do it on one that's not required. So let's grab this and let's throw this in. It's a little bit more complicated to do it in type, which we also need to make these enum back. My gosh, look at all these things we need to do.
It's a little bit more complicated to do it in type, which we also need to make these enum back. My gosh, look at all these things we need to do. We'll come over to the length field. And it's old. And then if we pass in length, we'll now get that same value. We can do the same thing for the abstract, except it's not a value field. It's what goes between the opening and closing braces. And then same for organizer notes. And so we've now got them saved in case we have a validation issue. It's a little bit more complicated if we do type, but that brings us to the point where we need to be doing type as iterations over a enum anyway.
And so we've now got them saved in case we have a validation issue. It's a little bit more complicated if we do type, but that brings us to the point where we need to be doing type as iterations over a enum anyway. Because this one's simple, it's not a big deal, but you'll often find yourself in a place where you change this list later. And then you have to remember to change it everywhere, which is one of the biggest benefits of an enum in the first place. All right, so I think I did talk type. So I'll say foreach app enums talk type cases as talkType. And then we've got our option. value equals talkType. And then this is going to be the ucwords talkType. So it looks all pretty like that.
And then this is going to be the UC words talk type. So it looks all pretty like that. That should be the same when I mix up. UC words must be of type string. Oops, because these both need to be talk type value. Remember, those are an enum. All right, there we go. So what we have to add here is sometimes it's nice to do a conditional directly in here. So we would say oldType equals talkType. If so, that means this is one that's selected.
So we would say oldType equals talkType. If so, that means this is one that's selected. And if not, this one's not selected. So that way, if we say keynote, then we get a validation error. It doesn't stand on keynote. What did I do wrong? Oops. Of course, I always need to do the value. So let's change it to something else. Submit it. And there you go. I do keynote. Submit it.
Submit it. And there you go. I do keynote. Submit it. There we go. Stays keynote. And while we're at it, let's take advantage of that enum for validation as well. So we can go to our TalkController and we'll go to the store method. And where we've got our type, we can say required and Rule::enum(TalkType::class). And now we're saying it must be one of the enums. So just for fun, let's add an extra option that doesn't work. We should get a validation error there. What should we do? The selected type is invalid.
We should get a validation error there. What should we do? The selected type is invalid. Great. So we're using our enums to their full potential now. So quick interlude. Let's go back and let's work on our edit form. What I was saying is that I like to reuse the same template if possible. So first thing I recognize is this method and route is going to be different. So I'm going to move those outside of our div so that we can move them into the including form page. So we'll go to talks/create, and we're going to pull this form declaration outside of here. Great. So this should now work exactly the same as it did before.
So we'll go to talks, create, and we're going to pull this form declaration outside of here. Great. So this should now work exactly the same as it did before. And now we can create an edit page. So we're going to duplicate our create page and we're going to say edit. And this is going to be edit Talk. And our route will be instead of TalkSpot::store, it will be Talks::update. So we've got edit and update and we have to pass in the talk. So how do we get there? We go to our talks. And we'll give it an edit button just like it has a delete button. So that's the talks show page.
We go to our talks. And we'll give it an edit button just like it has a delete button. So that's the talks show page. So again, we're just keeping this real ugly. So we're going to say. So we should get an error. Yep, that the route is not defined. So we want to make a Talk editable. All right. So there's now an edit page. We'd make that button be a little bit less ugly. And then we can go to the TalkController. To our edit method. return view(talks.edit, talk). Talk.update is the form that we're pointing to. It's not defined yet. It's right here. So. Let's find that in routes/web.php.
Talk.update is the form that we're pointing to. It's not defined yet. It's right here. So. Let's find that in routes/web.php. So that will be a. POST or PATCH, depending on how you look at it, but we're just going to do a PATCH. And we're going to go from this being named show to this being named update. So we now have a form that is the edit Talk form and the edit Talk form needs to be updated so that we're getting all the details of this particular Talk. Put in here. How do we do that? Similarly to how we just update the template. To pull in old. We set the value right here. We can also pass a fallback to old and the fallback can be talkTitle. Like this. And so now we suddenly see the talkTitle right here. We can do that for all of them. However, as you're probably imagining, what does that do to talk?
And so now we suddenly see the talk title right here. We can do that for all of them. However, as you're probably imagining, what does that do to talk? Create page. You get this error. Undefined variable talk. You think, oh, well, this is not possible. Check this out. TalkController at create. We can just pass in an empty talk. Nothing in it. All of a sudden, when you run talkTitle on an empty talk, you just get an empty thing and it's just like it was a new one. But if you're editing this talk, you can get the details for this particular one. And again, I won't bore you with fleshing this out for all of them because we're a little short on time in this one. But it works the same for every single one of these. When you're checking old, you pass the second parameter of what the actual talks version of that is.
But it works the same for every single one of these. When you're checking old, you pass the second parameter of what the actual talks version of that is. And the cool thing about that is and then if we're editing and we get a validation error, it is also known correctly. Should it be pulling from the talk in that particular field or should it be pulling from what we passed in most recently? And then when we work on our update page, as you can see, if we wanted to add an exclamation point here, we're going to get two things. We're going to get the request and we're going to get the talk itself. We want to make sure that we go over to this and we give it a method of patch. They're patching. Here we go. We've got a request. We got our talk. So if we go to our TalkController at the update method, we can also put in our own validation here. Now, sometimes you want the validation to be exactly the same and sometimes you want it to be different.
So if we go to our TalkController at the update method, we can also put in our own validation here. Now, sometimes you want the validation to be exactly the same and sometimes you want it to be different. Either way, if you're doing anything more complex than a few of these, you're probably going to want to use a FormRequest object. But because we're keeping it super simple, we're just going to keep it here, keep it in line. So just like with our create, we're going to grab the same validation rules. This one doesn't talk about uniqueness in the database or anything like that. So right now, at least we're just going to keep it the same. So we say validated and then we say talk. And we already have this talk defined, so we don't have to worry about making sure it's attached to the User or anything like that. We just say talk update and then we use all of its new stuff.
Conferences List Overview16:16
And we already have this talk defined, so we don't have to worry about making sure it's attached to the User or anything like that. We just say talk update and then we use all of its new stuff. And then since we are referring to the specific talk, we can redirect to its show page. And now we got our exclamation points so that users can now create a talk, they can see a list of all their talks. They can edit and update a talk. Basic interfaces for talks. And the good news is for conferences, we don't need all of that stuff. Conferences are going to be automatically imported via an API. So the only thing we need for a conference is just to be able to see it in a list. And again, if you go to symposiumapp.com, you can go to our conferences list.
So the only thing we need for a conference is just to be able to see it in a list. And again, if you go to symposiumapp.com, you can go to our conferences list. And all you got there for every single conference is the title, the most important dates, and then if you click through into it, you can see a couple URLs. So this is very similar to what we just built out for the talks list page and the talks show page. And nothing we need to really stress about. It does have some filters, but those filters are relatively complex and we're not going to have a chance to get to them here. Just know that these filters were something we added after the fact. By default, it just says, here's all the talks that are worth you considering submitting to going forward. And that's really all that's going to matter.
By default, it just says, here's all the talks that are worth you considering submitting to going forward. And that's really all that's going to matter. So again, I'm not going to take all the time to build out this user interface, but just imagine we're basically going to have a single method that says showAllTalks. It's going to iterate over those talks. It's going to give a couple bullet point items for each talk. And when you tap into it, there's going to be a show page for that talk that gives you a little bit more data than we were able to show in the list page. That's it. So assuming that you followed through with me building out the rest of the nuance of the pages, for example, a top navigation that actually allows you to go to the talks page and allows you to hit a createNewTalk button.
So assuming that you followed through with me building out the rest of the nuance of the pages, for example, a top navigation that actually allows you to go to the Talks page and allows you to hit a create new Talk button. And, you know, some of the niceties that we didn't spend time on here, making the CSS for the edit Talk page look good, and the show Talk page look good, and then the edit Talk link look good and everything. If we spent the time doing those things, which wouldn't take that much longer, you know, a couple hours, you would have a rudimentary software as a service ready to launch. Users can register. They can log in. They can add their Talks. They can see their previous Talks, and they can find conferences to submit to, which is what symposium is about.
They can add their talks. They can see their previous talks, and they can find conferences to submit to, which is what symposium's about. There's some more features we need to build in the future, some of the more, you know, beneficial back end things like actually importing conferences. But it's very important to note the fact that in this short amount of time, we've built a software as a service that provides an actual feature. It is a job that people need to be done that they can do with this app. And we built a couple forms, a couple controller methods. That's it. So the power that we have as application developers is pretty amazing. And going forward, everything from here, we're just going to be doing things that make it better.
So the power that we have as application developers is pretty amazing. And going forward, everything from here, we're just going to be doing things that make it better. But the core functionality is here already. So I'll see you in the next one.
