در حال بارگذاری ...

Creating a Basic Form0:00

One option you have when it comes to cleaning up your Eloquent models or even your controllers is to create a dedicated form object. And I'll demonstrate this in two steps. First, we'll write a manual integration. And then second, we'll figure out if Laravel's form request objects can serve the same purpose. All right, so imagine, yeah, we have our welcome page. That's fine to start. And then within here, let's get rid of the style tag, all of that. We're just going to add a dummy form here. All right, so we will post. And yeah, let's just stick with the stupid idea of creating a Post, the obligatory demo. So we have our csrf field. And then to keep it as simple as possible, a text area with a name of body. All right, finally, let's set up our button. All right, so incredibly basic, very bare. So when you visit the homepage, you see a form. And when you fill it out, that will post to this URI. Okay, so let's

Defining Form Object Role0:46

finally, let's set up our button. All right, so incredibly basic, very bare. So when you visit the homepage, you see a form. And when you fill it out, that will post to this URI. Okay, so let's go back to my routes file. And we'll say, when the User posts to this URI, then I'll do it in line for now. We'll trigger this function. All right, so think of it like this. The responsibility of a form object is to submit and persist a form. Okay, so with that in mind, it would make sense for the form object to have validation in place. And it would even be the responsibility to persist that data. So in this case, for a Post, yeah, you might have something like a PublishPostForm. And actually, before we continue on, a quick note about this. In this case, it's so simple. Just use the validatesRequest trait that's on every controller. And then just say this->validate(), and then make sure that the title is required. So you get that for free.

Just use the ValidatesRequests trait that's on every controller. And then just say this, validate your form, and then make sure that the title is required. So you get that for free. And then you can just call Post::create. Always go for the simplest possible option. But as you know, in some cases, well, maybe you have 20 different fields. So you don't want to muddy up your controller method with all of those rules. And then maybe you're not creating one Post, but you're actually updating multiple tables. And there's a lot of workflow in that, right? So on those conditions is when you might consider a form object. But don't just default to them, you reach for them, if it makes sense. Okay, anyways, back to it. So if we had a publish post form, well, this object would be responsible for validating the form and throwing an exception if it doesn't work. And then also, it would be responsible for persisting that form. Okay, so

publish Post Form. And first step, we would want to define the rules here. So we could say protected rules are the body is required. Next, if it validates itself, but also persists itself, then we should have a persist function. And for now, we'll just say save crap to the DB. Okay, so think about this, in our perfect world, this is ideal, this is what I want to write. But we know that well, at some point, you have to perform the validation. So you have to read the rules, you have to get the request, you have to validate it. And if it fails, you need to throw an exception. And if it doesn't fail, you need to call the persist method. So yeah, you could do it all in this class. But we know that we're going to have multiple form objects, right? So in that case, maybe we can bump that up a notch to an abstract parent class. So we're going to do that extends Form. Okay, let's create that Form class, once again, set the namespace app, HTTP Forms,

Creating Abstract Form Base4:03

maybe we can bump that up a notch to an abstract parent class. So we're going to do that extends Form. Okay, let's create that Form that php, once again, set the namespace app\Http\Forms, and then class Form. Okay, so first off, we need to check if the data is valid. Next, I know we're going to have a general save method. So for example, down here, yeah, at some point, we'll say form->save(). And then that will be the method that performs the validation. And if it passes triggers your persist method. Okay. Alright, let's go back. So with that in mind, I'm going to have a save method. And we'll say do validation and call persist if it passes. Now what else to check if it's valid? Well, in this case, Laravel already has our backs, it has a trait called ValidatesRequests. So I'm going to defer to that, and then import that trait. And yeah, once again, that's called ValidatesRequests. And I will import that use Illuminate\Foundation\Validation\ValidatesRequests.

So I'm going to defer to that, and then import that trait. And yeah, once again, that's called validatesRequests. And I will import that use Illuminate\Foundation\Validation\ValidatesRequests. So it's cool by importing that we get a lot out of the box, it will do the validation for you. And if it fails, it's going to automatically throw an exception, which behind the scenes, Laravel will catch that and then return a response, set the errors and go back. Pretty cool stuff. Okay, so we need to validate the request. And then we also need to compare that against our rules. So that's two other things we need, we need our request. And then we also need our rules. And on this note, you might want to make rules a method that returns an array. And then that way you could enforce it. For example, you could say abstract protected function rules(). And then that way you make every subclass implement that you can't make a property abstract. But yeah, this

you could enforce it. For example, you could say abstract protected function rules. And then that way you make every subclass implement that you can't make a property abstract. But yeah, this is fine for now. Okay, so we validate and then return true. Remember, if this fails, an exception will be thrown, and you'll never get to this point. Okay, next step, how do we fetch the request? Well, we're gonna have to pass that in like this. Use I don't have my tags file for this. So I have to manually do it Illuminate\Http\Request. Okay, but here's another cool thing we could do. So yeah, we could assign it $request equals request. But it's going to make our interface for our API a little less ideal to work with. So for example, you're going to have to new this up. But you're also going to have to pass through your request. Or if you pass that through up here. Either way, you have to pass that through. Not a big deal at all. But from my experiences,

