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

Dynamic transformer selection0:00

Now, in the previous episode, we had a good bit of refactoring, and we extracted all of these classes here, and we kept our tests at green, which is great. But you know what? I think there's still a good bit more we could do here, so let's keep pushing a bit further. Now immediately, if we go to our main put method, we can see that we construct the JavaScript, and then we initialize the variable, which in turn converts to JavaScript. Okay, so this is what I want to focus on. So you'll notice that for every single variable type, we are newing up a class. And in some situations, this is exactly what you want, where it gives every single class the ability to operate upon the value and convert it or transform it if it needs to.

And in some situations, this is exactly what you want, where it gives every single class the ability to operate upon the value and convert it or transform it if it needs to. In our case, though, there's only ever going to be one class that's responsible for converting this value. So if the value is a string, then we always want the string transformer. But at the moment, we're filtering through all of them and newing it up, which doesn't make much sense in this context. So let's see if we can make it more dynamic. For example, we could always say getType, and then let's var_dump that and just see what we get here.

For example, we could always say getType, and then let's var_dump that and just see what we get here. So we're going to run the test again. And yeah, you'll see that each type, well, you know what, why don't we just create a class that corresponds to each one of these. So you'll notice that we have, when you run getType, you're going to have things like array, string, null, double, I think there's one called resource that isn't applicable here, and then, of course, null and object. So let's keep working on this. Let's say strtolower and run it, and now everything's lowercase.

So let's keep working on this. Let's say strtolower and run it, and now everything's lowercase. And we could then say ucwords to capitalize the first letter. And then what if we were to say tack on Transformer there? You can see where we're getting. All right, so now we just want to make sure that there's a class that corresponds to each of these. So we already have most of them, but we will have to tweak a NumericTransformer to Integer, and then we'll have to set up one for Double. Okay, so if we were to save this to class, well, here's the next hurdle.

Instantiating namespaced classes1:53

and then we'll have to set up one for double. Okay, so if we were to save this to class, well, here's the next hurdle. We could say new Class like this, but I bet it's not going to work. We get Class StringTransformer not found. And that's weird, right, because we know that exists. And even weirder, if you were to say new StringTransformer, if you just hard code it, well, it's going to fail, but not because the class doesn't exist. So what's the deal here? When we reference it as a string, and in fact, I'll show you, if I were to say var_dump($class = StringTransformer), that's going to return true, right at the top, true.

When we reference it as a string, and in fact, I'll show you, if I were to say var_dump( $class equals string Transformer), that's going to return true, right at the top, true. So yeah, this can be confusing if you don't know how php works. If we are instantiating a class based on a variable, you have to use the full namespaced path. You can't omit that. So in this case, yeah, we would have to, well, we could, I'll show you a couple options here. We could do this. So we could grab that like this, and then we'll have to escape this. But yeah, that should be doable.

So we could grab that like this, and then we'll have to escape this. But yeah, that should be doable. We run it. And in this case, yeah, it's failing, but once again, for the right reason now. So in this case, there is no integer transformer. So let's fix those errors, and then we'll clean this up in a second. All right, so numeric, let's rename that to integer transformer. Run it again. And now there is no double transformer. Okay, so we're just going to copy this.

And now there is no double transformer. Okay, so we're just going to copy this. This is all kind of temporary, to be honest, because we're going to do a bigger refactor in a minute. All right, so we'll have double transformer here, and run it again. Okay, and now we're back to green. So at this point, think about it, if we switch back, we no longer have to filter through all of the transformers, and then give each one a chance to operate upon the value. Instead, we could just remove that entirely, and do it dynamically. All right, so we run that, and we're still going to get green.

Instead, we could just remove that entirely, and do it dynamically. All right, so we run that, and we're still going to get green. Cool. And then further, if we come up here, that means there's no need to hard code these anymore. So once again, run that again, and we get green. Okay, what else? Well, another option here, rather than hard coding this, we can just replace it with namespace. Okay, so let's run that. It fails. Transformers, yeah, it looks like we forgot the slash there.

It fails. Transformers, yeah, it looks like we forgot the slash there. All right, run it again. And yeah, now we're back to green, and this is a good bit more simple. So now what we can do is go to each of the transformers, and you'll remember before, they were checking the type to see if they should operate upon the value. But now, that can be removed entirely. Run it, and we still get green. And we can basically do this for every single one. So that, same thing here, null, object, we can get rid of that one on the top.

Replacing logic with json_encode4:35

And we can basically do this for every single one. So that, same thing here, null, object, we can get rid of that one on the top. And then string, same thing here. All right, so we run that, and we still get green. Okay, cool. Next up, though, so you'll see for ArrayTransformer, we can run it through json_encode. But as it turns out, we can pass a number of things to json_encode. So for example, here, in Boolean, whoops, we forgot to remove that one. Still a green, yeah. Anyways, for Boolean, yes, we can check the value and return a string.

