Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] Validated subsets #38366

Merged
merged 14 commits into from
Aug 13, 2021
Merged

[8.x] Validated subsets #38366

merged 14 commits into from
Aug 13, 2021

Conversation

taylorotwell
Copy link
Member

@taylorotwell taylorotwell commented Aug 12, 2021

Convenient methods for getting subsets of validated data.

$validator->safe()->only(['name', 'email']);
$validator->safe()->except([...]);

$formRequest->safe()->only([...]);
$formRequest->safe()->except([...]);

@taylorotwell
Copy link
Member Author

Decided to change approach and add a new Illuminate\Contracts\Support\ValidatedData contract which I can use as a generic interface to indicate that a collection of data has been validated as "safe". This will open up some more interesting doors for me later on.

You can now call the safe method on a validator or form request to get an instance of Illuminate\Support\ValidatedInput which implements this interface.

@iksaku
Copy link
Contributor

iksaku commented Aug 12, 2021

Loving it! It effectively resolves something I had to code just a few hours ago ⭐️

@franzliedke
Copy link
Contributor

@taylorotwell Should the interface contain the only() and except() methods? 🤔

*/
public function safe()
{
return new ValidatedInput($this->validated());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have a question ... may i know why not bind the interface to the concrete ValidatedInput instance inside ValidationServiceProvider ... and here call app(ValidatedData::class, ['input' => $thsi->validated()])

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's nice! And also changing the return type to the interface. I mean, we don't need the interface if the implementation is hardcoded to a specific class.

Comment on lines +37 to +51
$results = [];

$input = $this->input;

$placeholder = new stdClass;

foreach (is_array($keys) ? $keys : func_get_args() as $key) {
$value = data_get($input, $key, $placeholder);

if ($value !== $placeholder) {
Arr::set($results, $key, $value);
}
}

return $results;
Copy link
Member

@timacdonald timacdonald Aug 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we aren't just hitting the Arr::only(), Arr::* method on all of these?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that Request's InteractsWithInput had it's own implementation that I presume has some slightly different behavior than a plain Arr::only. Therefore, I just kept the implementation matching. I haven't dug into what the subtle differences between the two would be.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one difference that I can think of is passing * would return different result for both @timacdonald @taylorotwell

Comment on lines +76 to +79
public function collect()
{
return new Collection($this->input);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ryangjchandler will appreciate this one

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is beautiful.

@timacdonald
Copy link
Member

This is rad.

Only thing I would add to this is that it could be nice and also consistent to allow property style access on the ValidatedInput.

$request->safe()->name;

$request->safe('name');

This is already possible with array access $request->safe()['name'], but might be more consistent with other things in Laravel that also support individual property access out of the box.

Of course that could come in a follow up or something we implement in our own applications. Just a thought!

Thanks again!

@taylorotwell
Copy link
Member Author

Added __get, __set, __isset, __unset support @timacdonald.

@taylorotwell
Copy link
Member Author

@franzliedke the reason I didn't add those to the interface was the main use case I could think of for the interface was Eloquent mass-assignment operations, where you would simply be iterating through the data and hydrating a model without checking the fillable or guarded arrays. In that use case and similar use cases only iteration is needed.

* @return void
*/
#[\ReturnTypeWillChange]
public function offsetSet($key, $value)
Copy link
Contributor

@tpetry tpetry Aug 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't an immutable object make sense? A stict guarantee that ValidatedData is the unmodified result of a validator would sometimes be usefull i guess. What i mean is, changing a validated input may make it not the valid anymore.

@GrahamCampbell GrahamCampbell changed the title Validated subsets [8.x] Validated subsets Aug 13, 2021
@arcanedev-maroc
Copy link
Contributor

arcanedev-maroc commented Aug 13, 2021

@taylorotwell Any plan adding this feature in Illuminate\Foundation\Http\FormRequest ?

UPDATE: NVM, i've just checked the changed files. Thanks taylor ❤️

*/
public function safe()
{
return new ValidatedInput($this->validated());
Copy link
Contributor

@arcanedev-maroc arcanedev-maroc Aug 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this instead:

return $this->validator->safe();

And you can remove the use statement after.

@taylorotwell taylorotwell merged commit 3591526 into 8.x Aug 13, 2021
@taylorotwell taylorotwell deleted the validated-subsets branch August 13, 2021 13:58
@geocfu
Copy link

geocfu commented Aug 14, 2021

@taylorotwell @timacdonald
Would you be able to elaborate with an example usage? I searched in the docs (both 8.x and Master) but I could not find an example or a mention of this new feature.

Thank you for your efforts!

use Illuminate\Contracts\Support\ValidatedData;
use stdClass;

class ValidatedInput implements ValidatedData
Copy link
Member

@JosephSilber JosephSilber Aug 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason this isn't an actual collection?

(The only difference is the only method working on nested data, which we could easily add to the collection's method, or just override it here)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.