But you're also going to have to pass through your $request. Or if you pass that through up here. Either way, you have to pass that through. Not a big deal at all. But from my experiences, it's really important to focus on these little things. Ideally, it can find that automatically. So why don't we do this? For testing, we're going to pass it in. But if you don't pass anything in, well, could we just do this? If you gave us a $request, use it. Otherwise, we're just going to fetch it ourselves. Now, a lot of people would say this kind of approach is blasphemy. But you know what, I think it's pretty great. So to each their own. Anyways, now we have the $request, we have the $rules that will be defined on the subclass. What's the next step? Well, we talked about how a save method is what you will call from your controller or your routes file. So right here, we say do validation, I could say if this is valid, then persist.

we talked about how a save method is what you will call from your controller or your routes file. So right here, we say do validation, I could say if this is valid, then persist. And we'll return the results. Otherwise, we're going to return false to give some kind of indication that it didn't work like they wanted. And you know what, let's grab this and bring it up. How about right here? And yes, save will be the main method you call, which means isValid can be a protected function. Alright, so why don't we review this workflow? Again, I'm going to show you this in multiple steps. So just come along for the ride, we would new up a form somewhere, and we don't have to pass the request through anymore. So I can leave that off. Next, we could call form, save, and then do something like return success. Okay, so let's review exactly what's happening here. We new up the form, we take a look at that, nothing here. So we go to the parent.

call form, save, and then do something like return success. Okay, so let's review exactly what's happening here. We new up the form, we take a look at that, nothing here. So we go to the parent class. When we new it up, it accepts a request. But if you don't provide one, we'll just fetch it with Laravel. Okay, next, we call a save method. So what does that do? Well, right down here, we check to see if the data from the request is in fact valid. And if it is, you're good to go. So we will call a persist method. And actually, on that note, we should make sure that the User provides that method, right? So let's do this. public, but make it abstract. function persist. Remember, anytime you have an abstract method like this, what you're saying is it's mandatory that a subclass implements this method. In order for me to work, you have to add the method. And then further, anytime you define an abstract method, yeah, you want to make the class abstract.

that a subclass implements this method. In order for me to work, you have to add the method. And then further, anytime you define an abstract method, yeah, you want to make the class abstract itself. You should never new up the Form class itself, you'd always new up the subclass. Anyways, back to work. So if it's valid, we call a persist method. And that would defer to here. And in this case, yeah, here's where you would do like Post::create. And then maybe if a Post has a Subscriber, like an email Subscriber, you might add the currentUser automatically to that, it doesn't matter. Any logic here for how that form data is persisted would go there. Now, if we switch back, if it's not valid, well, this is a little magical, and you might want to get around to this. So it's magical in the fact that we know behind the scenes, an exception will be thrown. And behind the scenes, Laravel will catch that exception and redirect back with the

get around to this. So it's magical in the fact that we know behind the scenes, an exception will be thrown. And behind the scenes, Laravel will catch that exception and redirect back with the errors. But if you weren't familiar with Laravel, you wouldn't necessarily know that you'd really have to dig in. So if you want to make it a little more clear, I really don't mind this. But if you want to make it a little more clear, yeah, you could do something like this, try to validate this, but then catch any exception or any ValidationException. And well, you could return false if you want. But you might want to set the errors as well. So what you could maybe do is set the errors here, for example, get the validator. And then I'm trying to remember is it getErrors? Or is it errors? It might be errors. Anyways, you get the basic idea. So if you want to intercept that, and then assign the errors to

So they call a save method. And we say, if everything's valid, then persist it and return true. Otherwise, return false. And truthfully, it wouldn't even matter because we are throwing an exception here if it fails, and we're not catching it, we're letting Laravel handle it. But if you did want to catch it from your controller or your routes file, this can be useful. Okay, let's talk about a couple more things. So we have persist. But what about getting the request data itself? So how do I grab the attributes here? Well, maybe we could add on a few methods here. So for example, how about right here, method, maybe you could add a method called fields, and then that would fetch your request data like so. Now, if we were to come back, yeah, you could say Post::create(this->fields). And that will give you an array of all the fields from the form. Another one would be well, what if you let's get rid of that? What if you knew up the

back, yeah, you could say Post::create this fields. And that will give you an array of all the fields from the form. Another one would be well, what if you let's get rid of that? What if you knew up the form at Post? Yeah, maybe you could do something like this $post->body, and then just access it directly as a property on the form object. Could we do that? Totally. If you want to do that, you would need to add a getter method like this get. So basically, if you're not familiar with this, this method will be called if you try to access a property on an object that is not defined. So in our case, we say $this->body, we don't have any defined property called body. So this method will be called, and the property name will be passed through. That means this is a good candidate for deferring to our request. Like so, or in fact, you could even get away with just doing this, by the way.

