تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

مرور مرور فصل (و دمو)0:07

As I mentioned in the last video, breaking up big blocks will probably be your biggest motivator to refactor as such. Let's review the process one more time as well as apply the other practices we've learned like avoiding nested code. Here we have another method within a Laravel controller, and relatively speaking, it's a big block of code. So let's go through the process of breaking it up. First I want to review its level.

Define Level and Role0:30

So let's go through the process of breaking it up. First I want to review its level. We already said this was a controller, and again, we know that's responsible for handling the request and returning a response. Now looking at the role of the store action and this being the EnrollmentController by convention, I would assume that this stores or enrolls a User in a particular course. So let's change this request response to

Identify Sub-blocks0:54

or enrolls a User in a particular course. So let's change this request response to enroll a User. So our level is the controller and our role is to enroll a User. Alright, moving on to the next step. Let's look through each sub block and give it a quick temporary comment. So this looks like validate the request data. Here we pull the

So this looks like validate the request data. Here we pull the Course model. This next block is an if statement. So this looks like this enrolls an existing User. This next block seems to create the User. This next block seems to enroll the User. We log them in or log in new User,

We log them in or log in new User, and then finally we redirect to course page. Alright, let's use PHPStorm to collapse these real quick and let's just look over our sub blocks. So we validate the request data, we pull the Course model, we enroll an existing User, otherwise we create a User, we enroll them, we log them in and redirect to the course page. Now we've regrouped all of these blocks.

and re the course page. Now we've regrouped all of these blocks. We know the level we're armed with more information to start to refactor. Now it's a matter of going through each sub block and asking those two questions. Can this be done in a native way and does this code belong at this level? All right, so let's start at the top here and I'll go ahead and re-expand all of the code.

Extract Request Validation2:54

All right, so let's start at the top here and I'll go ahead and re-expand all of the code. The very first sub block is validating the request data. Now, if we ask ourself the first question, can this be done in a native way? Now, on the surface it might only seem like that answer is yes. We are using Laravel's facade to validate the data. We're not validating it ourselves necessarily, but we could streamline this a bit.

We're not validating it ourselves necessarily, but we could streamline this a bit. First of all, instead of using the facade, we could just directly use the request object. So we could say, request, validate and pass it in that data and then remove the chained method. This would achieve the same thing and it does streamline the code slightly. Many might leave it as this, but it's still a relatively large block of code.

Many might leave it as this, but it's still a relatively large block of code. Again, compared to the rest of the code within this big block, it's one of the largest blocks only second to the creation of the user. So if we moved and asked ourself, does it belong at the current level, you could also argue yes, but we've entered a gray area now for both of these questions.

yes, but we've entered a gray area now for both of these questions. We've said, yes, we're doing it a native way and we are. And we said, yes, validation belongs at this level, but if we push ourself, we can find a solution that gives us a resounding yes to both of those questions. Laravel has a FormRequest object and it's responsible solely for validating a request. Given that that object exists and provides not only a more native way,

Given that that object exists and provides not only a more native way, but another level for this code to exist, I would advocate using it, especially when attempting to refactor big blocks. So we could take these validation rules and remove them completely and put them on a FormRequest object, in this case a StoreEnrollmentRequest, and we'll just make that class in the background.

Use Route Model Binding4:56

in this case a StoreEnrollmentRequest, and we'll just make that class in the background. Moving on to the next sub block, we pull the Course model. This basically just finds that Course based on the ID passed into the controller. Now is there a more native way we could do this? Again, we are doing it a native way, but is there a more native way? And the answer is yes. We could actually use implicit route model binding.

And the answer is yes. We could actually use implicit route model binding. So instead of passing in the id, we could type in this to be the Course model and go ahead and change this variable name. The only place we were using id is here. So we could actually drop all of this code and the rest of the code will behave correctly as it was already using a course variable. Now we're left with the two different code paths.

as it was already using a course variable. Now we're left with the two different code paths. First, if you're already authenticated, we enroll the user to the course and redirect them to the course page. Now, if we actually scroll ahead to the other sub blocks, we see that we perform pretty similar behavior down here where we enroll a new user and redirect them to the course page. Now there's some other logic that exists between this and given the duplication, I'm gonna go ahead

Now there's some other logic that exists between this and given the duplication, I'm gonna go ahead and focus on those first. So let's see if there's a more native way we can create a new User. Again, we're already using eloquent create method. I don't think you're gonna get much more streamlined than that. So moving on to the next question, does it belong at this level?

Move User Creation Logic6:31

