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

Introducing Spam Refactor0:00

So far in this series, you've learned a lot of little techniques that you might start with. But most of the time, you want to refactor towards some of these patterns, right? So in this lesson, we're going to use a real world example. And specifically, if I switch back to Chrome, we're going to implement this step here, consider splitting tasks into steps. Okay, so if you want, watch this video to get a refresher. And when you're ready, let's get started. Okay, so here I have a class called Spam. And you basically call it like this, you have an instance, and then you call check, and you pass through the request, then that'll filter through a series of checks like like this. Well, let's make sure you didn't type in a forbidden keyword. Like if you're trying to promote some casino, or mega video or something like that, we won't allow it.

a robot. So you have to click on that, we have to submit a curl request to Google and just make sure that everything checks out. Okay, so that's what this class does. But as you'll see, it kind of grows and grows. So this is actually kind of a real world class, I would say just kind of jumbled up and muddied up and simplified, but it's still the basic building blocks for a class I have in the Laravel source. So what's the problem here? Well, well, when we start, let's imagine you just have like two things, like maybe this, you're checking for some forbidden keywords, and you want to make sure they didn't hold down a key to spam a thread. Okay, well, in those cases, you just create your class, you add your search method, bada bing, bada boom, you're done, right? No problem there. But usually what happens is, as time goes on, you add another check, and then you add another check, and then you add another check, you know, just kind of keeps going.

Reviewing Tests as Safety2:49

dependency. This is the only place it's used. But because we're putting everything in one class, they all get jumbled together. And like I said, for a couple methods, no problem. But as you add more and more and more checks, you might consider splitting this up. And that's what we're going to do today. Now I do have some tests for this. And they provide some good documentation, you may not use forbidden keywords or Korean, you can't hold a key down and you must click the capture checkbox. And mostly you don't need to worry about how this works. We're just mocking some dependencies. And here's the main part right here. We want to make sure that an exception is thrown. When I knew what the class called the search method, and then pass through the request. So for example, right here, yeah, if the body contains HD streaming online, maybe that's forbidden. Yeah, we want to make sure that an exception was thrown. So if I run phpunit, there we go,

Extracting Validator Classes3:32

right here, yeah, if the body contains HD streaming online, maybe that's forbidden. Yeah, we want to make sure that an exception was thrown. So if I run phpunit, there we go, we get green, so we can start refactoring. It's always good to start refactoring with tests that are entirely green. That way, if you make a mistake, you'll know immediately. Okay, let's get started back to my Spam class. Well, here's the cool thing right here. Each of these will become methods. So take a look, we're going to go into app, I have this validators directory. And yeah, right now, all of our validators are within the Spam class. But we're going to change that. Why don't we say NoForbiddenKeywords. All right, and build this up. namespace app\validators, and our class is NoForbiddenKeywords. Now later, we can create an interface if you want. If you don't want to do it, that's cool, too. Alright, so we're going to have

namespace app validators, and our class is no forbidden keywords. Now later, we can create an interface if you want. If you don't want to do it, that's cool, too. Alright, so we're going to have a method here called how about validate? Or you know what, we could keep the same API. So we could stick with this search method and accept the request. Okay, so let's import that. Now this is the same request that you would get from submitting a form when you're using Laravel. Okay, so let's take care of it. Here's our logic. So cut that out, and paste it in. Okay, so now here, the body I can replace with the appropriate property. So check the basically the body text area from the form. And we're basically looking through keywords and checking to see, well, does that text area contain one of these keywords? And if so, yeah, not cool. We're going to throw a validation exception. And we'll take care of that up the chain. Okay, so what about our keywords? Well,

And let's see what we need here. So key held down, I will steal all of this, paste it in. And actually, I'm going to take my Comment as well, just for myself six months from now. All right, so we are checking the title. And if we match that, we throw an Exception. And if we match that, we throw an Exception. Sweet. So now that one is done. We can grab the doc block, keep it nice and clean. And then once again, update this request, request, and we don't return anything. All right, so now I can get rid of that dude as well. And finally, we just have one more. And we can see, like I said earlier, this method actually has some dependencies. So we were injecting a curl class and a config class exclusively for this method. So like I said, that's another sign that maybe it should have had its own class. We'll say CAPTCHA was clicked. Good enough. Naming stuff is hard. All right, so paste it in.

this method. So like I said, that's another sign that maybe it should have had its own class. We'll say CAPTCHA was clicked. Good enough. Naming stuff is hard. All right, so paste it in. CAPTCHA was clicked. And then once again, let's figure out what the logic was for that. Okay, so we're going to grab all of this junk here, paste it in, grab my doc block, paste it in. And let's see. So we have a dependency of curl and the config class. So you know what, if we come back, maybe I can just grab all of that junk and move it over here. And then finally, my imports as well can move. Okay, so now when we perform the search, yeah, we make a curl request to CAPTCHA's API. We send through our secret key. We send through the request input from the form. And we also send through the user's IP address. And that's going to determine, did they click on it? Is everything good? Is there anything fishy going on?

Building Validator Pipeline10:43

