Проверка полей формы массива в ошибке laravel 4

Как мы можем проверить поля формы, которые являются массивами? Взгляните на следующий код

Модель UserPhone:

 public static $rules= array(
    'phonenumber'=>'required|numeric',
    'isPrimary'=>'in:0,1'
)
...........

UserController:

$validation = UserPhone::validate(Input::only('phonenumber')));


    if($validation->passes())
      {
         $allInputs = Input::only('phonenumber','tid');
         $loopSize = sizeOf($allInputs);

         for($i=0;$i<$loopSize;$i++)
         {

         $phone = UserPhone::find($allInputs['tid'][$i]);
         $phone->phonenumber = $allInputs['phonenumber'][$i];
         $phone->save();

        }

     return Redirect::to('myprofile')->with('message','Update OK');

  }
  else
  {
     return Redirect::to('editPhone')->withErrors($validation);

  } 

}

$validation происходит от BaseModel, который расширяет яркость.

На мой взгляд:

 <?php $counter=1; ?>
          @foreach($phones as $thephone)

           <section class="col col-12">
              <label class="label">Phone Number {{$counter++}}</label>
              <label class="input">
              <i class="icon-append icon-phone"></i>
                 {{Form::text('phonenumber[]',$thephone->phonenumber)}}
                 {{Form::hidden('tid[]',$thephone->id)}}
              </label>
            </section>
          @endforeach

Все работает нормально, и я получаю все номера телефонов, которые я хочу в Форме обновления, но я не могу обновить модель, потому что проверка не выполняется с сообщением "Номер телефона должен быть числом".

Я знаю, что нет простого решения для проверки полей формы массива, и я попытался расширить класс проверки, но без успеха.

Как я могу проверить такие поля?

Ответы

Ответ 1

Здесь решение, которое я использую:

Использование

Просто измените свои обычные правила, префикс each. Например:

'names' => 'required|array|each:exists,users,name'

Обратите внимание, что правило each предполагает, что ваше поле является массивом, поэтому не забудьте использовать правило array, как показано ниже.

Сообщения об ошибках

Сообщения об ошибках будут автоматически вычисляться сингулярной формой (используя помощник Laravel str_singular()) вашего поля. В предыдущем примере атрибут name.

Вложенные массивы

Этот метод работает в ящике с вложенными массивами любой глубины в точечной нотации. Например, это работает:

'members.names' => 'required|array|each:exists,users,name'

И снова атрибут, используемый для сообщений об ошибках здесь, будет name.

Пользовательские правила

Этот метод поддерживает любые ваши пользовательские правила из коробки.

Реализация

1. Расширить класс проверки

class ExtendedValidator extends Illuminate\Validation\Validator {

    public function validateEach($attribute, $value, $parameters)
    {
        // Transform the each rule
        // For example, `each:exists,users,name` becomes `exists:users,name`
        $ruleName = array_shift($parameters);
        $rule = $ruleName.(count($parameters) > 0 ? ':'.implode(',', $parameters) : '');

        foreach ($value as $arrayKey => $arrayValue)
        {
            $this->validate($attribute.'.'.$arrayKey, $rule);
        }

        // Always return true, since the errors occur for individual elements.
        return true;
    }

    protected function getAttribute($attribute)
    {
        // Get the second to last segment in singular form for arrays.
        // For example, `group.names.0` becomes `name`.
        if (str_contains($attribute, '.'))
        {
            $segments = explode('.', $attribute);

            $attribute = str_singular($segments[count($segments) - 2]);
        }

        return parent::getAttribute($attribute);
    }
}

2. Зарегистрируйте расширение для проверки подлинности

В любом месте вашего обычного загрузочного места добавьте следующий код:

Validator::resolver(function($translator, $data, $rules, $messages)
{
    return new ExtendedValidator($translator, $data, $rules, $messages);
});

И это! Наслаждайтесь!

Бонус: правила размера с массивами

Как отмечалось в комментарии, похоже, нет простого способа проверить размеры массива. Однако документации по Laravel не хватает для правил размера: он не упоминает, что он может считать элементы массива. Это означает, что вам разрешено использовать правила size, min, max и between для подсчета элементов массива.

Ответ 2

Он лучше всего подходит для расширения класса Validator и повторного использования существующих функций Validator:

Validator::resolver(function($translator, $data, $rules, $messages)
{

    return new Validation($translator, $data, $rules, $messages);

});

class Validation extends Illuminate\Validation\Validator {

