Laravel: Validierung beim Update eindeutig

74

Ich weiß, dass diese Frage schon oft gestellt wurde, aber niemand erklärt, wie man die ID erhält, wenn man im Modell validiert.

'email' => 'unique:users,email_address,10'

Meine Validierungsregel befindet sich im Modell. Wie übergebe ich die ID des Datensatzes an die Validierungsregel?

Hier sind meine Modelle / Benutzer

protected $rules_update = [
    'email_address' => 'required|email|unique:users,email_address,'.$id,
    'first_name' => "required",
    'last_name' => "required",
    'password' => "required|min:6|same:password_confirm",
    'password_confirm' => "required:min:6|same:password",
    'password_current' => "required:min:6"
];

Modelle / BaseModel

    protected $rules = array();

public $errors;

/*
    * @data: array, Data to be validated
    * @rules: string, rule name in model 
*/

public function validate($data, $rules = "rules") {

    $validation  = Validator::make($data, $this->$rules);

    if($validation->passes()) {
        return true;
    }

    $this->errors = $validation->messages();

    return false;
}
user742736
quelle
Ich würde auch gerne wissen, wie die $ id an das Modell übergeben wird. Ich verwende das way / database-Paket und habe das gleiche Problem.
Thomas
Ich habe meine Antwort unten hinzugefügt.
user742736
Ich auch, mit dem gleichen Problem
user3189734

Antworten:

43

Eine einfache Lösung.

In Ihrem Modell

protected $rules = [
    'email_address' => 'sometimes|required|email|unique:users',
    ..
];

Aktion: Update in Ihrem Controller

...
$rules = User::$rules;
$rules['email_address'] = $rules['email_address'] . ',id,' . $id;
$validationCertificate  = Validator::make($input, $rules); 
Ricardo Canelas
quelle
2
Zugriff auf geschützte
Objekte
@ SoubhagyaKumarBarik Welche Version ist dein Laravel?
Ricardo Canelas
Meine Laravel-Version ist 7.x
Soubhagya Kumar Barik
@SoubhagyaKumarBarik Meine Antwort war 2014 für Version 4.x. Ich denke, für Version 7 haben Sie eine andere Lösung. Überprüfen Sie die anderen Antworten unten oder erstellen Sie eine neue Frage zum Stapelüberlauf und fügen Sie den Link hier zu uns hinzu, um Ihnen zu helfen.
Ricardo Canelas
77

Nur eine Randnotiz, die meisten Antworten auf diese Frage sprechen darüber, email_addresswährend in Laravels eingebautem Authentifizierungssystem der Name des E-Mail-Feldes gerecht ist email. Hier ist ein Beispiel, wie Sie ein eindeutiges Feld überprüfen können, dh eine E-Mail zum Update:

In einer Formularanforderung gefällt Ihnen Folgendes:

public function rules()
{
  return [
      'email' => 'required|email|unique:users,email,'.$this->user->id,
  ];
}

Oder wenn Sie Ihre Daten in einem Controller direkt validieren:

public function update(Request $request, User $user)
{
  $request->validate([
      'email' => 'required|email|unique:users,email,'.$user->id,
  ]);
}

Aktualisieren : Wenn Sie den angemeldeten Benutzer aktualisieren und das UserModell nicht in Ihre Route einfügen, können beim Zugriff idauf undefinierte Eigenschaften auftreten $this->user. Verwenden Sie in diesem Fall:

public function rules()
    {
      return [
          'email' => 'required|email|unique:users,email,'.$this->user()->id,
      ];
    }

Ein eleganterer Weg seit Laravel 5.7 ist:

public function rules()
{
    return [
        'email' => ['required', 'email', \Illuminate\Validation\Rule::unique('users')->ignore($this->user()->id)]
    ];
}

PS: Ich habe einige andere Regeln hinzugefügt, z. B. Erforderlich und E-Mail, um dieses Beispiel für Neulinge deutlich zu machen.

