Laravel für verschachtelte Array-Validierung

Lesezeit: 8 Minuten

Ich baue eine REST-basierte API, bei der eine der APIs die folgende Anfrage hat

{
   "categories_id" :"1",
   "product_name" : "Pen",
   "product_description" : "this is pen",
   "tags" : "pen,write",
   "image_count" : "4",
   "skus": 
      {
          "is_shippable":"n",
          "actual_price":"100.55", 
          "selling_price":"200.45",
          "quantity_type":"bucket",
          "quantity_total":"10",
          "bucket_value":"instock",
          "sort_order":"1"
      }
}

Dies sind meine Validierungsregeln

protected $rules = [
        ValidatorInterface::RULE_CREATE => [
        'users_id' => 'required',
        'user_profiles_id' => 'required',
        'categories_id' => 'required',
        'product_name' => 'required|max:100',
        'product_description' => 'required|max:1000',
        'tags' => 'required',
        'image_count'=>'required|integer',
        'creation_mode'=>'required|integer',
        'skus.is_shippable'=>'in:y,n',
        'skus.actual_price'=>'regex:/^\s*(?=.*[1-9])\d*(?:\.\d{1,2})?\s*$/',
        'skus.selling_price' => 'regex:/^\s*(?=.*[1-9])\d*(?:\.\d{1,2})?\s*$/',
        'skus.quantity_type' => 'sometimes|required|in:finite,infinite,bucket',
        'skus.quantity_total' => 'integer|required_if:skus.quantity_type,finite', 
        'skus.bucket_value'=>'in:instock,soldout,limited|required_if:skus.quantity_type,bucket',
        'skus.sort_order'=> 'required|integer'
        ],
        ValidatorInterface::RULE_UPDATE => [
        ]
    ];

Die obige Anfrage wird ordnungsgemäß validiert. Aber die skus können mehrere Entitäten enthalten, wie unten angefordert

{
       "categories_id" :"1",
       "product_name" : "Pen",
       "product_description" : "this is pen",
       "tags" : "pen,write",
       "image_count" : "4",
       "skus": 
          [{
              "is_shippable":"n",
              "actual_price":"100.55", 
              "selling_price":"200.45",
              "quantity_type":"bucket",
              "quantity_total":"10",
              "bucket_value":"instock",
              "sort_order":"1"
          },
          {
              "is_shippable":"n",
              "actual_price":"100.55", 
              "selling_price":"200.45",
              "quantity_type":"bucket",
              "quantity_total":"10",
              "bucket_value":"instock",
              "sort_order":"1"
          }]
    }

Wie überprüfe ich, ob mehrere verschachtelte Entitäten vorhanden sind?

Benutzer-Avatar
Fabio Antunes

Welche Version von Laravel verwendest du? Wenn Sie verwenden Laravel 5.2 oder wenn es Ihnen nichts ausmacht, darauf zu aktualisieren, gibt es eine sofort einsatzbereite Lösung.

Array-Validierung

Die Validierung von Eingabefeldern in Array-Formularen ist in Laravel 5.2 viel einfacher. Um beispielsweise zu überprüfen, ob jede E-Mail in einem bestimmten Array-Eingabefeld eindeutig ist, können Sie Folgendes tun:

$validator = Validator::make($request->all(), [
    'person.*.email' => 'email|unique:users'
]);

Ebenso können Sie das *-Zeichen verwenden, wenn Sie Ihre Validierungsmeldungen in Ihren Sprachdateien angeben, wodurch es ein Kinderspiel wird, eine einzelne Validierungsmeldung für Array-basierte Felder zu verwenden:

'custom' => [
    'person.*.email' => [
        'unique' => 'Each person must have a unique e-mail address',
    ]
],

Ein weiteres Beispiel aus Laravel-Neuigkeiten:

Stellen Sie sich vor, Sie haben ein Formular mit einer Reihe von Eingabefeldern wie diesem:

<p>
<input type="text" name="person[1][id]">
<input type="text" name="person[1][name]">
</p>
<p>
<input type="text" name="person[2][id]">
<input type="text" name="person[2][name]">
</p>

In Laravel 5.1 mussten zum Hinzufügen von Validierungsregeln die Regeln einzeln durchlaufen und hinzugefügt werden. Anstatt all das tun zu müssen, wurde es hierin „laravelisiert“:

$v = Validator::make($request->all(), [
  'person.*.id' => 'exists:users.id',
  'person.*.name' => 'required:string',
]);

Wenn Sie also Laravel 5.2 nicht verwenden möchten, müssen Sie dies manuell tun. Wenn Sie auf Laravel 5.2 aktualisieren, können Sie die neue Array-Validierung verwenden, und sie wird etwa so aussehen:

