DEV Community

Joël Harkes
Joël Harkes

Posted on

2 3

Make Laravel do type safe validation

Laravel has a nice validation framework, but a big catch is that it doesn't make sure your result has the right types.

here an example:

$data = ['isOk' => '1'];
$result = $this->validate($data, ['isOk' => 'boolean');

$isOkString = $result['isOk'];
$isOkBoolean = (boo;)  $result['isOk'];
if($isOkString === true){
    // wont fall here.
}
if($isOkBoolean === true){
    // but will go here.
}

wouldn't be wonderful to make sure you have no side affects.

Todo this we must create a nice interface to be able to make Rules allow to transform the result. You must subclass the Laravel Validator and extend the method: validateUsingCustomRule

interface RuleTransformsResult
{
    public function transformResult($attribute, $value);
}


class MyTypeSafeValidator extends \Illuminate\Validation\Validator {
    protected function validateUsingCustomRule($attribute, $value, $rule)
    {
        // below is just copy pasted laravel code of method
        if (!$rule->passes($attribute, $value)) {
            $this->failedRules[$attribute][get_class($rule)] = [];

            $messages = (array) $rule->message();

            foreach ($messages as $message) {
                $this->messages->add($attribute, $this->makeReplacements(
                    $message, $attribute, get_class($rule), []
                ));
            }

        // here is where we do our magic
        } elseif ($rule instanceof RuleTransformsResult) {
            $newValue = $rule->transformResult($attribute, $value, $result);
            if($newValue !== $value) {
                Arr::set($this->data, $attribute, $result);
            }
        }
    }
}

Now we can write our own Boolean validator:

class ValidBoolean implements Rule, RuleTransformsResult
{
    public static $validValuesMap = [
        '0' => false,
        '1' => true,
        'false' => false,
        'true' => true,
        'yes' => true,
        'no' => false,
    ];

    public function passes($attribute, $value)
    {
        if (is_bool($value)) {
            return true;
        }

        return array_key_exists((string) $value, self::$validValuesMap);
    }

    public function message()
    {
        return __('validation.boolean');
    }

    public function transformResult($attribute, $value)
    {
        return (bool) $value;
    }
}

Now we can always have a boolean as result:

$data = ['isOk' => '1'];
$result = $this->validate($data, ['isOk' => [new ValidBoolean()]);

if($result['isOk'] === true){
   // yeah we will make it here.
}

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up