Rehmat
quelle
1
Ich benutze FormRequest, und genau das musste ich tun.
Sonny
1
Wie soll ich in FormRequest auf $ this-> user () oder andere Modelle zugreifen / diese bereitstellen? wie $ this-> article () in ArticleFormRequest
Krzysztof Dziuba
@KrzysztofDziuba Möchten Sie auf angemeldete Benutzer zugreifen?
Rehmat
3
Mit \Illuminate\Validation\Rule::unique()es ist wirklich die eleganteste Art und Weise. Für mich war das der beste Vorschlag. Danke
Magno Alberto
Dies ist ideal für eine einzigartige Validierung. Jetzt möchte ich das Bild für die Aktualisierung validieren, aber nur, wenn der Benutzer ein neues Bild hochlädt.
HaxxanRaxa
25

Es gibt eine elegante Möglichkeit, dies zu tun. Wenn Sie Resource Controller verwenden, sieht Ihr Link zum Bearbeiten Ihres Datensatzes folgendermaßen aus:

/ users / {user} / edit ODER / users / 1 / edit

Und in Ihrer UserRequest sollte die Regel folgendermaßen lauten:

public function rules()
{
    return [
        'name' => [
            'required',
            'unique:users,name,' . $this->user
        ],
    ];
}

Oder wenn Ihr Link zum Bearbeiten Ihres Datensatzes folgendermaßen aussieht:

/ users / edit / 1

Sie können dies auch versuchen:

public function rules()
{
    return [
        'name' => [
            'required',
            'unique:users,name,' . $this->id
        ],
    ];
}
Ihn Hah
quelle
Diese Antwort ist die beste und eleganteste. Ich habe eine Frage. Der Link zu meinem Bearbeitungsdatensatz sieht so aus /users/{user}/editund ich möchte verhindern, dass ich unique:users,nameaktualisiert werden kann, während andere Felder aktualisiert werden können. Wie würde ich es erreichen?
Chamal101
6

Wenn ich verstehe, was Sie wollen:

'email' => 'erforderlich | email | unique: Benutzer, E-Mail-Adresse,'. $ id. ''

Bei der Modellaktualisierungsmethode sollte beispielsweise die $ id mit Parameter empfangen werden.

Entschuldigung für mein schlechtes Englisch.

Lucas Silva
quelle
Wird der Parameter $ id an das Modell übergeben? Ich habe bereits versucht 'email' => 'unique: users, email_address, {{$ id}}' ohne Glück '
user742736
{{$ id}} funktioniert nur in Ansichten mit Blade. Sie sollten also string scape verwenden: 'unique: users, email_address,'. $ id. '' oder "unique: users, email_address,". $ id. ""
Lucas Silva
Funktioniert nicht Fehler unten 1. Symfony \ Component \ Debug \ Exception \ FatalErrorException… / app / models / User.php42 0. Illuminate \ Exception \ Handler handleShutdown <#unknown> 0 Syntaxfehler Symfony \ Component \ Debug \ Exception \ FatalErrorException, unerwartet '. ', erwartet'] '
user742736
Sie können Ihre Modellmethode zeigen oder ein Bild von der Fehlerseite drucken?
Lucas Silva
6

Eine noch einfachere Lösung, die mit Version 5.2 getestet wurde

in Ihrem Modell

// validator rules
public static $rules = array(
    ...
    'email_address' => 'email|required|unique:users,id'
);
Altravista
quelle
5
public function rules()
{

    switch($this->method())
    {
        case 'GET':
        case 'DELETE':
        {
            return [];
        }
        case 'POST':
        {
            return [
                'name' => 'required|unique:permissions|max:255',
                'display_name' => 'required',
            ];
        }
        case 'PUT':
        case 'PATCH':
        {
            return [                    
                'name' => 'unique:permissions,name,'.$this->get('id').'|max:255',
                'display_name' => 'required',
            ];
        }
        default:break;
    }    
}
Janak Bhatta
quelle
1
Manchmal kann die ID Teil der URL sein und Sie finden sie in den Routenparametern: $ this-> route ({Feldname}) zB: $ this-> route ('id')
matthiku
Es ist immer besser zu erwähnen, dass Sie den Code von laracasts.com/discuss/channels/requests/…
Aftab Naveed
Sie müssen ein verstecktes Feld mit der ID übergeben, um $ this-> get ('id')
Anand Mainali
5

Test unter Code:

'email' => 'required|email|unique:users,email_address,'. $id .'ID'

Dabei ist ID die primäre ID der Tabelle

Sonam Tomar
quelle
4

Hier ist die Lösung:

Zum Update:

public function controllerName(Request $request, $id)