protected $rules = [
        ValidatorInterface::RULE_CREATE => [
        'users_id' => 'required',
        'user_profiles_id' => 'required',
        'categories_id' => 'required',
        'product_name' => 'required|max:100',
        'product_description' => 'required|max:1000',
        'tags' => 'required',
        'image_count'=>'required|integer',
        'creation_mode'=>'required|integer',
        'skus.*.is_shippable'=>'in:y,n',
        'skus.*.actual_price'=>'regex:/^\s*(?=.*[1-9])\d*(?:\.\d{1,2})?\s*$/',
        'skus.*.selling_price' => 'regex:/^\s*(?=.*[1-9])\d*(?:\.\d{1,2})?\s*$/',
        'skus.*.quantity_type' => 'sometimes|required|in:finite,infinite,bucket',
        'skus.*.quantity_total' => 'integer|required_if:skus.quantity_type,finite', 
        'skus.*.bucket_value'=>'in:instock,soldout,limited|required_if:skus.quantity_type,bucket',
        'skus.*.sort_order'=> 'required|integer'
        ],
        ValidatorInterface::RULE_UPDATE => [
        ]
    ];

Bearbeiten

Ihmo ist der beste Weg, diese zusätzliche Validierungslogik hinzuzufügen, die Erweiterung der Prüfer Klasse, die Ihre erstellt CustomValidator Klasse, es mag ein bisschen übertrieben sein, aber wenn Laravel 5.2 veröffentlicht wird, können Sie Ihren CustomValidator entfernen und den 5.2-Validator von Laravel weiter verwenden, ohne Änderungen an Ihrem Code vorzunehmen.

Wie? Nun, zuerst erstellen wir einen Ordner unter unserem app/ Ich beschloss, diesen Ordner zu benennen Prüfer Sie können es benennen, wie Sie wollen, denken Sie nur daran, den Namensraum der folgenden Klassen zu aktualisieren. Als nächstes werden wir 3 .php-Dateien in diesem Ordner erstellen CustomValidator.php, CustomValidatorServiceProvider.php und Fabrik.php.

CustomValidator.php

<?php

namespace App\Validator;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Validation\Validator;
use Symfony\Component\Translation\TranslatorInterface;

class CustomValidator extends Validator
{
    /**
     * Create a new Validator instance.
     *
     * @param  \Symfony\Component\Translation\TranslatorInterface  $translator
     * @param  array  $data
     * @param  array  $rules
     * @param  array  $messages
     * @param  array  $customAttributes
     * @return void
     */
    public function __construct(TranslatorInterface $translator, array $data, array $rules, array $messages = [], array $customAttributes = [])
    {
        $this->translator = $translator;
        $this->customMessages = $messages;
        $this->data = $this->parseData($data);
        $this->customAttributes = $customAttributes;

        // Explode the rules first so that the implicit ->each calls are made...
        $rules = $this->explodeRules($rules);

        $this->rules = array_merge((array) $this->rules, $rules);
    }

    /**
     * Explode the rules into an array of rules.
     *
     * @param  string|array  $rules
     * @return array
     */
    protected function explodeRules($rules)
    {
        foreach ($rules as $key => $rule) {
            if (Str::contains($key, '*')) {
                $this->each($key, $rule);
                unset($rules[$key]);
            } else {
                $rules[$key] = (is_string($rule)) ? explode('|', $rule) : $rule;
            }
        }
        return $rules;
    }


    /**
     * Define a set of rules that apply to each element in an array attribute.
     *
     * @param  string  $attribute
     * @param  string|array  $rules
     * @return void
     *
     * @throws \InvalidArgumentException
     */
    public function each($attribute, $rules)
    {
        $data = Arr::dot($this->data);
        foreach ($data as $key => $value) {
            if (Str::startsWith($key, $attribute) || Str::is($attribute, $key)) {
                foreach ((array) $rules as $ruleKey => $ruleValue) {
                    if (! is_string($ruleKey) || Str::endsWith($key, $ruleKey)) {
                        $this->mergeRules($key, $ruleValue);
                    }
                }
            }
        }
    }



    /**
     * Get the inline message for a rule if it exists.
     *
     * @param  string  $attribute
     * @param  string  $lowerRule
     * @param  array   $source
     * @return string|null
     */
    protected function getInlineMessage($attribute, $lowerRule, $source = null)
    {
        $source = $source ?: $this->customMessages;
        $keys = ["{$attribute}.{$lowerRule}", $lowerRule];
        // First we will check for a custom message for an attribute specific rule
        // message for the fields, then we will check for a general custom line
        // that is not attribute specific. If we find either we'll return it.
        foreach ($keys as $key) {
            foreach (array_keys($source) as $sourceKey) {
                if (Str::is($sourceKey, $key)) {
                    return $source[$sourceKey];
                }
            }
        }
    }

