Introducing Guard Clauses0:00
Hey there. Welcome back. This is going to be a very quick one. A very simple but powerful concept we're going to use throughout the years is guard clauses. You might not know guard clauses by this name, but I'm pretty sure you've probably used it at some point. We typically use guard clauses to avoid deep nesting within functions. So imagine that you have a function within your model and it has two code paths.
So imagine that you have a function within your model and it has two code paths. A simple way to refactor it is to just have an early return at the very top of a function. Therefore, you can remove an if block and just have a condition at the very top, return early, and then have everything else deal with the second condition. That's one of the most common ways to use guard clauses. Another usage for guard clauses is to ensure
Validating State Early0:41
That's one of the most common ways to use guard clauses. Another usage for guard clauses is to ensure that you have valid state. For example, you might have a method that takes some parameters and you wanna make sure they fit within your business rules. We're going to talk a little bit more about that in the future within this course when we talk about the main variance. But for now, I wanna show you some examples of
Client Creation Redirect1:01
about the main variance. But for now, I wanna show you some examples of how we can use gar clauses to make our code a little bit more straightforward, more predictable and safer. So let's jump into the code. One of the functionalities of our application is to add clients. Pretty simple stuff. If a look at ClientControllerTest, we have a test
Pretty simple stuff. If a look at ClientController test, we have a test to ensure we can create a Client and if we run this, it is going to pass. And we also have a test to ensure that if we try to create a Client that already exists by email, that's our unique identifier, we do not want to return a validation error. And we also do not want to duplicate the Client. In fact, we just want to redirect the user.
And we also do not want to duplicate the client. In fact, we just want to redirect the user to the existing client. And if we run this one, it is going to fail. So let's see how we can fix this with a very simple guard clause. Let's go back to our ClientController and write something like this. We're gonna fetch the existing client, so we're gonna say team, to fetch the current
We're gonna fetch the existing client, so we're gonna say team, to fetch the current team from the logged in user. We're gonna look into its clients. We want a client with an email equal to the one that was given, and we wanna fetch the first record. Now we can just add Eager clause. It says if we have an existing client, we want to redirect to reroute, and we're gonna say clients.show and pass that existing client.
to redirect to reroute, and we're gonna say clients.show and pass that existing client. So if we run the task, it now passes. And we could even make this shorter by inlining this. So we could go here and we could say if team.clients or email equals the given email. If we can fetch one and we can put this into a variable and get rid of this, if we run this, it still passes. So this is, although a very simple one is still a guard clause, we have two code paths here.
Fail Fast With Exceptions2:55
So this is, although a very simple one is still a guard clause, we have two code paths here. One of them is to redirect the User and we're returning early within this condition and we have the second path, which is to create the client. Let's look at a different example now where we did not want to have a successful action. We want to fail early. Let's go back to our PaymentScheduleTest. And we have this task which is passing.
Let's go back to our payment schedule test. And we have this task which is passing. This is the one we worked on in the previous lesson and it added a new task. Even though we can ensure we're getting an integer for the number of installments, that's not enough for it to be valid. For example, we did not want negative installments. In fact, we don't want negative amounts either. But let's start with this one.
In fact, we don't want negative amounts either. But let's start with this one. If I were to run this, this is obviously going to fail because we're gonna get a negative value, but I don't want this to run at all. I want this to fail. So let's see what we can do. I'm gonna say that I expect this to fail. So let's say expectException, and we're going to say InvalidArgumentException. That's one of the different exceptions that share with php.
and we're going to say InvalidArgumentException. That's one of the different exceptions that share with php. And we can get rid of this assertion. So let's run this. It is failing. It is failing to assert that an exception was thrown. And the reason this test is failing is because this code is not failing. So let's go to it and let's add a guard clause. We can say that if the numberOfInstallments is less than or equal to zero, we want to throw
We can say that if the number of installments is less than or equal to zero, we want to throw an InvalidArgumentException and we can say, number installments must be greater than zero. Got something like this, let's try this. And now it is failing. And we should probably do the same for the totalAmount as well. Let's add a new task.
for the total amount as well. Let's add a new task. Let's say that it does not accept negative amounts and then we can try and pass a negative amount. Let's try -1000 failing. So let's add another assert clause and say totalAmount. And you can say, amount must be greater than zero. We got this value instead. And it is passing. So if we run all of the tests, they out pass. Obviously we can refactor this a little bit.
Refactor Validation Checks5:13
So if we run all of the tests, they out pass. Obviously we can refactor this a little bit. So we could probably say if the numberOfInstallments is less than or equal to zero or if the totalAmount is less than or equal to zero, we want to throw an Exception. And then we can say, let's make this a little bit simpler. Let's just say numberOfInstallments and the totalAmount must be greater than zero. So at this point right here, we know
and the total amount must be greater than zero. So at this point right here, we know that we have a total amount, any number of installments that are both integers and that are both valid. So there's no path for a function to return incorrect or unpredictable results because we've narrowed down our arguments, our state, to something that's valid for this function. Let's get rid of this, rerun the tasks and they are all passing.
Wrap-Up and Takeaways6:03
Let's get rid of this, rerun the tasks and they are all passing. There are other ways we can achieve the same functionality as this, and we're going to talk about it a little bit later on the scores, but this is a totally valid way to ensure that your methods always have valid state. Alright, I told you guys this was going to be a quick one. Guard clauses are extremely simple but also very powerful and we're going to use them.
Guard clauses are extremely simple but also very powerful and we're going to use them all the time throughout the course. So get used to them. As usual, thank you for watching and I'll see you on the next video. Bye-bye.