Testing Validation and Errors13:33

If it's successful, we will persist it. And remember, in this case, all we're doing is var_dumping. But yeah, anyways, we'll persist it, and then we're just going to return it. Okay, so why don't we try it out? Let's do the full class path. And if I switch back to the browser, here's our stupid form, enter some gibberish here, and there you go. It validated. But what if it did not validate? So let's leave nothing there and add the post. Well, yes, Laravel redirected back, but we're not displaying the errors. Well, that's fine. That's not related to this at all. You could just add an error at all. You could just add an errors partial, or I'll put it here. If count($errors), then we'll say foreach $errors as $error. And then yeah, just echo it out within a list item. Okay, let's try it again. Add post. And now we see the error.

then we'll say for each errors all as error. And then yeah, just echo it out within a list item. Okay, let's try it again. Add Post. And now we see the error. But if we pass validation, we would then move on to persisting the Post. So yeah, kind of an interesting approach, right? Especially outside of your Laravel projects, creating a form object is a good choice when you don't necessarily want to put it in the controller and there's more going on than just a simple use case. Yeah, this is a cool way to go about it. So once again, the responsibility of this form is to persist the data from a form block within one of your views. Nice and clean. Oh, and by the way, I should show you this. Let's say save. And then the body to the DB, just to show you that yes, we can use that magic method. So come back to Chrome, go back, the body here and Post and we are able to fetch that.

Switching to Form Requests15:06

Let's say save. And then the body to the DB, just to show you that yes, we can use that magic method. So come back to Chrome, go back, the body here and post and we are able to fetch that. And then the same thing for dump, we added a fields method. So this fields will give you an array of all the fields from the form. Useful. So good, we've learned a new pattern here, creating form objects. But what about Laravel's form requests? How does that work? Well, let me show you. Let's do php artisan make:request and do the same thing, PublishPostRequest. Okay, so if we take a look at that, at HTTP\Requests, PublishPostRequest, we can see it's kind of similar, right? You define the rules. There's even a section for authorization. I'm going to set that to true by default. But it's interesting, right? This is fairly similar to a form object. We are just thinking of it more as a request object. So what if we did

I'm going to set that to true by default. But it's interesting, right? This is fairly similar to a form object. We are just thinking of it more as a request object. So what if we did this? What if we renamed it to PublishPostForm? And let me rename the file itself. Now we can sort of get around the mental burden of thinking of this object as a request, and instead more as a form object. So let me create a split here. And we're going to go to PublishPostForm. Here's our old one. We're now going to bring that over like so. And then just return that. Next, this persist method, we're going to yank that and bring that over. And in this case, it's not called fields, it's called all. But other than that, with a form request object, it's the same thing. So here you could say this->body, and it'll still defer to the body on the request object. Exact same thing there. Okay, so now I can close that one out. And this

object, it's the same thing. So here you could say this body, and it'll still defer to the body on the request object. Exact same thing there. Okay, so now I can close that one out. And this looks pretty similar. So a PublishPostForm has our validation rules, it performs the validation automatically for you. And then we have the logic for how it's persisted. So let's go back to our routes file. Here, we no longer have to new it up. And actually on that note, you also could have had Laravel new it up for you using the automatic resolution feature. So yeah, if you had done this, then you could have simply stuck with that. Anyways, though, let's switch this over to a FormRequest. And now here, I'm just going to call persist. Let's try this method out now. So back to the homepage, I'm going to add a post but fail validation. And sure enough, we redirect back, that's done automatically with a FormRequest. But this time, we will pass

So back to the homepage, I'm going to add a post but fail validation. And sure enough, we redirect back, that's done automatically with a form request. But this time, we will pass validation, add the post. And once again, we move on to that persist method. And remember, that is specifically where you would write the logic for how you persist this form. So yeah, you would do something like post::create and then pass through your request data. If you needed to do more than that. Remember, this only makes sense if you have a slightly more complicated structure. Or in other words, in this particular example, it's so easy. And I know we're getting off track, but it's important to note this. It's so easy to within your controller, say validate the request against the rules. And then continue on with your logic. And yeah, like in this case, that would be your request data. Let's add that here. And then you would pass that through here.

against the rules. And then continue on with your logic. And yeah, like in this case, that would be your request data. Let's add that here. And then you would pass that through here. In this case, that's so simple. It's two lines of code. This is what I would recommend you do. But once again, when your rules are 10 items long, and you have special rules and special situations, and then you're not creating a record in one table, but you're actually updating two or three different tables. Those are the times when yeah, you can create a dedicated FormRequest. And then all you have to do is add a method on it and call it from your controller. I'm going to undo until we get back to that state. So what have we done here? Well, first, we created an implementation of a form object from scratch. And then we switched over and realized that Laravel's form request objects are basically the same thing. And there's nothing prohibiting you from

of a form object from scratch. And then we switched over and realized that Laravel's form request objects are basically the same thing. And there's nothing prohibiting you from adding a persist method directly to that object, and then storing your logic here.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