the request input from the form. And we also send through the user's IP address. And that's going to determine, did they click on it? Is everything good? Is there anything fishy going on? So if the response was not successful, we throw a ValidationException. And that's how we handle that. All right, so now if we come back, this is actually quite a bit cleaner. So if we get rid of this, we can see that there's really nothing being done here. And that's okay. This class will now almost be like a manager class. It's just going to be responsible for filtering through all of your validators and triggering them, exactly like we learned in that other lesson. So why don't we create a array, and we could call this validators or checkers, or how about just checks? Okay, so this is going to be an array of classes. So for example, forbiddenKeywords, keyHeldDown, Korean. And then finally, the CAPTCHA was clicked. All right, so these are the classes we have right

so this is going to be an array of classes. So for example, forbidden keywords, keyHeldDown, Korean. And then finally, the CAPTCHA was clicked. All right, so these are the classes we have right now that will search for spam. Now, in our main search method, very simple. For each, this checks as class, because that's what it is. Then we will resolve the class out of the service container. And basically, the only difference between that and new Class is, well, that would be fine in most cases. But for a class like this, we do need to pass in some dependencies. So we're going to let Laravel help us with that, excuse me, and resolve this through the service container. Okay, so new it up, and then we'll call our search method and pass through the request, exactly like we discussed in that other lesson. Okay, so we're getting close. We like the way this is looking. Let's add our docBlocks, spamCheckers. But anyways, if we run phpunit, I think it's going to fail. And yeah,

Fixing Tests with Container12:24

that other lesson. Okay, so we're getting close. We like the way this is looking. Let's add our doc blocks, spam checkers. But anyways, if we run phpunit, I think it's going to fail. And yeah, it will. So we're expecting an exception, but we're getting an error here. Okay, so let's see what's going on. If we go to our test class, and for example, if I were to just get rid of all the other ones to make it easy for you, it's still going to fail. So here's the deal. We're mocking the curl request. So we're going to use prophecy. You may not know this, but prophecy, which is like a mocking tool, you get that out of the box with phpunit. So we're going to basically mock the curl class and make sure that a post method is called. And when it is, we're just going to hard code the response. And that's because in this case, it's a unit test. I don't actually want to call Google Server. I don't care in this case. So I'm going to mock it out. Next, the same thing

code the response. And that's because in this case, it's a unit test. I don't actually want to call GoogleServer. I don't care in this case. So I'm going to mock it out. Next, the same thing with Laravel's config repository. Now I think as it turns out, we could just new up the repository because there's no dependencies. But just to be safe, we're going to mock it with prophecy. And then here we expect an exception. And then we knew of the class. So here's where we need to change a couple of things. Notice that before we were passing in that curl and config class directly here. But now it's actually on our CaptureWasClicked class. So I no longer, excuse me, I no longer have to do this. So I can get rid of that entirely. But now that does beg the question, if in our Spam class, we're resolving this out of the container, how can I, for the purposes of testing, say that this dependency and this dependency should be mocked versions?

the question, if in our Spam class, we're resolving this out of the container, how can I, for the purposes of testing, say that this dependency and this dependency should be mocked versions? How do I inject that if I no longer am doing it up manually? Here's how. All we have to say is when you need an instance of app\Utilities\Curl, well, I want you to use my mocked version. And that's it. So now, for example, right here, when Laravel is ready to inject this, it's going to go, oh, I actually already have an instance of that in my container. So I'm just going to pass that through. And as it turns out, it's actually our mocked version. Okay, so we'll do the exact same thing for Illuminate\Config\Repository. And this is basically like when you do config('foo.bar'), you know, something from the configuration. This is actually what's being used behind the scenes, or the same if you just use the global function.

when you do config get foo.bar, you know, something from the configuration. This is actually what's being used behind the scenes, or the same if you just use the global function. Same thing. Anyways, it's good for now, I think. And we can always try that out later to see if we remove it, will everything still work like we expect? Anyways, that should just about do it. However, I am leveraging some Laravel magic here. And if we scroll up before I was just using PHPUnit\Framework\TestCase. So let's extend Laravel's TestCase. And that way, I can get access to some of Laravel's helpers and the framework itself. Okay, let's run it again. And we get green, it worked. So let's bring back the other three methods here. And I'll just paste them in right here. And if we run it again, we still get green. So that was a successful refactor. If it wasn't, yeah, we'd get a bunch of red, but we didn't. So we know that all of that refactoring

Adding a New Checker16:54

Anyways, let's imagine I want to add a new checker. So we'll say, if the if the title of your post is Jeffrey is stupid, you know what, I just can't allow it. So I'm going to simulate a request where the title of the thread is that and then the body is just a default body doesn't matter. Now let's change this, you may not call Jeffrey. I should keep this here just to see if any of you tried out. Alright, so it failed. I expected an exception, but we didn't get one. Alright, well, now I just create a new checker. Jeffrey is stupid Class, this is so lame. Jeffrey is stupid. steal some of this. And now, yeah, how do we check it? Well, we'll just say, if string contains the request title, yeah, if it contains Jeffrey is stupid, then in that case, we will throw an exception.

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