Refactoring for real projects0:00
All right, so I know in the last episode, I mentioned that for this video, we'd move on to something else. But looking over this, it has occurred to me, we probably need to wrap this up with a bow. And here's what I mean. Yes, I get it. We're mostly focused in this series on the interaction between Laravel and OpenAI. But yeah, it's still Laracast, right? And we're still working with Laravel. And the problem is, what we ended up with in the last episode works, but it doesn't actually represent what you would probably do in your projects. So for example, in real life, what I would do here is I would wrap all of that logic up within a validation rule. So you know what, as a supplementary episode, we're not going to learn anything new about OpenAI here. But yeah, as a supplementary episode, why don't we just clean this up and
So you know what, as a supplementary episode, we're not going to learn anything new about OpenAI here. But yeah, as a supplementary episode, why don't we just clean this up and make it reflect the code you would actually write or potentially write in real life. Okay. So think about it. We begin this controller action by validating that the body field is both required and a string. But then we have all of this code down here. But if you think about it, it's also performing validation. It's validating that the body field does not include spam. But yeah, you notice how it's separate from the validation itself, which is kind of confusing. So why don't we incrementally clean this up? Here's what I'll start with. Let's get rid of this. And we'll say, if the response is spam, one way that we could manually trigger a validation error is by throwing an instance of ValidationException. And that
Throwing validation errors1:32
Let's get rid of this. And we'll say, if the response is spam, one way that we could manually trigger a validation error is by throwing an instance of ValidationException. And that will be Illuminate\Validation\ValidationException. We'll send through the messages. And this will be an array where we reference the attribute name. So this would be the input or the text area with the name of body, and then the validation message that we want to potentially display to the user. So I could say spam was detected. Now, actually, on this note, keep in mind, it's possible if you detect spam, you don't necessarily want to alert the end user. If this is a bot you're dealing with, it might instead be smarter to treat the request as if it finished successfully. So you're not providing any clue that, hey, we detected spam here. But yeah, keep that in mind. But we're going to go a more traditional
the request as if it finished successfully. So you're not providing any clue that, hey, we detected spam here. But yeah, keep that in mind. But we're going to go a more traditional route just for this exercise. Okay. Anyways, throwing this exception will effectively redirect back to the previous page and populate that errors object. Okay. So now if we get down here, yeah, redirect wherever would be appropriate, right? If it's a forum, redirect back to the page to view their message or back to the main blog post. It doesn't matter. I'm just going to say return redirect() wherever is needed. Post was valid. And you get the idea. All right. Very cool. So now why don't we display the error messages? And I'll do it very quickly. Here's our form. I'm just going to put it right down here. And I believe I have a little snippet. And, yeah, this is logic you've probably written a million times,
very quickly. Here's our form. I'm just going to put it right down here. And I believe I have a little snippet. And, yeah, this is logic you've probably written a million times, right? You check if we have any validation errors. And if so, we loop over the errors and display each one in red text within a list item. All right. Let's give it a go. So I will paste in some spam here. That will then make a request to OpenAI. Now, keep in mind, that can sometimes take a bit of time. So you should factor that into how you handle this. It might instead need to be thrown onto a queue where you handle it behind the scenes. But, anyways, it detects spam. We wrap it within a ValidationException and we redirect back. All right. Very cool. So now I'm going to go into my routes file and take a second pass here. So if it turns out all of this is effectively a validation rule,
Inlining a validation rule3:50
we redirect back. All right. Very cool. So now I'm going to go into my routes file and take a second pass here. So if it turns out all of this is effectively a validation rule, let's start by inlining it here along with the other validation rules. And we can use a closure here. The first argument will be the attribute name. So this would be body. The next one is the value of the text area. And then the final parameter will be a closure that you can call to trigger the validation exception. Okay. So here's what I'm going to do. I will grab all of this junk. Yeah. All of that. That represents our rule. And I will paste it in and reformat. Okay. But now within here, I no longer need to manually throw an exception. I can instead just call that fail function. And I can say spam was detected. All right. Next, right up here, we no longer have attributes. We have the
throw an exception. I can instead just call that fail function. And I can say spam was detected. All right. Next, right up here, we no longer have attributes. We have the value itself. So I will add that here. All right. So, yeah, mostly now, and in fact, I can get rid of this. But yeah, mostly now, we've taken all of that logic and we've placed it inside an inline validation rule. But other than that, it's identical. And actually, let me clean this up just a little bit. That looks good. All right. Let's give it another try. We'll start with something valid. This was great, Bob. Publish. Yep. Redirect wherever you need. Next, I will paste in the spam. Like so. And let's see what we get. The exact same thing. So, that was a successful refactor. Okay. So, now, if you want to keep this inline, that's fine. But why don't we take it one step further and extract a validation rule.
Extracting a rule class5:32
same thing. So, that was a successful refactor. Okay. So, now, if you want to keep this inline, that's fine. But why don't we take it one step further and extract a validation rule class. From the command line, I can say php artisan make:rule. And what is the rule here? It's not spam. So, why don't we call it SpamFree. All right. Cool. So, within a split, I will open up that class. And, yeah, notice, I'm going to take everything within this closure here. I will cut it and then paste it within our new class. Okay. We'll come back to that in just a second. But now, I can replace this with a new instance of SpamFree. Just like that. And then let's simplify that like so. All right. Looking pretty good. So, now, if I open up our dedicated validation rule, yeah, I could import this. Like so. And definitely better, I think. Now, we do have this one issue where potentially throughout the entire codebase, we keep
Using the Assistant wrapper6:24
dedicated validation rule, yeah, I could import this. Like so. And definitely better, I think. Now, we do have this one issue where potentially throughout the entire codebase, we keep referencing that OpenAi facade. So, yeah, this is why and, again, I want to note, maybe it doesn't matter for some projects. Maybe it does. It just depends on context, as always. But, yeah, often you will find that you want to wrap these things up within your own class or your own interface. And luckily, we've already created that Assistant class in the last several episodes. So, I think we should probably reference that class instead. Let's do that now. At the top here, we would instantiate Assistant. All right. Next, we need to declare what the system message is. And luckily, we have an API for that. systemMessage. And I'll paste that in. So, now, I can get rid of all of this. The model. So, we could
Next, we need to declare what the system message is. And luckily, we have an API for that. System message. And I'll paste that in. So, now, I can get rid of all of this. The model. So, we could well, if we went into assistant, of course, we could add a method to make the model dynamic. And in real life, that probably should happen. However, for now, we could just update this or we could just update this or upgrade this to the latest version and that will still work just like before. Okay. And that's the route I'm going to take here. So, now, I can get rid of that. And then, finally, all we have left is this user message. So, why don't we grab that and save it to message or why don't we call it prompt. Paste that in. And clean it up just a little bit. Okay. So, now, all of this can go. And I can replace it with response = assistant.send(prompt). And actually, you know what? I'm going to change this. I call it message. So, why don't
Okay. So, now, all of this can go. And I can replace it with response equals assistant send prompt. And actually, you know what? I'm going to change this. I call it message. So, why don't we be consistent? Let's rename prompt to message. Even though prompt is entirely valid. All right. So, are we on the same page? We instantiate our assistant. We declare a systemMessage. We then send our request to check for spam. That will then be JSON. We decode it and then we fail if spam was detected. All right. So, now, the only remaining step is just to clean it up a little bit. We did offer a fluent API of sorts. So, why don't we make that a little more clear? I'll start by taking this message variable and inlining it. All right. That looks good. Next, systemMessage returns an assistant. So, what I could do here is just remove that, like so. Reformat. That would then give us our response. And then, finally, if we want, we can instantiate assistant inline.
returns an assistant. So, what I could do here is just remove that, like so. Reformat. That would then give us our response. And then, finally, if we want, we can instantiate assistant inline. Like this. And then, finally, just format it however you think looks good. Okay. Finally, why don't we decode it inline? And if we have an object and if it's spam, we should fail. And, yeah. I think I'm going to stop right here. I think that looks good. Create an assistant, set the system message, make our request, check for spam, and fail if so. Yeah. I think that looks good. So, now, if I go back to my routes file, notice this is nice and clean. I don't even need to think about the logic or steps involved with making a request to OpenAI. All I have to say is this field should be spam free. All right. Let's test our work. One more time. This was great. Entirely valid. So, we should see a good message. We do. Next, paste in some spam, like so. Cross
this field should be spam free. All right. Let's test our work. One more time. This was great. Entirely valid. So, we should see a good message. We do. Next, paste in some spam, like so. Cross our fingers and we still get our failed validation message. And, yeah. That's it. That's all I meant by dotting our I's and crossing our T's and wrapping everything up with a bow. And, again, I get it. We didn't cover anything new here. You didn't learn more about open AI. And yet, still, it's really important. All of this relates to the architecture or the structure or the organization for how you would prepare this code and where you would put each line of code. And that's really important, I think. So, finally, before I let you go, just keep in mind that it's very difficult to estimate how long these API requests will take. On a good day, it could be less than a second. But it could also be ten seconds. So, you have to decide, do you want to make the user wait that
Handling slow API calls10:47
to estimate how long these API requests will take. On a good day, it could be less than a second. But it could also be ten seconds. So, you have to decide, do you want to make the user wait that long? And often the answer will be no. So, if that's the case, you need to structure things even a little bit differently from what we did here. Maybe instead, you throw it onto a queue and you handle that logic behind the scenes. And to decide what to do, again, just think about it. What do I want to do if I detect spam in my forum? Do I mark that it needs to be approved or manually inspected by an admin? Do I just silently delete it? What do I do? As always, that's going to be up to you. Okay. But this time, I really do promise, in the next episode, we move on to something brand new. I'll see you then.
we move on to something brand new. I'll see you then.