{

    $this->validate($request, [
        "form_field_name" => 'required|unique:db_table_name,db_table_column_name,'.$id
    ]);

    // the rest code
}

Das ist es. Viel Spaß beim Codieren :)

RashedRahat
quelle
3

Ich würde das lösen, indem ich so etwas mache

public function rules()
{
    return [
         'name' => 
            'required|min:2|max:255|unique:courses,name,'.\Request::get('id'),
    ];
}

Woher erhalten Sie die ID aus der Anfrage und geben sie an die Regel weiter?

Matovu Ronald
quelle
Die Laravel-Dokumente warnen vor dieser Technik, da sie eine Sicherheitslücke verursachen kann. Stellen Sie sich vor, ein Angreifer kann die Regeln ändern, indem er dem idParameter etwas angibt. laravel.com/docs/5.8/validation#rule-unique
Peter Krebs
3
$rules = [
    "email" => "email|unique:users, email, '.$id.', user_id"
];

In Illuminate \ Validation \ Rules \ Unique;

Durch die eindeutige Überprüfung wird die Zeichenfolgenüberprüfung für das Regelobjekt analysiert

Die eindeutige Validierung weist ein Muster auf: eindeutig:% s,% s,% s,% s,% s '

Entspricht: Tabellenname, Spalte, Ignorieren, ID-Spalte, Format wherees

/**
 * Convert the rule to a validation string.
 *
 * @return string
 */
public function __toString()
{
    return rtrim(sprintf('unique:%s,%s,%s,%s,%s',
        $this->table,
        $this->column,
        $this->ignore ?: 'NULL',
        $this->idColumn,
        $this->formatWheres()
    ), ',');
}
thenguyenit
quelle
Mir gefällt, wo du hingekommen bist. Aber ich bin mir nicht sicher, wie ich das auf die Regeln eines Modells anwenden soll. Wo soll ich {{$ id}} ersetzen?
dmmd
2

Am einfachsten gefunden, funktioniert gut, während ich Laravel 5.2 verwende

public function rules()

{

switch ($this->method()) {
    case 'PUT':
        $rules = [
            'name'                  => 'required|min:3',
            'gender'                => 'required',
            'email'                 => 'required|email|unique:users,id,:id',
            'password'              => 'required|min:5',
            'password_confirmation' => 'required|min:5|same:password',
        ];
        break;

    default:
        $rules = [
            'name'                  => 'required|min:3',
            'gender'                => 'required',
            'email'                 => 'required|email|unique:users',
            'password'              => 'required|min:5',
            'password_confirmation' => 'required|min:5|same:password',
        ];
        break;
}

return $rules;
}
Zarpio
quelle
In meinem Fall konnte ich die ID nicht mit ": id" abrufen, also habe ich so etwas gemacht:if (in_array($this->method(), ['PUT', 'PATCH'])) { $rules['order'] .= ",{$this->route('videos')->id}"; }
Igor de Lorenzi
2

Verwendung für Laravel 6.0

use Illuminate\Validation\Rule;

public function update(Request $request, $id)
    {
        // Form validation
        $request->validate([
            'category_name'   =>  [
                'required',
                'max:255',
                 Rule::unique('categories')->ignore($id),
            ]
        ]);
}
ufuk
quelle
2

Sie können auch den Modellklassenpfad verwenden, wenn Sie den Tabellennamen nicht fest codieren möchten.

function rules(){
    return [
        'email' => ['required','string',
         Rule::unique(User::class,'email')->ignore($this->id)]
    ];
}

Hier ist $ this-> id entweder 0 oder die zu aktualisierende Datensatz-ID.

Abdul Malik
quelle
2

Sie können dies versuchen.

protected $rules_update = [
    'email_address' => 'required|email|unique:users,email_address,'. $this->id,
    'first_name' => "required",
    'last_name' => "required",
    'password' => "required|min:6|same:password_confirm",
    'password_confirm' => "required:min:6|same:password",
    'password_current' => "required:min:6"
];
Raju Rj
quelle
1

Das habe ich letztendlich getan. Ich bin mir sicher, dass es einen effizienteren Weg gibt, aber das habe ich mir ausgedacht.

Model / User.php

protected $rules = [
    'email_address' => 'sometimes|required|email|unique:users,email_address, {{$id}}',
];

Model / BaseModel.php

public function validate($data, $id = null) {


      $rules = $this->$rules_string;

     //let's loop through and explode the validation rules
     foreach($rules as $keys => $value) {

        $validations = explode('|', $value);

        foreach($validations as $key=>$value) {

            // Seearch for {{$id}} and replace it with $id
            $validations[$key] = str_replace('{{$id}}', $id, $value);

        }
        //Let's create the pipe seperator 
        $implode = implode("|", $validations);
        $rules[$keys] = $implode;

     }
     ....

  }

Ich übergebe die $ user_id an die Validierung im Controller

Controller / UserController.php

public function update($id) { 

   .....

    $user = User::find($user_id);

    if($user->validate($formRequest, $user_id)) {
      //validation succcess 
    } 

    ....


}
user742736
quelle
Am Ende habe ich nur Watson / Validating verwendet
Thomas
Danke, ich werde es mir auch ansehen.
user742736
1

Während der Aktualisierung eines vorhandenen Datenschreibprüfers wie folgt:

'email' => ['required','email', Rule::unique('users')->ignore($user->id)]

Dadurch wird der eindeutige Wert der vorhandenen Benutzer-ID für die jeweilige Spalte übersprungen / ignoriert.

Saiful Islam
quelle
1

Test unter Code:

$validator = Validator::make(
            array(
              'E-mail'=>$request['email'],
             ),
            array(
              'E-mail' => 'required|email|unique:users,email,'.$request['id'],
             ));
GQ.
quelle
Versuchen Sie, Anforderungsparameter nicht direkt in Ihren Validierungsregeln zu verwenden. Ein Angreifer kann die an die Steuerung gelieferten Parameter ändern (sogar den idParameter 0|nullableoder schlechter sein lassen). laravel.com/docs/5.8/validation#rule-unique
Peter Krebs
1

Die beste Option ist hier, nur einmal zu versuchen, keinen Code mehr zu benötigen, wenn eine eindeutige Validierung beim Aktualisieren von Daten erfolgt

'email' => 'unique:users,email_address,' . $userId,

Hier emailist der Feldname und der users Tabellenname sowie der email_address Tabellenattributname, der eindeutig sein soll und $userid die Zeilen-ID aktualisiert

Kashif Shahzad
quelle
0

Meine Lösung:

$rules = $user->isDirty('email') ? \User::$rules : array_except(\User::$rules, 'email');

Dann zur Validierung:

$validator = \Validator::make(\Input::all(), $rules, \User::$messages);

Die Logik lautet: Wenn die E-Mail-Adresse im Formular unterschiedlich ist, müssen wir sie validieren. Wenn sich die E-Mail nicht geändert hat, müssen wir sie nicht validieren. Entfernen Sie diese Regel aus der Validierung.

Silberner Paladin
quelle
0

Für uniqueRegeln in der Steuerung - die sich offensichtlich für die storeMethode und die updateMethode unterscheiden - mache ich normalerweise eine Funktion innerhalb der Steuerung, für rulesdie ein Array von Regeln zurückgegeben wird.

protected function rules($request)
{
    $commonRules = [
        'first_name' => "required",
        'last_name' => "required",
        'password' => "required|min:6|same:password_confirm",
        'password_confirm' => "required:min:6|same:password",
        'password_current' => "required:min:6"
    ];

    $uniqueRules = $request->id

          //update
        ? ['email_address' => ['required', 'email', 'unique:users,email' . $request->get('id')]]

          //store
        : ['email_address' => ['required', 'email', 'unique:users,email']];


    return array_merge($commonRules, $uinqueRules);
}

Dann in den jeweiligen storeund updateMethoden

$validatedData = $request->validate($this->rules($request));

Dies erspart die Definition von zwei verschiedenen Regelsätzen für Speicher- und Aktualisierungsmethoden.

Wenn Sie es sich leisten können, bei der Lesbarkeit ein wenig Kompromisse einzugehen, kann dies auch der Fall sein

protected function rules($request)
{
    return [
        'first_name' => "required",
        'last_name' => "required",
        'password' => "required|min:6|same:password_confirm",
        'password_confirm' => "required:min:6|same:password",
        'password_current' => "required:min:6",
        'email_address' => ['required', 'email', 'unique:users,email' . $request->id ?: null]
    ];
}

