در حال بارگذاری ...

Goal: Direct Prompt Methods0:00

What Jess is essentially saying here is that instead of having to call add each and every time we want to add a new step to our form, we can just call the prompt names directly, text, select, confirm, suggest, and it will automatically call the correct prompt under the hood. It's a really nice feature. So let's see if we can implement it. We'll do it dirty first, and then we'll improve over the course of this episode. I'm going to update one of my tests so I can see this work in real time. We'll get rid of everything apart from the prompt names themselves, and then I'll run the test, and of course it fails because there is no method called text on our formBuilder.

Prototype via __call0:34

We'll get rid of everything apart from the prompt names themselves, and then I'll run the test, and of course it fails because there is no method called text on our formBuilder at the moment. So with that in place, we'll jump into the formBuilder, and I'm going to use the __call method here at least to start, and then we'll improve this over time. The name I'll change to prompt. Of course, if we look under the helpers file, we actually have a namespace of Laravel Prompts, so we'll need to slightly update this. Prompt equals \Laravel\Prompts, and then we pass in the prompt that we are calling.

Prompt equals \Laravel, \prompts, and then we pass in the prompt that we are calling. Okay. If that doesn't exist, we should probably stop. So if !function_exists, passing in the prompt that we've created. Let's throw a new BadFunctionCallException, and we'll say the prompt, and then we'll pass in the promptName, does not exist. Okay. Now we can be absolutely sure that it does exist as a prompt and we're able to call it. So we should be able to return this $ad that obviously receives our closure.

Now we can be absolutely sure that it does exist as a prompt and we're able to call it. So we should be able to return this ad that obviously receives our closure. Our closure needs to have access to both the prompt and any arguments that you passed, and then we'll return the result of calling that prompt, spreading out the arguments that you've given. Okay, let's rerun, and now the test passes. Tell you what, let's jump into our form playground, and I'm going to update this playground to make use of that new syntax we've just introduced. Give me one minute. And I'm back.

Support Name Argument2:10

Give me one minute. And I'm back. Let me just walk you through a couple of things I noted whilst performing the transformation. First of all, no IDE auto completion, but of course, Jess says that IDE support is important. So we need to make sure that we bear that in mind in just a moment. Everything else is as you'd expect. There is the name argument that we need to keep in mind. We want support for that name argument. So we'll have to update our __call method to support that. And also, it is still possible, of course, to use add directly for more complex scenarios.

So we'll have to update our magic call method to support that. And also, it is still possible, of course, to use add directly for more complex scenarios or to receive the array of responses to make use of that inside the step. So with that in mind, I would imagine that if I try to run this from the terminal, php form.php, it will work at first, but as soon as we get to that third prompt where we make use of the name parameter, it fails. Let's fix that. We'll jump back into formBuilder, and I guess I could just unset the argument. So unset arguments name. But I also want to make sure that I pass the name as the second parameter to add.

So unset arguments name. But I also want to make sure that I pass the name as the second parameter to add. So here we'll say name is equal to arguments name, or if it doesn't exist, if it's not set, I'll just pass null instead. And now I imagine if we rerun the script, php artisan tinker, path, undo, Laravel, see it all works exactly as expected. We can confirm that this works without a doubt by updating the test for it can key a response by a given string. Perhaps I'll leave this one as it is, but for the second one, I'll make use of our new syntax, calling select directly.

Add IDE Autocomplete3:43

Perhaps I'll leave this one as it is, but for the second one, I'll make use of our new syntax, calling select directly. And of course, that will mean just passing the name argument to select like so. And then if I run this test, you can see it passes. So that is indeed supported. But as you can see here, and as you can see in our playground, IDE support is currently missing. There are a couple of ways we could add it. First of all, we could go to our form builder, and at the top, much like Laravel does with facades,

First of all, we could go to our form builder, and at the top, much like Laravel does with facades, we could use the at method syntax to add, for example, text or confirm, and then you'd pass in parameters and it should work. But at that point, are we really saving ourselves anything? I actually think it would likely be easier to just add the methods directly to the form builder. That's how I'm going to tackle it anyway, and we'll see what Jess says. Let's come down to the bottom of our form builder, and I'm going to extract the majority of this method here.

Let's come down to the bottom of our form builder, and I'm going to extract the majority of this method here into its own little protected method on the form builder. So protected function, we'll call it callPrompt. You pass in the name of the prompt and any arguments, and it returns an instance of itself. It does the little function_exists check on the prompt that you've given. Everything else should work, I think. So basically, the magic call now would say, return $this->callPrompt passing in the prompt that we define and any arguments.

