Playing
01. Cleaning up code with dynamic method calling

Episodes

0%
Your progress
  • Total: 17m
  • Played: 0m
  • Remaining: 17m
Join or sign in to track your progress

Transcript

00:00
So today, I'm going to show you a couple of ways that you can clear up your code if you have multiple splits based on a piece of data. So for example, you might have a webhook controller,
00:11
and you might need to take the data that you get through from a service like Stripe or Braintree and decide what to do with that. For example, if the user's card was declined
00:20
or if their subscription was canceled. And this can apply to absolutely anything. I'm just using this because this is a very common example. So first, let's take a look at this,
00:28
and then we'll dive into exception handling because I find this particularly useful. I'm just going to go ahead and create a fake controller out in this app.
00:36
I've just gone ahead and auto-loaded everything in here. So let's create a webhook controller inside of here and take a look at how we would typically deal with multiple forks based on a piece of data.
00:49
So let's create this webhook controller out. Very, very simple. And what we're going to do is just have one handle method in here
00:55
that's going to handle it. Now, if you're used to working with frameworks like Laravel and you've dived into a package like Cachier, Cachier does exactly this.
01:03
So you may already be familiar with this, but stick around if you want to find out how to do a similar thing with exception handling. So let's imagine that when we have a webhook come through
01:13
from our payment processor, we have a couple of actions that we might want to take in here. So let's just go ahead and new this controller up,
01:20
assign this webhook controller. And of course, let's just create some kind of request data in here that we would pass through.
01:28
So let's just fake this by casting this array to an object. Perhaps we have some kind of type that comes through in this webhook. So for example, card underscore declined,
01:37
and this could be in any format. It's up to you how you kind of format this to make it work. So let's just add some more data in here. Let's just say user one.
01:47
It doesn't really matter in this case. So we would go ahead and we would call the handle method on the controller. Of course, you would use some kind of routing system
01:55
or container to do this for you, and you would pass that request in. So if we come over to the webhook controller, so let's just do a quick var dump on request
02:03
and see what we get. So if we pop over to the browser, it looks like that's not found. So let's go ahead and pull the namespace in for that.
02:11
So we have this data then. Based on the data that we get through to the webhook, which of course can vary, we need to go ahead and take an action.
02:18
So for example, the, you know, not easiest way, but the first way that we might go ahead and do this is check if the type is card declined. So that would be one way to do it.
02:28
And then you would take any action in here, maybe disable the user's account, send them an email, you know, do pretty much anything else. Now in this case, you could also then say,
02:37
well, else if the request type is something else, and you can kind of see where I'm going here, this is going to start to get messy. Let's say subscription canceled, for example.
02:48
So of course, this is not really great for the future. If you add more actions, you're going to have to add more else ifs, and you're going to end up in a huge mess.
02:56
You essentially will have one handle method, which deals with every single type of request type, which isn't great. Now, another way to split this out would of course be
03:06
to just tidy up the if else. So you could take the if statement here, get rid of the else, do this. And then of course, at some point in here,
03:14
you could return. So this doesn't continue through and hit the other if statement. But of course, this doesn't really matter
03:21
because we're specifically checking a type. So you wouldn't really need that anyway. Either way, the concept is the same. We are still going ahead and making an if statement
03:29
for every single request type. Now, of course, another way to deal with this would be to go ahead and create a method for each one. So for example, handle card declined,
03:40
and we'd go ahead and maybe accept the request in there so we can read any data from this. So if we just do a var dump in here, handle card declined.
03:51
And in here, of course, what we would do is we would call this handle card decline. That's another way to do it. And you would do the same thing for here.
03:58
Let's just pull this in as well, just so we have it. So handle subscription canceled. So that's one way that you could fix that up to spread this out and make it a little bit easier
04:08
to add to. But ideally, what you want is the subject of this video, dynamic method calling. So let's just switch this over.
04:17
So handle subscription canceled, just so we can see which one is being called. So if we just preview this in the browser, just to make sure we're on the same page,
04:25
and we go ahead and pass that request in, of course, we get handle card declined because that's the type that's coming through. And this is acceptable, but there's still things
04:34
that we could do to improve this. So what we're going to do is pretty much get rid of all of this. We're still going to keep the two separate methods
04:40
because it's nice to have these separated out. What we're going to do is we're going to convert the type over to a specific format. And by this, I mean taking card underscore declined
04:52
or card hyphen declined in whichever format it's already in and change it over to something like card declined like this. So we can dynamically call a handle card decline method
05:03
or handle subscription canceled method. So in this case, we need to work out how to call that method dynamically. So we need to change this format of this piece
05:14
of data over. So the first thing that we're going to do is replace the underscore with an empty string. If you're working with a framework like Laravel,
05:20
there are helpers to allow you to do this, but of course we're not, we're just working with PHP. So we don't really have that benefit. And then what we do is we just say uppercase words
05:29
and we pass in the request type like so, and we can pass an optional argument in here, which will go ahead and uppercase the word separated by a particular thing.
05:38
So we're removing underscores and we are uppercasing words split by a underscore, which is really helpful. So if we just do a var dump on the method here, what we should get is card declined in this case.
05:53
So what we can do is call handle and then card decline because we now have that method in the format that it's in. Now, really importantly, the first thing we want to do is check if that method actually exists.
06:04
There's no point in trying to call this if it doesn't exist. So we're going to go ahead and check inside of this object if a particular method exists.
06:12
And that is, of course, handle, and then we can concatenate on the method name that we've got. So, of course, this will be handle card declined,
06:19
handle subscription canceled, et cetera. Now, in this case, what we could also do is apply or assign the concatenated string here to a handler while we're checking this.
06:32
And then it's very, very simple to just say this handler like so, and then we obviously invoke that method inside of this object. And then we pass the request in.
06:42
So that's going to have exactly the same effect as we saw before. And now if we get any new kind of format in, for example, subscription canceled,
06:53
it's going to work in exactly the same way and it's going to handle that method dynamically for you. And the beauty of this means you don't have to update your handle method whatsoever now.
07:01
If there's a new type that you want to handle, you simply go ahead and add a new method in, and that's going to go ahead and handle that for you. So this is just one really simple way
07:11
of handling multiple different types of data, dynamically calling a method, and keeping things really, really tidy. You literally will not have to update anything here.
07:20
Now, another thing I want to speak about is exception handling, because exception handling can be incredibly messy. There are improvements to PHP 7,
07:31
which means that you can capture multiple different types of exceptions all in one go. And these kind of clear things up. And I've covered that before on CodeCourse.
07:38
But what we're going to do is we're going to look at a payment controller where we may have different types of failed payments. We might have a card declined.
07:46
We might have some other kind of reason. So let's just create this controller out and let's just take a look at how we can beautifully handle exceptions.
07:53
And this is probably one of the best ones to go ahead and implement into your application. So let's go ahead and create a store method inside of this payment controller.
08:03
This will, of course, start to process the payment. And let's come over to index and new up this controller and do exactly what we did before
08:09
just to kind of test this out. So let's say controller store, and we won't bother passing any request data in. There's not really much point.
08:16
So let's go ahead and just do a var dump in here just to make sure this is all working. And sure enough, it's not. So let's go ahead and pull the namespace in for this,
08:25
like so. Okay, so let's say we had some kind of method. This might be a separate class to handle making a payment. It could be literally anything.
08:35
But I'm just going to implement this as a method inside of here called make payment, which could potentially throw multiple exceptions. For example, a card declined.
08:45
It could literally be anything. So inside of here, we'll throw the exception. So in here, what we're going to do is try and catch this. So we're going to do a try on this make payment,
08:54
which would, of course, start to process the payment. And then we want to catch some kind of exception. So let's just catch any exception in here, and then we'll just do something.
09:02
So var dump, and we'll just say failed, like so. So inside of here, let's just throw a new exception, and we'll just make sure exception is pulled in into our class.
09:14
Give that a refresh, and of course, we see failed. So that's pretty straightforward. But like I said, if you have multiple different types of exceptions,
09:22
you're going to want to handle these in a nice way. So for example, you might even have a method in here called handle card declined. And what that would do
09:32
is perhaps return a specific response. And I find this particularly useful when I'm building out an API. I want to return a particular response in here
09:39
based on the card declined. Plus, I might also want to do something else. I might want to log something. I might want to notify myself.
09:47
I might want to do anything. So it's not always the case. You know, you could make this nice without doing this method,
09:53
but it's a good way to split things up. So in future, if you need to make an action inside of here, you have that option. So let's go over to our app
10:03
and just create an exceptions directory. We'll just make this pretty rough. And let's create the payment or the card declined exception inside of here.
10:12
So let's say card declined.php. This of course is just gonna be a really simple exception. So card declined. And let's go ahead and pull the namespace in for this.
10:22
And of course, extend our base exception so we can throw this. And you could include a message in there if you wanted to. So now we have our card declined exception.
10:32
We could of course catch a card declined exception in here. Let's pull the namespace in for this at the top. And we can in here throw a card declined exception. And that's gonna have exactly the same effect.
10:43
But when you want to catch another type of exception, of course, you're gonna have to chain this on. So for example, you know, card not valid exception could be literally anything.
10:52
I'm not gonna add these all in now because you get the idea, but that's potentially what you could end up doing. So what we want to do is to have a similar method
11:00
that we had before where we went ahead and from the exception, perhaps extracted out the identifier for this. Now a perfect identifier for an exception
11:09
is the short name of the class, e.g. just card declined. And then we can say handle card declined. This of course is the name of the exception
11:18
and handle is just a kind of generic prefix to each of your methods. So how are we gonna do this then? Well, let's go over to our card declined exception
11:28
and let's add in a method in here to get the short name. And then we'll extract this out to a trait to make things a little bit more reusable and tidy. So let's say get short name
11:37
and we're gonna use the PHP reflection class for this. So let's go and create a new reflection class instance, pass this class in and then we can use the get short name method.
11:51
If we go ahead and pull in the reflection class namespace, we should be good to go. So what this is gonna do then, if we just do a var dump on e get short name,
12:01
that's gonna give us the following. So card declined. And of course, as we already know, we can use this to prefix handle
12:08
and then dynamically call a method. So let's do that first and then we'll look at tidying this up a little bit further because how we've set things up at the moment
12:15
isn't too maintainable. So let's say if the method inside of this class exists and this is handle and then of course concatenating on e get short name,
12:25
like so, then of course, at the same time, we want to assign this to handler. We wanna call this handler, like so.
12:34
And of course, what we would probably want to do is pass the exception in because that might have information that we need. Now, we could also pass in other things as well
12:44
so you can pretty much decide how your application works and do that accordingly. So let's go ahead and just do a var dump in here. So let's just say handle card declined
12:53
and let's give that a refresh and sure enough, that works as we would expect. Now, at the moment, this is not really too reusable because if we had another exception,
13:02
we would of course have to duplicate this method. So a really nice way to do this would be to break this up into a trait. You can of course place this trait anywhere.
13:10
A get short name trait is very, very useful so you could use this as a kind of generic trait. And I'm gonna call this exposes short name. Of course, you can choose whatever you want.
13:21
Let's go ahead and create this out. So exposes short name. And of course, all we're gonna do here is just take that method that we have here,
13:29
pop that into this trait. We're gonna go ahead and make sure we pull the reflection class into here so we don't get any namespacing issues.
13:37
And of course, what we're gonna do is inside of card declined, we're gonna use exposes short name. Pull the namespace in for that.
13:43
We get exactly the same thing. We've just gone ahead and allowed ourselves to reuse this in another exception if we want to throw one. Now, this is all well and good,
13:53
but what we're doing here is we're saying catch card declined. Well, what happens if we want to catch another type of exception?
14:01
Well, in this case, we're gonna have to pretty much replicate the code that we created to make this clean in the first place, which doesn't make any sense whatsoever.
14:09
So this is say card invalid exception, whatever you wanna call this. It's just basically duplicating all this code over. So in this case, what we could do
14:19
is create perhaps an abstract payment failed exception or payment exception. So let's go ahead and create payment exception.php. And let's go ahead and make this an abstract class
14:30
because we probably don't want to instantiate this on its own. And let's say payment exception. And of course, this is going to extend PHP's base exception.
14:40
Let's pull the namespace in for this. And we don't really need anything in here. You could add kind of helper methods in here if you wanted to.
14:47
But over in the card declined exception now, this can extend the payment exception rather than the PHP base exception. You can pull the namespace in for this.
14:55
And now what we can do is generically catch a payment exception. And you know, that's gonna work as normal. So now we can switch this over to payment exception.
15:04
Let's go ahead and pull the namespace in for this. And we should get exactly the same thing. Let's just have a look here. Of course, over here, we just need to make sure
15:11
we pull in the exception class. So that works in exactly the same way. The benefit you have now is with make payment, let's say we wanted to throw a card declined exception.
15:20
Well, of course, we might just want to say card was declined. This might be the message we end up using for the user on the API or on the front end.
15:28
And of course, what that's gonna do is it's gonna allow us to handle this, take in that exception. We could go ahead and type in this as a generic exception,
15:36
or we could type in this as a payment exception if you wanted to. And of course, what we can do is var dump out eGetMessage. And you could include that in your API response
15:45
or include that on the front end. And you can see that here. So just a nice way to handle something like this. If you have a payment controller
15:52
or some other similar controller that really just only makes one action and an important action where you want to efficiently catch each exception,
16:01
you can go ahead and do this now. So we grab the type for a particular payment exception, and we have multiple ways to handle it. And remember, you don't just need to call this method.
16:09
What you could have is, say, getCardDeclinedResponse, like so. And you could even go ahead and grab the short name for this. So you could take this like so,
16:21
go ahead and grab this out and call this maybe response. And you could say getE.shortNameResponse. And of course, that would be a capital R.
16:34
Now what you end up with is the ability to extract the particular response that you want out. So if we just do a var dump on that response and we fix this up to get rid of that,
16:46
we should see getCardDeclinedResponse. Now what you can do is go ahead and return a response, maybe whichever framework or if you're working with raw PHP,
16:57
and you can go ahead and include that in here. So you could call that method as part of your response. So you might say something like data response, or in this case, you would say this response,
17:08
and you would invoke that. And that would, of course, return a response from here and output it. Regardless of how you do this,
17:14
dynamic method calling is exactly the same. You have some similar way to get the method name for each thing that you want to call, and you go ahead and call that if that's the case.
17:24
So two kind of scenarios where I've used this before, and it's really nicely cleaned things up by being able to extract these out to different methods by grabbing the method name,
17:35
either by grabbing the short name of the class or by doing what we did over in our webhook controller where we used string replace and uppercase words to grab out the part of the method that we wanted to call.
1 episode 17 mins

Overview

Here's a popular pattern for diverging and dynamically calling a method based on an event, instantly cleaning up huge lists of conditionals.

Alex Garrett-Smith
Alex Garrett-Smith
Hey, I'm the founder of Codecourse!

Episode discussion

No comments, yet. Be the first!