Still a green, yeah. Anyways, for Boolean, yes, we can check the value and return a string. But as it turns out, you can also just pass the Boolean to json_encode, and it will do that for you. I'll show you. Let's do php, and then open a shell, and then just say json_encode(true). Whoops, sorry. I'm used to Laravel. Yeah, so we don't see it here, but we are getting a string here. So let's give this a shot.

we've refactored things to the point that they all do more or less the same operation. So let's try this again here. And if we run it, yep, everything's still passing. Okay, now object is a little more complex. We could encode down here, but we do have to check the type of some classes to see if they're encodable, I think. So we'll skip that for now. But string transform, okay, so let's see how this works. Right now, we are escaping things. So for example, if we were to just do the value like that, and run it, it's all going

Right now, we are escaping things. So for example, if we were to just do the value like that, and run it, it's all going to fail. But once again, let's just try encoding it, and maybe that will take care of the escaping in the process. Now in this case, it looks like it's failing, but if we compare the before and after, it's correct. It's just that when you encode it, it's using double quotes here. So let's go back to our spec and fix that. Okay, so it translates an array of key value pairs.

So let's go back to our spec and fix that. Okay, so it translates an array of key value pairs. Array of key value pairs. So yeah, this just needs to be double quotes. All right, run it again. It translates two arguments, so right here. This is still working. We're just using a different type of quote. We run it. It transforms php strings, and let's fix that one.

We run it. It transforms PHP strings, and let's fix that one. Okay, cool. So now we're at green, so it seems that we no longer have to do this escaping. All right, well that's kind of a good win there. But now, yeah, take a look at it at this point. We have array transformer that encodes the value. But Boolean does, and double, and integer does, and null does, and string does. So clearly at this point, all of these type-specific classes are unnecessary because json_encode will do the trick.

Consolidating to default transformer7:40

So clearly at this point, all of these type-specific classes are unnecessary because json_encode will do the trick. So now, what if we added a new one for like a default, or you could do, if you don't like that basic encodable transformer, let's just keep it simple. It's the default one we have. And then we'll fix that. Okay, so now what you could do is go to each of these. Let's go to string and remove this entirely, and if we run it, it fails. But now we could do basic inheritance, and now it passes. Okay, so just stepping stones at this point.

This is just a simple explanation with no code.

So let's fix that. What could we do here? Well, we could say, in a couple steps, we could say, well, if this class exists, then we do have a transformer registered for it. So we could do something like that. And then we could say, otherwise, we will new up a default transformer, like that. Okay, let's run it. And we get green. Okay, so that's one step. Next here, because we're using json_encode, we no longer have to check if it's null.

Okay, so that's one step. Next here, because we're using json_encode, we no longer have to check if it's null. So I should be able to delete that and then just return the result. Run it again, and we still get green. Let's keep going, because it seems like now we no longer have to dynamically construct a class name like StringTransformer. It looks like we're only ever going to have the default transformer and an ObjectTransformer. So could we do something like, well, let's just say, if the value is an object, then we know we need to use ObjectTransformer. Otherwise, we can stick with default.

we know we need to use object transformer. Otherwise, we can stick with default. And now you might want to stick with this second option if there will be new types added and new transformers. In this case, it's a small project. It looks like there's only ever going to be those. So I'm going to keep it very simple and just do a type check directly here. All right, so now there's our class. We could then, at this point, just reconstruct our class path like we had before, and I think that should do it.

We could then, at this point, just reconstruct our class path like we had before, and I think that should do it. So let's run it. Yeah, we're still at green. But if we were to var_dump the class, yeah, now you'll see we're not constructing all these different types. We just either have a default or an object transformer based on the type of the variable, which means I no longer need to see if a class exists, because it always will. So I think we can get rid of this, run it, and we get green. So yeah, we're simplifying this code quite a bit at this point.

Refactoring with collections11:13

So I think we can get rid of this, run it, and we get green. So yeah, we're simplifying this code quite a bit at this point. Next up, let's just scroll the page and see what else we can do here. So I'm seeing this here. Learn to pinpoint whenever you see this shape where you assign a variable and then you append to the variable and then you return it. Often you can maybe take more of a functional approach to build this up, but it doesn't really matter. This is 100% readable, but some teams do have rules that they don't want you to use loops at all.