So basically, the magic call now would say, return this call prompt passing in the prompt that we define and any arguments. Let's run our form test to make sure everything still works. Yes, it does. Very nice. So how about we go to our playground, and we'll start at the very beginning. A very good place to start. Here's the intro. I'm going to find intro in helpers. Intro, I'll copy it directly. We'll head into our form builder,

Intro, I'll copy it directly. We'll head into our form builder, and just above callPrompt, we'll drop it in. Let's add the public modifier. We'll make sure it returns self, and of course, it accepts a string message, but because we're inside form builder, it also accepts a name, which by default can be null. Of course, we don't want to do this. We want to actually return this callPrompt. We'll make sure to pass the correct prompt in.

We want to actually return this call prompt. We'll make sure to pass the correct prompt in. Laravel prompts, and of course, we're looking for intro here, and we want to pass in the keyed arguments from this method. Now, in php, we can do that using get_defined_vars. You have to be careful where you call get_defined_vars because if you've created any additional variables along the way, they will also be included inside the return of this function, but as long as we call it here at the top of the method, all we'll receive is, well, whatever you've passed to the method itself.

but as long as we call it here at the top of the method, all we'll receive is, well, whatever you've passed to the method itself. So it's perfect for this use case. Hopefully now, if we go back to our little playground, we come to the top. Yeah, note that the yellow squiggly line has disappeared, and in fact, if I inspect the method, I even get display and introduction along with the different parameters available to me. Let's do another one together. We'll take a look at suggest. So I'll go to helpers, find suggest, copy it in its entirety.

We'll take a look at suggest. So I'll go to helpers, find suggest, copy it in its entirety. We'll head back to the form builder, and we'll paste suggest in there. I'll add the public modifier, and then we'll say that it actually returns self. We can copy the majority of this call prompt here, and we'll drop that in place inside the body of suggest prompt. Of course, we need to add the name as well. So we'll say string name is equal to null by default as the final parameter, and we call the prompt.

So we'll say string name is equal to null by default as the final parameter, and we call the prompt. We're actually interested in calling suggest, not intro, and we pass in getDefinedVars. Again, if we go back to our playground, yeah, the squiggly line is gone, and we see that we can prompt the use of a text input with auto-completion. Let's just run our playground to make sure it actually works. So php playground form.php. Yep, looks like it's working absolutely fine. Here's path, and we can revert hopefully.

Yep, looks like it's working absolutely fine. Here's path, and we can revert hopefully. Yep, Laravel, revert twice. Awesome. This is working exactly as we'd expect. Now, I don't want you to have to watch me copy and paste all of these prompts from the helpers file into the form builder, so I'm going to use the magic of editing to fast forward and see you in the future. Whew, there we are. That didn't take too long.

Whew, there we are. That didn't take too long. So you can see now on the form builder API, I have all of these prompt methods available to me that will give auto-complete support to our IDE. In other words, if we go to our playground, and let's say after our intro, I want to introduce another step, I can see all of the different prompts that are available to me. Imagine I want to add text, and the text is, how old are you? Maybe we could add a little name here as well.

Imagine I want to add text, and the text is, how old are you? Maybe we could add a little name here as well. Let's say name is equal to age. Note that we have auto-complete support for the name. It filled it out as we typed. And in fact, I can obviously use my IDE to see that this is prompting the user for text input and take a look at all of the different options that are available to me. If we then run this, php playgroundform.php, how old are you is available.

If we then run this, phpplaygroundform.php, how old are you is available. Let's say 20. I'm definitely not, so let's go back and change that. And there we go. It's working perfectly. We no longer need the magic __call method on the form builder. Let's remove that. As a final step for this change, it would make sense to go through our existing tests.

As a final step for this change, it would make sense to go through our existing tests and just update them wherever it makes sense. So, for example, here, we can use the new syntax. That's very nice. Passes all available responses to each step. Well, again, it would make sense to use the new syntax where we can, but obviously we need to do manually wherever we accept responses like so. This one is already using the new syntax. We updated it earlier.

Docblocks and PHPStan Fixes9:37

This one is already using the new syntax. We updated it earlier. Does not allow reverting normal prompts. Again, we can use the new syntax here to simplify. Very nice. And when we run these tests, everything still passes. We need to make sure we add a doc block with comments above run to fit in with the rest of the code base. And I'm pretty sure that phpstan has, yeah, here we go, a callable string type. So, that's a little more specific than just string.

And I'm pretty sure that phpstan has, yeah, here we go, a callable string type. So, that's a little more specific than just string. It has to be available as a callable as progress, for example, is. The arguments are mixed because we don't necessarily know what they will contain, and we can remove the at return this because it's nonspecific. Let's add a little comment as to what runPrompt does. Execute the given prompt passing the given arguments. Very good. Let's run vendor/bin/phpstan to make sure types work. All right, so we've got an error, formBuilder 158.

Let's run vendor/bin/phpstan to make sure types work. All right, so we've got an error, formBuilder 158. Let's take a quick look. Okay, so it's returning treturn, but in the case of the formBuilder, that doesn't actually make sense because it returns itself. So, we'll remove that return tag. In fact, I'm pretty sure we could remove the entire thing here because we're no longer doing anything with that treturn. So, instead, we can simply say that this returns mixed. Let's rerun phpstan.

So, instead, we can simply say that this returns mixed. Let's rerun phpstan. Okay, that fixes that. And then let's finally run our tests, vendor/bin/pest. Make sure everything's passing. Looks like we're good to go. So, again, work in progress. And then I'll go ahead and run git push so that our changes are now live on GitHub. Let's perform just a couple of little tidy ups to our pull request.

Squash Commits with Reset11:16

so that our changes are now live on GitHub. Let's perform just a couple of little tidy ups to our pull request. So, if you take a look at the pull request on GitHub because we've been using that workInProgress alias, we have so many commits that just say work in progress. Some maintainers won't care. They'll be absolutely fine with you doing that, but other maintainers will ask you to squash your commits. Let me show you exactly how you do that. We'll go back to the very first commit we made and I'll click it.

Let me show you exactly how you do that. We'll go back to the very first commit we made and I'll click it. And you'll see that it will open up an overview of what files were changed. And here on the right, we can copy the commit hash. So, go ahead, copy that, head to your terminal, and I'm going to type git reset. I want the --soft flag, and then you can paste in the hash that you just copied from GitHub and press enter. This is essentially going to undo all of those commits locally,

and press enter. This is essentially going to undo all of those commits locally, but because we use the --soft flag, all of the changes to the files that we made will still be in place. You can see this if you run git status. Note that we have these renamed and new files in place that were previously committed to the repository. So, let's add a more meaningful git commit message. We'll say git add ., just in case there are any additional files that need adding.

We'll say git add ., just in case there are any additional files that need adding. And then we'll say git commit -m to specify an inline message. And then inside quotes, we can say something like, "adds form support to prompts." Very good. Hit enter. You'll see that it's going to commit all of those changes. And then we'll need to push up, but we'll have to use the -f flag, which is force, because it's going to have to overwrite the commits that were previously in place.

but we'll have to use the -F flag, which is force, because it's going to have to overwrite the commits that were previously in place. But don't worry, so long as you've followed my advice here, you're not going to destroy anything. git push -f, hit enter. And now from GitHub, yeah, you can see that our commits have been reduced to just two. Our initial work in progress that we reverted to, and then our latest commit, which is down here at the bottom. There we go.

and then our latest commit, which is down here at the bottom. There we go. Adds form support to prompts. Our second cleanup is going to be trying to match the code style of the package we're contributing to. Now, if you're contributing to any Laravel package, more than likely that's going to be done via Pint. You can install that globally using Composer. So composer global require laravel/pint will install it, and then it will be globally available under Pint.

So composer global require laravel/pint will install it, and then it will be globally available under pint. So from, in this case, the prompts project, I run pint. It will fix any code style issues. In our case, we have none, which is wonderful. And then, of course, you could commit that back to the repository. Most of the time, they'll actually have an automated process to handle code styling for you. Certainly, the Laravel framework does, for example. But I really like to try and get this in place.

Certainly, the Laravel framework does, for example. But I really like to try and get this in place so that the author is not worrying about that as they review our pull request, and instead, they can concentrate on the code we've actually written. Now, I'm not going to make you watch me make these changes, but our PR description is out of date because of the changes to the code we've made. For example, playground/steps.php no longer exists. We no longer support preventing reverting or custom revert logic, so those headings make no sense.

We no longer support preventing reverting or custom revert logic, so those headings make no sense. And our code examples can be updated. We call the form function, not the steps function. We can call text and select directly, and we would call submit instead of run. So I'll go ahead and make those adjustments, but know that you should keep your PR descriptions up to date. Not only does that help inform people who come to GitHub what's actually going on,

Not only does that help inform people who come to GitHub what's actually going on, but you can use this as the basis for any documentation for the feature down the line, which is super helpful. With our PR description updated, let's update Jess. Hey, at Jess Archer, thanks for the feedback. All changes implemented and ready for review. And now, we just have to wait to see what Jess says on the other side.

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