Check out the Sanitizer package by Dayle Rees.
Filtering request input
Hi, all! This is my first post on here. I'm hoping you'll be gentle. Otherwise, bring lube, 'cus I didn't.
What I want to achieve is simple. As an example, on my app, the user should be able to submit a form with a number in any (well, almost any) format he or she would like. "100 000" should be valid. "100 000 kr" should be valid (Norwegian currency.) What I want to do, is filter (or normalize) the request inputs in such a way that both values in the previous example should become 100000, which I could then validate using the standard validation stuff in a controller or a FormRequest.
Anyway, I made a trait which can be used on a FormRequest, which looks like this:
<?php
namespace App\Http\Requests\Traits;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Symfony\Component\HttpFoundation\ParameterBag;
trait FiltersRequestInput
{
/** @var bool */
private $hasFiltered = false;
/**
* Override `Illuminate\Http\Request::getInputSource()` to perform the
* filtering.
*
* @return \Symfony\Component\HttpFoundation\ParameterBag
*/
protected function getInputSource(): ParameterBag
{
if ($this->hasFiltered) {
return parent::getInputSource();
}
$input = parent::getInputSource();
foreach ($this->filters() as $key => $filter) {
if (!$input->has($key)) {
continue;
}
$fn = $this->getCallable($filter, $key);
$input->set($key, $fn($input->get($key)));
}
$this->hasFiltered = true;
return $input;
}
/**
* Get the filters for this request.
*
* @return array
*/
abstract protected function filters(): array;
/**
* Get the callable from a filter.
*
* @param callable|string $filter
* @param string $key
*
* @return callable
*
* @throws \InvalidArgumentException
*/
private function getCallable($filter, string $key): callable
{
if (is_callable($filter)) {
return $filter;
}
if (substr_count($filter, '@', 1) === 1) {
$fn = explode('@', $filter);
if (is_callable($fn)) {
return $fn;
}
}
if (method_exists($this, $filter)) {
return [$this, $filter];
}
throw new InvalidArgumentException(sprintf(
'Invalid filter [%s] defined for input key [%s] on request [%s]',
$filter, $key, static::class
));
}
}
Next, on a FormRequest:
<?php
class SomeRequest extends \Illuminate\Foundation\Http\FormRequest
{
use FiltersRequestInput;
// validation rules...
protected function filters(): array
{
return [
'number' => 'intval', // <- will "filter" through php's intval. could be any callable
];
}
}
Then, in a controller somewhere:
<?php
class Controller
{
public function store(SomeRequest $request)
{
// will be validated already
$request->input('number'); // will have run through filter
}
}
Is there a better way to achieve this? Is the trait implementation too sketchy? I would appreciate some thoughts.
Thanks.
Please or to participate in this conversation.