Thales Nathan
Thales Nathan

Reputation: 51

can't use boolean validation laravel 5

I'm using Laravel 5.2, and as documentation says:

boolean

The field under validation must be able to be cast as a boolean. Accepted input are true, false, 1, 0, "1", and "0".

So I've created a checkbox (styled like a switch from materialize), to return true when on, and false when off. Here goes the blade:

{!! Form::hidden('eh_capa', 0) !!}
Want to select as a graph cover?
<label>
    Off
    <input name="cover" type="checkbox" checked>
    <span class="lever"></span>
    on
</label>

Of course this code goes inside a form tag. I do the validation inside a Request class as said on this part of laravel documentation, here is my rules method:

public function rules()
{
    $this['cover'] = $this['cover'] === 'on' ? 1 : 0;
    $this['obra_id'] = $this->route('obra');
    $this['arquivo'] = $this->hasFile('arquivo') ? $this->file('arquivo') : NULL;
    dd($this);
    return [
        'cover' => 'required|boolean',
        'obra_id' => 'exists:obras',
        'path' => 'required',
        'arquivo' => 'required|file|max:2048|mimes:pdf',
    ];
}

The dd() function returns my request like this:

StoreGraficoPostRequest {#441 ▼
  #container: Application {#3 ▶}
  #redirector: Redirector {#448 ▶}
  #redirect: null
  #redirectRoute: null
  #redirectAction: null
  #errorBag: "default"
  #dontFlash: array:2 [▶]
  #json: null
  #convertedFiles: array:1 [▶]
  #userResolver: Closure {#355 ▶}
  #routeResolver: Closure {#354 ▶}
  +attributes: ParameterBag {#443 ▶}
  +request: ParameterBag {#440 ▼
    #parameters: array:5 [▼
      "_token" => "bZIpGW6UCcYHlCTZuIZMtmOrpCodWyfcbO1HgQid"
      "path" => "hello.pdf"
      "cover" => 1
      "obra_id" => "29"
      "arquivo" => UploadedFile {#438 ▶}
    ]
  }
  +query: ParameterBag {#442 ▶}
  +server: ServerBag {#446 ▶}
  +files: FileBag {#445 ▶}
  +cookies: ParameterBag {#444 ▶}
  +headers: HeaderBag {#447 ▶}
  #content: ""
  #languages: null
  #charsets: null
  #encodings: null
  #acceptableContentTypes: null
  #pathInfo: null
  #requestUri: null
  #baseUrl: null
  #basePath: null
  #method: "POST"
  #format: null
  #session: Store {#394 ▶}
  #locale: null
  #defaultLocale: "en"
}

But when I comment the dd function, the validation returns that cover must be true or false. The same happens if I change the value of cover field to true, "1" and "true" when on. I've searched all the web for anything that helps and got nothing... I'm beginning to think that this is a Laravel bug...

Upvotes: 3

Views: 8096

Answers (3)

Arno van Oordt
Arno van Oordt

Reputation: 3510

I ran into the same problem and decided to create a small static class that parses all values that are marked as boolean in a rule.

The advantage is that it will only parse booleans that the rules dictate to be boolean. Any other input values will go unchanged, making it still possible to post a string with value 'true' if you desire.

<?php

namespace App\Helpers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class ValidationHelper {

    /**
    * A recursive funciton to loop over any input and parse them to true booleans.
    */
    private static function _getObj(&$inputs, $idPath) {
        $idPart = array_shift($idPath);

        if (count($idPath) > 0) {
            if ($idPart == '*') {
                for ($i = 0; $i < count($inputs); $i++) {
                    ValidationHelper::_getObj($inputs[$i], $idPath);
                }
            }
            else {
                ValidationHelper::_getObj($inputs[$idPart], $idPath);
            }
        }
        else {
            $currentValue = $inputs[$idPart];
            if ($currentValue == 1 || strtolower($currentValue) == 'true') {
                $inputs[$idPart] = true;
            }
            else if ($currentValue == 0 || strtolower($currentValue) == 'false') {
                $inputs[$idPart] = false;
            }
            else {  // any other value will be left untouched
                $inputs[$idPart] = $currentValue;
            }
        }
    }

    /**
    * Will validate a request against the given rules.
    * Will also help fix any booleans that otherwise are parsed as 'true' strings.
    * @param Request $request   
    * @param Array $rules       
    * @return void
    */
    public static function validateRequest(Request $request, $rules) {
        // Find any rules demanding booleans:
        $booleanIDs = [];
        foreach ($rules as $id => $rule) {
            if (is_string($rule)) {
                $rule = explode('|', $rule);
            }
            if (in_array('boolean', $rule)) {
                $booleanIDs[] = $id;
            }
        }

        if (isset($booleanIDs)) {
            // Set all inputs to a bindable array:
            $inputs = [];
            foreach ($request->all() as $key => $value) {
                $inputs[$key] = $value;
            }

            // Recursively loop through the boolean-ids
            foreach ($booleanIDs as $id) {
                $idPath = explode('.', $id);
                ValidationHelper::_getObj($inputs, $idPath);
            }

            // Make sure the booleans are not just validated correctly but also stay cast when accessing them through the $request later on.
            $request->replace($inputs);
        }
        else {
            $inputs = $request->all();
        }

        $validator = Validator::make($inputs, $rules);
        if ($validator->fails()) {
            throw new \Exception('INVALID_ARGUMENTS', $validator->errors()->all());
        }
    }

}

Rules can be set as array or string (as normal) and it even works with nested values:

    ValidationHelper::validateRequest($request, [
            ['foo'] => ['nullable', 'boolean'],
            ['bar'] => ['required', 'boolean'],
            ['item.*.isFoo'] => ['nullable', 'boolean'],
            ['item.*.isBar'] => 'required|boolean'],
        ]);

Upvotes: 0

Thales Nathan
Thales Nathan

Reputation: 51

Well, I got a way to do it. The trick was just to add this code to my Request class:

protected function getValidatorInstance()
{
    $data = $this->all();
    $data['eh_capa'] = $data['eh_capa'] === 'on' ? 1 : 0;
    $data['obra_id'] = $this->route('obra');
    $this->getInputSource()->replace($data);

    /* modify data before send to validator */

    return parent::getValidatorInstance();
}

and then, my rules method ended only with the return.

Upvotes: 2

Sameer A.
Sameer A.

Reputation: 167

You are modifying your input in the wrong place. You should override the all() function in your request class, and modify your input there.

public function rules()
{
    return [
        'cover' => 'required|boolean',
        'obra_id' => 'exists:obras',
        'path' => 'required',
        'arquivo' => 'required|file|max:2048|mimes:pdf',
    ];
}

public function all()
{
    $input = parent::all();

    $input['cover'] = $input['cover'] === 'on' ? 1 : 0;
    $input['obra_id'] = ...
    $input['arquivo'] = ...

    return $input;
}

Upvotes: 0

Related Questions