So moving on to the next question, does it belong at this level? Now you might think yes, because we're using request data, but reading at a controller level, do we really care exactly how a User is created all the way down to the details that the password is hashed? Probably not. If our goal is to refactor this big block into a more streamlined or more readable version, then we could extract this relatively big block somewhere.

or more readable version, then we could extract this relatively big block somewhere else or to another reading level, and the immediate candidate is the User model itself. We could make a method called create from enrollment and instead simply pass it the request itself, or better yet, the validated request data. So I think we've done pretty well with that sub block. Moving on to the next, we see that there are two lines here, but one is actually a duplicate of the previous line.

Moving on to the next, we see that there are two lines here, but one is actually a duplicate of the previous line. We're just passing it the authenticated user instead of the user we just created. The only difference actually is this line where we add the user to the course organization. We could leave this separate, but here it feels a little out of place. Or more specifically, it doesn't feel like it belongs at this reading level.

Or more specifically, it doesn't feel like it belongs at this reading level. Given that it's on the course and it's part of enrolling the User, I would argue that this particular block of code probably belongs within this method. So I'll abstract it to there as well. This gets us closer to just the duplicated code within the if block. So we'll go ahead and remove our temporary comment.

to just the duplicated code within the if block. So we'll go ahead and remove our temporary comment. Finally, we log in the User and we redirect them to the course page. Neither of these can be slim or belong elsewhere. So let's remove these temporary comments. Now, the only difference between these two code paths is creating a new User and logging them in. The primary action of this controller is to enroll the User.

Collapse Duplicate Code Paths8:31

and logging them in. The primary action of this controller is to enroll the User and redirect them to the Course page. So I'm gonna change this comment to say, enroll a User and redirect to Course page. So going back to this sub block and knowing that we have have duplication, how can we collapse this together? Now, making this leap might feel a bit challenging. We've already broken things into sub blocks,

Now, making this leap might feel a bit challenging. We've already broken things into sub blocks, so taking another pass and asking the same questions may not get us any farther in these scenarios. What I like to do is write the code. I'd like to see based on this primary action and knowing the reading level of the Controller, let's just break out some pseudo code. So ideally we would say something like Course::enroll().

let's just break out some pseudo code. So ideally we would say something like Course enroll user, and of course we need to pass this, the user. And as far as redirecting to the course page, well, we already have that code. So let's just take this and put it here. Now, this isn't really pseudo code. This is exactly the code we need to redirect to the course page so we can go ahead

This is exactly the code we need to redirect to the course page so we can go ahead and remove it from these other code paths. Let's come back up here and remove our temporary comment as well. Now we really need to focus on this last remaining piece. What does this dot dot really need to do? We can go through the same practice again, let's just write a comment for what this needs to do. Enroll an existing User

let's just write a comment for what this needs to do. Enroll an existing User or create a new one. The result of this is what we need to pass to enroll User. This would allow our pseudo code to become real code. Again, it might sound hard on the surface, but by going through this process, this code actually exists right here. So I'm gonna cut it and actually put it into a private method.

So I'm gonna cut it and actually put it into a private function. So let's say private function, and for now, I'm just gonna spell out what this method is going to do. It's basically gonna create User if unauthenticated, and we'll need to pass it the request data, and I'll just paste in everything else. Now remember, this was our original code. It's not responsible for enrolling the User anymore.

Now remember, this was our original code. It's not responsible for enrolling the User anymore because that's what this is calling. It's responsible for returning the User that's passed to the method. So in the event that we are already authenticated, then we would simply return the authenticated User. If we weren't authenticated, then we would create them just as we were before from the request data. Again, we no longer would need to enroll them directly as

as we were before from the request data. Again, we no longer would need to enroll them directly as that's called after this method. So we go ahead and log them in and return that new User coming back up to our original method. We can now call the method that we just created. Let's get rid of our temporary comment and we actually don't need a semicolon there to that point, let's jump out and format this code vendor/bin

and we actually don't need a semicolon there to that point, let's jump out and format this code blue vendor/bin/pint, paste the path. And there we go. Alright, let's get rid of all of our temporary comments and extra line breaks and let's review this. All of this is broken up a relatively big block of code for the store action to two simple lines. Now, it's important to remember that we didn't just move the code somewhere else.

Now, it's important to remember that we didn't just move the code somewhere else. We still refactored all of the individual sub blocks that were originally in the big block of code, therefore making them readable as well, but potentially reusable by other pieces of the application. This may make it easier to refactor the next big block of code you have.

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