    /**
     * Get the custom error message from translator.
     *
     * @param  string  $customKey
     * @return string
     */
    protected function getCustomMessageFromTranslator($customKey)
    {
        $shortKey = str_replace('validation.custom.', '', $customKey);
        $customMessages = Arr::dot(
            (array) $this->translator->trans('validation.custom')
        );
        foreach ($customMessages as $key => $message) {
            if ($key === $shortKey || (Str::contains($key, ['*']) && Str::is($key, $shortKey))) {
                return $message;
            }
        }
        return $customKey;
    }
}

Dieser benutzerdefinierte Validator enthält alle Änderungen, die an Laravel 5.2 vorgenommen wurden, Sie können sie einchecken hier

Da wir nun eine neue CustomValidator-Klasse haben, müssen wir einen Weg finden, sie zu verwenden, dafür müssen wir die erweitern ValidatorServiceProvider und die Validator-Fabrik.

CustomValidatorServiceProvider.php

<?php

namespace App\Validator;


class CustomValidatorServiceProvider extends \Illuminate\Validation\ValidationServiceProvider
{
    /**
     * Register the validation factory.
     *
     * @return void
     */
    protected function registerValidationFactory()
    {
        $this->app->singleton('validator', function ($app) {
            $validator = new Factory($app['translator'], $app);

            // The validation presence verifier is responsible for determining the existence
            // of values in a given data collection, typically a relational database or
            // other persistent data stores. And it is used to check for uniqueness.
            if (isset($app['validation.presence'])) {
                $validator->setPresenceVerifier($app['validation.presence']);
            }

            return $validator;
        });
    }
}

Fabrik.php

<?php

namespace App\Validator;

use App\Validator\CustomValidator as Validator;

class Factory extends \Illuminate\Validation\Factory
{
    /**
     * Resolve a new Validator instance.
     *
     * @param  array  $data
     * @param  array  $rules
     * @param  array  $messages
     * @param  array  $customAttributes
     * @return App\Test\CustomValidator
     */
    protected function resolve(array $data, array $rules, array $messages, array $customAttributes)
    {
        if (is_null($this->resolver)) {
            return new Validator($this->translator, $data, $rules, $messages, $customAttributes);
        }

        return call_user_func($this->resolver, $this->translator, $data, $rules, $messages, $customAttributes);
    }
}

Jetzt haben wir unsere Validierung erweitert, um die verschachtelte Syntax zu unterstützen sku.*.id

Wir müssen nur den Validator in unseren CustomValidator tauschen, und der letzte Schritt ist das Ändern der Datei config/app.php und suchen Sie im ServiceProviders-Array nach der ValidatorServiceProviderkommentieren Sie einfach diese Zeile und fügen Sie unseren erweiterten Dienstanbieter wie folgt hinzu:

....
// Illuminate\Validation\ValidationServiceProvider::class,
App\Validator\CustomValidatorServiceProvider::class,
....

Der Grund, warum wir es auskommentieren, ist, dass Sie jedes Mal, wenn Sie Ihr Laravel 5.1 auf 5.2 aktualisieren, es nur auskommentieren möchten, unseren CustomValidatorServiceProvider aus der Liste entfernen und dann unseren App/Validator-Ordner löschen, weil wir ihn nicht mehr brauchen.

  • Groß! Vielen Dank. Ich wusste nicht, dass 5.2 diese Funktion hat. Ich verwende 5.1 Wird es kaputt gehen, wenn ich es aktualisiere?

    – Ajeesh

    21. Dezember 2015 um 10:45 Uhr

  • @Ajeesh, es gibt Dinge, die Sie beachten sollten, Laravel 5.2 befindet sich in der Beta-Phase, aber Sie können den Migrationsleitfaden hier überprüfen hier Was Sie tun können, ist Ihre Validierungslogik zu erstellen, und wenn die stabile Version von Laravel 5.2 veröffentlicht wird, werden Sie auf diese neue Art der Validierung verschachtelter Arrays aktualisiert.

    – Fabio Antunes

    21. Dezember 2015 um 10:51 Uhr

  • Ich werde das tun. Anstatt auf eine Beta-Version zu aktualisieren, aktualisiere ich besser, sobald die stabile Version veröffentlicht wird. Wenn ich dafür eine benutzerdefinierte Logik schreiben muss, können Sie mir ein Beispiel zeigen und Ihre Antwort aktualisieren?

    – Ajeesh

    21. Dezember 2015 um 10:55 Uhr

  • Danke für die ausführliche Antwort!

    – Ajeesh

    22. Dezember 2015 um 5:47 Uhr

  • @Ajeesh in CustomValidator fehlte eine Funktion, ich habe meine Antwort bearbeitet, schau dir die Funktion an getInlineMessage und zu Ihrem CustomValidator hinzugefügt

    – Fabio Antunes

    22. Dezember 2015 um 10:39 Uhr


1229030cookie-checkLaravel für verschachtelte Array-Validierung

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy