web-dev-qa-db-de.com

Laravel-Aktualisierungsmodell mit eindeutiger Validierungsregel für Attribut

Ich habe ein Laravel User-Modell, das eine eindeutige Validierungsregel für username und email hat. Wenn ich in meinem Repository das Modell aktualisiere, validiere ich die Felder erneut, um bei der erforderlichen Validierung der Regeln kein Problem zu haben:

public function update($id, $data) {
    $user = $this->findById($id);
    $user->fill($data);
    $this->validate($user->toArray());
    $user->save();
    return $user;
}

Dies schlägt beim Testen mit fehl 

ValidationException: {"username":["The username has already been taken."],"email":["The email has already been taken."]}

Gibt es eine Möglichkeit, dies elegant zu beheben?

51
Tom Macdonald

Hängen Sie die id der aktuell aktualisierten Instanz an den Prüfer.

  1. Übergeben Sie die Variable id Ihrer Instanz, um den eindeutigen Prüfer zu ignorieren.

  2. Verwenden Sie im Validator einen Parameter, um zu ermitteln, ob Sie Aktualisierung oder Erstellung der Ressource durchführen.

Erzwingen Sie bei der Aktualisierung, dass die eindeutige Regel eine angegebene ID ignoriert:

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

Gehen Sie beim Erstellen wie gewohnt vor:

//rules
'email' => 'unique:users,email_address',
108
marcanuy

Ein anderer eleganter Weg ...

Erstellen Sie in Ihrem Modell eine statische Funktion:

public static function rules ($id=0, $merge=[]) {
    return array_merge(
        [
            'username'  => 'required|min:3|max:12|unique:users,username' . ($id ? ",$id" : ''),
            'email'     => 'required|email|unique:member'. ($id ? ",id,$id" : ''),
            'firstname' => 'required|min:2',
            'lastname'  => 'required|min:2',
            ...
        ], 
        $merge);
}

Validierung beim Erstellen:

$validator = Validator::make($input, User::rules());

Validierung beim Update:

$validator = Validator::make($input, User::rules($id));

Validierung beim Update mit einigen zusätzlichen Regeln:

$extend_rules = [
    'password'       => 'required|min:6|same:password_again',
    'password_again' => 'required'
];
$validator = Validator::make($input, User::rules($id, $extend_rules));

Nett.

27
BaM

Arbeit in meiner Frage:

public function update($id, $data) {
    $user = $this->findById($id);
    $user->fill($data);
    $this->validate($user->toArray(), $id);
    $user->save();
    return $user;
}


public function validate($data, $id=null) {
    $rules = User::$rules;
    if ($id !== null) {
        $rules['username'] .= ",$id";
        $rules['email'] .= ",$id";
    }
    $validation = Validator::make($data, $rules);
    if ($validation->fails()) {
        throw new ValidationException($validation);
    }
    return true;
}

das habe ich getan, basierend auf der oben akzeptierten Antwort.

BEARBEITEN: Mit Formularanfragen wird alles einfacher:

<?php namespace App\Http\Requests;

class UpdateUserRequest extends Request
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required|unique:users,username,'.$this->id,
            'email' => 'required|unique:users,email,'.$this->id,
        ];
    }
}

Sie müssen lediglich UpdateUserRequest an Ihre Aktualisierungsmethode übergeben, und stellen Sie sicher, dass die Modell-ID POST ist.

8
Tom Macdonald

Laravel 5 kompatible und generische Weise:

Ich hatte gerade das gleiche Problem und löste es auf eine generische Art und Weise. Wenn Sie ein Element erstellen, verwendet es die Standardregeln. Wenn Sie ein Element aktualisieren, werden Ihre Regeln für :unique überprüft und automatisch ein Ausschluss eingefügt (falls erforderlich).

Erstellen Sie eine BaseModel-Klasse, und lassen Sie alle Ihre Modelle davon erben:

<?php namespace App;

use Illuminate\Database\Eloquent\Model;

class BaseModel extends Model {

    /**
     * The validation rules for this model
     *
     * @var array
     */
    protected static $rules = [];

    /**
     * Return model validation rules
     *
     * @return array
     */
    public static function getRules() {
        return static::$rules;
    }

    /**
     * Return model validation rules for an update
     * Add exception to :unique validations where necessary
     * That means: enforce unique if a unique field changed.
     * But relax unique if a unique field did not change
     *
     * @return array;
     */
    public function getUpdateRules() {
        $updateRules = [];
        foreach(self::getRules() as $field => $rule) {
            $newRule = [];
            // Split rule up into parts
            $ruleParts = explode('|',$rule);
            // Check each part for unique
            foreach($ruleParts as $part) {
                if(strpos($part,'unique:') === 0) {
                    // Check if field was unchanged
                    if ( ! $this->isDirty($field)) {
                        // Field did not change, make exception for this model
                        $part = $part . ',' . $field . ',' . $this->getAttribute($field) . ',' . $field;
                    }
                }
                // All other go directly back to the newRule Array
                $newRule[] = $part;
            }
            // Add newRule to updateRules
            $updateRules[$field] = join('|', $newRule);

        }
        return $updateRules;
    }
}    

Sie definieren jetzt Ihre Regeln in Ihrem Modell wie Sie es gewohnt sind:

protected static $rules = [
    'name' => 'required|alpha|unique:roles',
    'displayName' => 'required|alpha_dash',
    'permissions' => 'array',
];

Und validiere sie in deinem Controller. Wenn das Modell nicht validiert wird, wird es automatisch mit den entsprechenden Validierungsfehlern zum Formular zurückgeleitet. Wenn keine Validierungsfehler aufgetreten sind, wird der Code danach weiter ausgeführt.

public function postCreate(Request $request)
{
    // Validate
    $this->validate($request, Role::getRules());
    // Validation successful -> create role
    Role::create($request->all());
    return redirect()->route('admin.role.index');
}

public function postEdit(Request $request, Role $role)
{
    // Validate
    $this->validate($request, $role->getUpdateRules());
    // Validation successful -> update role
    $role->update($request->input());
    return redirect()->route('admin.role.index');
}

Das ist es! :) Beachten Sie, dass wir beim Erstellen Role::getRules() und beim Bearbeiten $role->getUpdateRules() aufrufen.

3
cgross

Eindeutige Validierung mit unterschiedlicher Spalten-ID in Laravel

'UserEmail'=>"required|email|unique:users,UserEmail,$userID,UserID"
3
user5797691

Ein einfaches Beispiel für das Rollen-Update


// model/User.php
class User extends Eloquent
{

    public static function rolesUpdate($id)
    {
        return array(
            'username'              => 'required|alpha_dash|unique:users,username,' . $id,
            'email'                 => 'required|email|unique:users,email,'. $id,
            'password'              => 'between:4,11',
        );
    }
}       

.

// controllers/UsersControllers.php
class UsersController extends Controller
{

    public function update($id)
    {
        $user = User::find($id);
        $validation = Validator::make($input, User::rolesUpdate($user->id));

        if ($validation->passes())
        {
            $user->update($input);

            return Redirect::route('admin.user.show', $id);
        }

        return Redirect::route('admin.user.edit', $id)->withInput()->withErrors($validation);
    }

}
2
Ricardo Canelas

Ich habe die BaseModel-Klasse, also brauchte ich etwas generischeres.

//app/BaseModel.php
public function rules()
{
    return $rules = [];
}
public function isValid($id = '')
{

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

    if($validation->passes()) return true;
    $this->errors = $validation->messages();
    return false;
}

Nehmen wir an, in der Benutzerklasse brauche ich nur E-Mail und Namen, um bestätigt zu werden:

//app/User.php
//User extends BaseModel
public function rules($id = '')
{
    $rules = [
                'name' => 'required|min:3',
                'email' => 'required|email|unique:users,email',
                'password' => 'required|alpha_num|between:6,12',
                'password_confirmation' => 'same:password|required|alpha_num|between:6,12',
            ];
    if(!empty($id))
    {
        $rules['email'].= ",$id";
        unset($rules['password']);
        unset($rules['password_confirmation']);
    }

    return $rules;
}

Ich habe das mit phpunit getestet und funktioniert einwandfrei. 

//tests/models/UserTest.php 
public function testUpdateExistingUser()
{
    $user = User::find(1);
    $result = $user->id;
    $this->assertEquals(true, $result);
    $user->name = 'test update';
    $user->email = '[email protected]';
    $user->save();

    $this->assertTrue($user->isValid($user->id), 'Expected to pass');

}

Ich hoffe, jemandem wird geholfen, auch wenn er eine bessere Idee hat. Danke für das Teilen von Ihnen .. (getestet auf Laravel 5.0)

2
Angel M.

Ich rufe verschiedene Validierungsklassen für Store and Update an. In meinem Fall möchte ich nicht alle Felder aktualisieren, daher habe ich baseRules für allgemeine Felder für Erstellen und Bearbeiten. Fügen Sie für jede weitere Validierungsklassen hinzu. Ich hoffe mein Beispiel ist hilfreich. Ich benutze Laravel 4.

Modell: 

public static $baseRules = array(
    'first_name' => 'required',
    'last_name'  => 'required',
    'description' => 'required',
    'description2' => 'required',
    'phone'  => 'required | numeric',
    'video_link'  => 'required | url',
    'video_title'  => 'required | max:87',
    'video_description'  => 'required',
    'sex' => 'in:M,F,B',
    'title'  => 'required'
);

public static function validate($data)
{
    $createRule = static::$baseRules;
    $createRule['email'] = 'required | email | unique:musicians';
    $createRule['band'] = 'required | unique:musicians';
    $createRule['style'] = 'required';
    $createRule['instrument'] = 'required';
    $createRule['myFile'] = 'required | image';

    return Validator::make($data, $createRule);
}

public static function validateUpdate($data, $id)
{
    $updateRule = static::$baseRules;
    $updateRule['email'] = 'required | email | unique:musicians,email,' . $id;
    $updateRule['band'] = 'required | unique:musicians,band,' . $id;
    return Validator::make($data, $updateRule);
}

Controller: Speichermethode:

public function store()
{
    $myInput = Input::all();
    $validation = Musician::validate($myInput);
    if($validation->fails())
    {
        $key = "errorMusician";
        return Redirect::to('musician/create')
        ->withErrors($validation, 'musicain')
        ->withInput();
    }
}

Update-Methode:

public function update($id) 
{
    $myInput = Input::all();
    $validation = Musician::validateUpdate($myInput, $id);
    if($validation->fails())
    {
        $key = "error";
        $message = $validation->messages();
        return Redirect::to('musician/' . $id)
        ->withErrors($validation, 'musicain')
        ->withInput();
    }
}
1
Oat

oder was Sie in Ihrer Formularanfrage tun könnten, ist (für Laravel 5.3+)

public function rules()
    {
        return [

            'email' => 'required|email|unique:users,email,'.$this->user, //here user is users/{user} from resource's route url
               ];
    }

ich habe es in Laravel 5.6 gemacht und es hat funktioniert.

1
DaShInG Sid

Sie können den folgenden Code ausprobieren

return [
    'email' => 'required|email|max:255|unique:users,email,' .$this->get('id'),
    'username' => 'required|alpha_dash|max:50|unique:users,username,'.$this->get('id'),
    'password' => 'required|min:6',
    'confirm-password' => 'required|same:password',
];
1
luongit
'email' => [
    'required',
    Rule::exists('staff')->where(function ($query) {
        $query->where('account_id', 1);
    }),
],

'email' => [
    'required',
    Rule::unique('users')->ignore($user->id)->where(function ($query) {
        $query->where('account_id', 1);
    })
],
1
tanmay

Wenn Sie eine andere Spalte haben, die als Fremdschlüssel oder Index verwendet wird, müssen Sie diese auch in der Regel so angeben.

'phone' => [
                "required",
                "phone",
                Rule::unique('shops')->ignore($shopId, 'id')->where(function ($query) {
                    $query->where('user_id', Auth::id());
                }),
            ],
1
Chaudhry Waqas
public static function custom_validation()
{
    $rules = array('title' => 'required ','description'  => 'required','status' => 'required',);
    $messages = array('title.required' => 'The Title must be required','status.required' => 'The Status must be required','description.required' => 'The Description must be required',);
    $validation = Validator::make(Input::all(), $rules, $messages);
    return $validation;
}
1
Binal Patel

Ich hatte das gleiche Problem ... Was habe ich getan: Fügen Sie in meiner Ansicht ein verstecktes Feld mit der ID eines Modells hinzu und überprüfen Sie das eindeutige Element, wenn ich eine ID von der Ansicht habe.

$this->validate(
        $request,
        [
            'index'       => implode('|', ['required', $request->input('id') ? '' : 'unique:members']),
            'name'        => 'required',
            'surname'     => 'required',
        ]
);
1
svolkov

Für eine benutzerdefinierte FormRequest und Laravel 5.7+ können Sie die ID Ihres aktualisierten Modells wie folgt erhalten:

public function rules()
    {
        return [
            'name' => 'required|min:5|max:255|unique:schools,name,'.\Request::instance()->id
        ];
    }
0
steve

Laravel 5.8 einfach und leicht

sie können dies alles in einer Formularanforderung mit ganz nett tun. . .

erst machen sie ein feld mit dem sie die id (unsichtbar) im normalen editierformular übergeben können. d.h.

 <div class="form-group d-none">
      <input class="form-control" name="id" type="text" value="{{ $example->id }}" >
 </div>

... Stellen Sie dann sicher, dass Sie die Regelklasse wie folgt zu Ihrer Formularanforderung hinzufügen:

use Illuminate\Validation\Rule;

... Fügen Sie die Unique-Regel hinzu und ignorieren Sie die aktuelle ID wie folgt:

public function rules()
{
    return [
          'example_field_1'  => ['required', Rule::unique('example_table')->ignore($this->id)],
          'example_field_2'  => 'required',

    ];

... Geben Sie schließlich hint die Formularanforderung in der Aktualisierungsmethode wie in der Speichermethode ein:

 public function update(ExampleValidation $request, Examle $example)
{
    $example->example_field_1 = $request->example_field_1;
    ...
    $example->save();

    $message = "The aircraft was successully updated";


    return  back()->with('status', $message);


}

Auf diese Weise wird der Code nicht unnötig wiederholt :-)

0
Rick