    /**
     * Magically adds validation methods. Normally the Laravel Validation methods
     * only support single values to be validated like 'numeric', 'alpha', etc.
     * Here we copy those methods to work also for arrays, so we can validate
     * if a value is OR an array contains only 'numeric', 'alpha', etc. values.
     *
     * $rules = array(
     *     'row_id' => 'required|integerOrArray', // "row_id" must be an integer OR an array containing only integer values
     *     'type'   => 'inOrArray:foo,bar' // "type" must be 'foo' or 'bar' OR an array containing nothing but those values
     * );
     *
     * @param string $method Name of the validation to perform e.g. 'numeric', 'alpha', etc.
     * @param array $parameters Contains the value to be validated, as well as additional validation information e.g. min:?, max:?, etc.
     */
    public function __call($method, $parameters)
    {

        // Convert method name to its non-array counterpart (e.g. validateNumericArray converts to validateNumeric)
        if (substr($method, -7) === 'OrArray')
            $method = substr($method, 0, -7);

        // Call original method when we are dealing with a single value only, instead of an array
        if (! is_array($parameters[1]))
            return call_user_func_array(array($this, $method), $parameters);

        $success = true;
        foreach ($parameters[1] as $value) {
            $parameters[1] = $value;
            $success &= call_user_func_array(array($this, $method), $parameters);
        }

        return $success;

    }

    /**
     * All ...OrArray validation functions can use their non-array error message counterparts
     *
     * @param mixed $attribute The value under validation
     * @param string $rule Validation rule
     */
    protected function getMessage($attribute, $rule)
    {

        if (substr($rule, -7) === 'OrArray')
            $rule = substr($rule, 0, -7);

        return parent::getMessage($attribute, $rule);

    }
}

Ответ 3

каждый()

Это не в документах, но в ветке 4.2 может быть простое решение вокруг строки 220.

Как и функция sometimes($attribute, $rules, callable $callback), теперь есть функция each($attribute, $rules).

Чтобы использовать его, код будет чем-то более простым, чем вызов sometimes():

$v->each('array_attribute',array('rule','anotherRule')); //$v is your validator

Предостережения

  • sometimes() и each() не кажутся легко связанными друг с другом, поэтому, если вы хотите специально настроить условные правила для значений массива, вам лучше справиться с магическими решениями в других ответах на данный момент.
  • each() выходит только на уровень ниже, который не отличается от других решений. Самое приятное в волшебных решениях заключается в том, что они будут идти на 0 или 1 уровень, если необходимо, вызывая базовые правила в зависимости от ситуации, поэтому я полагаю, что если вы хотите пройти от 1 до 2 уровней, вы можете просто объединить два подхода, вызвав each() и передать ему магическое правило из других ответов.
  • each() принимает только один атрибут, а не массив атрибутов как sometimes(), но добавление этой функции в each() не будет значительным изменением функции each() - просто проведите через результаты $attribute и array_merge() $data и array_get(). Кто-то может сделать запрос на захват хозяином, если он считает это желательным, и он еще не выполнен, и мы можем увидеть, превращает ли он его в будущую сборку.

Ответ 4

Здесь обновляется код Ronald, потому что мои пользовательские правила не будут работать с расширением массива. Протестировано с Laravel 4.1, правилами по умолчанию, расширенными правилами,...

public function __call($method, $parameters) {
    $isArrayRule = FALSE;
    if(substr($method, -5) === 'Array') {
        $method = substr($method, 0, -5);
        $isArrayRule = TRUE;
    }

    //
    $rule = snake_case(substr($method, 8));

    // Default or custom rule
    if(!$isArrayRule) {
        // And we have a default value (not an array)
        if(!is_array($parameters[1])) {
            // Try getting the custom validation rule
            if(isset($this->extensions[$rule])) {
                return $this->callExtension($rule, $parameters);
            }

            // None found
            throw new \BadMethodCallException("Method [$method] does not exist.");
        } // Array given for default rule; cannot be!
        else return FALSE;
    }

    // Array rules
    $success = TRUE;
    foreach($parameters[1] as $value) {
        $parameters[1] = $value;

        // Default rule exists, use it
        if(is_callable("parent::$method")) {
            $success &= call_user_func_array(array($this, $method), $parameters);
        } else {
            // Try a custom rule
            if(isset($this->extensions[$rule])) {
                $success &= $this->callExtension($rule, $parameters);
            }

            // No custom rule found
            throw new \BadMethodCallException("Method [$method] does not exist.");
        }
    }

    // Did any of them (array rules) fail?
    return $success;
}