laravel / prompts

Beautiful and user-friendly forms for your command-line PHP applications.
https://laravel.com/docs/prompts
MIT License
532 stars 94 forks source link

Add transformation support #156

Closed emenkens closed 3 months ago

emenkens commented 3 months ago

I thought it would be nice to be able to make changes to input before it gets validated.

Problem

Suppose you want to trim user input before it's validated. We start off with a basic prompt to ask for a username:

$username = text(
    label: 'Please select a username:',
    required: true,
);

If I enter string(8) " " (8 spaces), that's a totally valid username. This could of course be solved with validation, so let's add a validation rule to make sure that the username, when trimmed, is at least 4 characters:

$username = text(
    label: 'Please select a username:',
    required: true,
    validate: fn (string $value) => strlen(trim($value)) < 4 ? 'Too short!' : '',
);

And enter string(8) " ssss " (2 spaces, and the letter s 4 times, followed by 2 more spaces). The validation logic above trims the string and agrees that string(4) "ssss" is a valid username. However, that's not the input we entered...

The trim function was called inside the validation logic, which means the value of $username will still be string(8) " ssss ". This could be resolved with more advanced validation logic, like not allowing a string to start and end with a space etc., but this can become unmanageable pretty fast.

We could of course wrap all of the code above in a trim():

$username = trim(text(
    ...
));

but now I have repeated myself and have to remember to change the code every time the validation logic changes. This can be tedious when working with large form sets, especially if there are multiple developers working on the same codebase.

Solution

In this PR, I have added a transform option, which accepts a Closure and can be used like this:

$username = text(
    label: 'Please select a username:',
    transform: fn ($value) => trim($value),
    validate: fn (string $value) => strlen($value) < 4 ? 'Too short!' : '',
);

This will alter the value before validation takes place, which means I don't need to repeat myself, and I know for sure that the value that is assigned to $username is the same value that was being validated.

This could be super useful not only for trimming strings, but also to filter out other kinds of unwanted characters, like spaces in a phone number or hyphens in a UUID. It can also be used with the Str helpers:

use Illuminate\Support\Str;

$slug = text(
    label: 'Please enter a slug for your blog post:',
    transform: fn ($value) => Str::slug($value),
    validate: ...
);

All input types are supported and I've added tests for each type. Let me know what you think!

Thank you

github-actions[bot] commented 3 months ago

Thanks for submitting a PR!

Note that draft PR's are not reviewed. If you would like a review, please mark your pull request as ready for review in the GitHub user interface.

Pull requests that are abandoned in draft may be closed due to inactivity.

macocci7 commented 3 months ago

I think that src/FormBuilder.php also needs to be updated.

taylorotwell commented 3 months ago

@emenkens does FormBuilder need to be updated? Please mark as ready for review when the requested changes have been made.

emenkens commented 3 months ago

@taylorotwell it does indeed! Thank you @macocci7 for pointing that out. I've updated it now.