This is 100% readable, but some teams do have rules that they don't want you to use loops at all. They prefer using a pipeline or a functional approach. So again, this is mostly just for educational purposes. But yeah, if we wanted to do a more functional approach, what if we collected the variables? And that will give us some of the helpers that Laravel provides, and we do pull in Illuminate support as a dependency here. So we could say collect those and then map over them and then map them down where we will have the value and the name. Okay, so now what we could do is return this initialized variable and we'll send those.

will have the value and the name. Okay, so now what we could do is return this initialized variable and we'll send those through again. So we're just doing this, but now what's getting returned here is an array of the initialized variables. So ultimately when we're done, we're going to convert it back to a string here like that. Okay, so let's try this. Get rid of that, run our tests. We're still at green, but we can go further. We could now say return JS, plus those variables and get rid of this.

We're still at green, but we can go further. We could now say return JS, plus those variables and get rid of this. All right, that passes. Next though, what if we inlined this variable entirely? All right, well now we run it. Still green, but this is great. I think you could make an argument it's a little more difficult to consume. And I think that's a very important argument. But also it's so incredibly simple that it just doesn't matter in this case. We've removed the temporary variable and it's very clear, okay, we're constructing the namespace

But also it's so incredibly simple that it just doesn't matter in this case. We've removed the temporary variable and it's very clear, okay, we're constructing the namespace and then we're initializing the variables and turning it into a string. It's still incredibly easy to consume. So what else? Let's go up here. And yeah, here's another thing to spot where you assign a variable, you do something with it, and then you return that variable. Whenever you see that shape, once again, that's a really good use case for TAP. Because if you're not familiar with TAP, TAP comes with Laravel.

Adding test for view binding13:30

Whenever you see that shape, once again, that's a really good use case for TAP. Because if you're not familiar with TAP, TAP comes with Laravel. If you're not using Laravel, you can create your own TAP helper function very, very easily. It's one or two lines of code. But anyways, the way TAP works is it's going to return whatever you pass as the first argument, and then the value of that first argument will be sent through to this callback function. So what you could do here is if you want to operate upon sum, but still return the sum, well TAP will allow you to do that. So let's see what we might do here. So it sounds like we need to construct the JavaScript and then do something with that.

So let's see what we might do here. So it sounds like we need to construct the JavaScript and then do something with that. So bind it to the view and then return it. Okay, so let's try this. Let's say TAP, just put it on its own line here, create that, and then we're going to do something with it. So I will bind this to the view. Finally, we always wanted to return it, so I could just say return. And let's give this a test. And here, hmm, it's trying to use higher order TAP proxy.

Just because you make a change and the tests don't fail doesn't mean everything still works. It just means the tests you have written are still working. But in this case, I'm going to delete that entirely or comment it out, and it still passes, which means we don't have a test that ensures that when we call the put method, we bind the necessary JavaScript to the view. So here's what I'm going to do. I'm going to copy this, but bring it back to what we had before so that we can write a test for it. And then what we'll do is paste the comment it out for later. Okay, so here's the original implementation.

And then what we'll do is paste the comment it out for later. Okay, so here's the original implementation. Everything's at green, but we've already verified that we're not testing this at all. Okay, let's go back here. And then at the top, I'm just going to say function, it binds the JavaScript to the view. Now the way php spec works, I can type in what I need here, and then immediately declare expectations against it. So for example, I could say, well, when I call the put method on the transformer, and with php spec, when you say this put, it delegates to the thing under test, so it will delegate to transform.

with PHP spec, when you say put, it delegates to the thing under test, so it will delegate to transform. Anyways, we could say, when I call put, what I expect to happen is at some point, I expect the viewBinders bind method to be called, so I could say should be called when I call this put method. Okay, so let's run this. And now it's going to fail, but we can see here, we called bind, but it expected us to call bind with this value. So I'm just going to cheat and paste it in there, because I already know it's correct. Yeah, now we're back to green.

So I'm just going to cheat and paste it in there, because I already know it's correct. Yeah, now we're back to green. Okay, so at this point, I can no longer delete this, because we have a test that ensures, no, we expected you to bind to the view, but you never did. Okay, so that's important. So at this point, we can delete all of this. Everything's going to blow up, but if we use our new implementation, we're back to green, so we know this is working. So yeah, at this point, in terms of how you set this up, it's entirely up to you. So if you like this to be a variable, then it's up to you.

I didn't have it so low, it would be pretty short, and it's very clear to me. So anyways, let's switch over and take a look at our second refactor. So now we realize that if we just change up the way we encode the variable, it turns out that most of these classes are superfluous. They don't need to exist, because they all do the exact same thing. So instead, we were able to break that down to our standard object transformer, and then a default transformer here. Now because we did that, that allowed us to delete all of the transformer types entirely, and then if we scroll down, we can dynamically figure out which transformer to trigger and run it.

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