Donkarnash
quelle
Verwenden Sie solche Anforderungsparameter nicht direkt. Ein Angreifer kann den idParameter so ändern , dass er beliebig ist. laravel.com/docs/5.8/validation#rule-unique
Peter Krebs
0

Ich habe den vorherigen Beitrag gelesen, aber keiner nähert sich dem eigentlichen Problem. Wir müssen die Regel eindeutig verwenden, um sie auf das Hinzufügen und Bearbeiten von Groß- und Kleinschreibung anzuwenden. Ich verwende diese Regel beim Bearbeiten und Hinzufügen von Groß- und Kleinschreibung und arbeite einwandfrei.

In meiner Lösung verwende ich die Regelfunktion aus der Anforderungsklasse.

  1. Ich habe eine ID über ein verstecktes Eingabeformularfeld im Bearbeitungsformular gesendet.
  2. Auf der Regelfunktion finden wir nach eindeutiger Spalte und erhalten den Datensatz.
  3. Bewerten Sie nun die Situation. Wenn vorhandener Datensatz und ID gleich sind, darf das eindeutige nicht aktiviert werden (das bedeutet, dass der Datensatz bearbeitet wird).

Auf dem Code:

public function rules()
    {
        //
        $user = User::where('email', $this->email)->first();
        //
        $this->id = isset($this->id) ? $this->id : null;
        $emailRule = (($user != null) && ($user->id == $this->id)) ? 'required|email:rfc,dns|max:255' : 'required|unique:users|email:rfc,dns|max:255';
        //        
        return [
            //
            'email'            =>  $emailRule,                
            //
        ];
        //


    }
Luis Morales
quelle
0

Da Sie den Datensatz, den Sie aktualisieren, bei der Durchführung eines Updates ignorieren möchten, sollten Sie ihn ignorewie von einigen anderen erwähnt verwenden. Aber ich bevorzuge es, eine Instanz der UserID zu erhalten, anstatt nur eine ID. Mit dieser Methode können Sie dies auch für andere Modelle tun

Regler

    public function update(UserRequest $request, User $user)
    {
        $user->update($request->all());

        return back();
    }

Benutzeranfrage

    public function rules()
    {
        return [
            'email' => [
                'required',
                \Illuminate\Validation\Rule::unique('users')->ignoreModel($this->route('user')),
            ],
        ];
    }

Update: ignoreModelanstelle von verwendenignore

Surya
quelle
0

Es funktioniert wie ein Zauber, den jemand ausprobieren kann. Hier habe ich Soft Delete Checker verwendet. Sie können das letzte weglassen: id,deleted_at, NULLWenn Ihr Modell keine Soft-Delete-Implementierung hat.

public function rules()
{
    switch ($this->method()) {
        case 'PUT':
            $emailRules = "required|unique:users,email,{$this->id},id,deleted_at,NULL";
            break;
        default:
            $emailRules = "required|unique:users,email,NULL,id,deleted_at,NULL";
            break;
    }

    return [
        'email' => $emailRules,
        'display_name' => 'nullable',
        'description' => 'nullable',
    ];
}

Vielen Dank.

Nazmus Shakib
quelle
0

Nachdem ich viel zu diesem Thema der Laravel-Validierung einschließlich einer einzigartigen Spalte recherchiert hatte, bekam ich endlich den besten Ansatz. Guck dir das mal bitte an

In Ihrem Controller

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

    class UserController extends Controller
    {
         public function saveUser(Request $request){
                $validator = Validator::make($request->all(),User::rules($request->get('id')),User::$messages);
                if($validator->fails()){
                    return redirect()->back()->withErrors($validator)->withInput();
                }
            }
    }

saveUser Methode kann zum Hinzufügen / Aktualisieren von Benutzerdatensätzen aufgerufen werden.

In deinem Modell

class User extends Model
{
    public static function rules($id = null)
    {
        return [
            'email_address' => 'required|email|unique:users,email_address,'.$id,
            'first_name' => "required",
            'last_name' => "required",
            'password' => "required|min:6|same:password_confirm",
            'password_confirm' => "required:min:6|same:password",
            'password_current' => "required:min:6"
        ];
    }
    public static $messages = [
        'email_address.required' => 'Please enter email!',
        'email_address.email' => 'Invalid email!',
        'email_address.unique' => 'Email already exist!',
        ...
    ];
}
Soubhagya Kumar Barik
quelle