Модель обновления Laravel с уникальным правилом проверки атрибута

У меня есть модель User laravel, которая имеет уникальное правило проверки username и email . В моем репозитории, когда я обновляю модель, я переопределяю поля, чтобы не иметь проблемы с необходимой проверкой правильности:

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

Это не позволяет

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

Есть ли способ исправить это изящно?

Related of "Модель обновления Laravel с уникальным правилом проверки атрибута"

Добавьте id экземпляра, который в настоящее время обновляется в валидаторе.

  1. Передайте id вашего экземпляра, чтобы игнорировать уникальный валидатор.

  2. В валидаторе используйте параметр для обнаружения, если вы обновляете или создаете ресурс.

Если обновление, заставьте единственное правило игнорировать данный идентификатор:

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

Если вы создаете, действуйте как обычно:

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

Еще один элегантный способ …

В своей модели создайте статическую функцию:

 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); } 

Проверка на создание:

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

Проверка при обновлении:

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

Проверка при обновлении с некоторыми дополнительными правилами:

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

Ницца.

Работая в моем вопросе:

 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; } 

это то, что я сделал, исходя из принятого ответа выше.

EDIT: с запросами формы все упрощается:

 <?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, ]; } } 

Вам просто нужно передать UpdateUserRequest вашему методу обновления и обязательно установить POST идентификатор модели.

Laravel 5 совместимый и общий способ:

У меня была такая же проблема и она была решена в общем виде. Если вы создаете элемент, он использует правила по умолчанию, если вы обновите элемент, он будет проверять ваши правила :unique и автоматически вставлять исключение (при необходимости).

Создайте класс BaseModel и пусть все ваши модели наследуют от него:

 <?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; } } 

Теперь вы определяете свои правила в своей модели, как вы привыкли:

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

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

 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'); } 

Это оно! 🙂 Обратите внимание, что при создании мы вызываем Role::getRules() а при редактировании мы вызываем $role->getUpdateRules() .

У меня есть класс BaseModel, поэтому мне нужно что-то более общее.

 //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; } 

В пользовательском классе предположим, что мне нужно только письмо и имя для проверки:

 //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; } не //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; } не //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; } 

Я тестировал это с помощью phpunit и отлично работал.

 //tests/models/UserTest.php public function testUpdateExistingUser() { $user = User::find(1); $result = $user->id; $this->assertEquals(true, $result); $user->name = 'test update'; $user->email = 'ddd@test.si'; $user->save(); $this->assertTrue($user->isValid($user->id), 'Expected to pass'); } 

Я надеюсь, что это поможет кому-то, даже если для получения лучшей идеи. Спасибо, что поделились с вами. (проверено на Laravel 5.0)

Простой пример обновления ролей


 // 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); } } 

Уникальная проверка с использованием идентификатора столбца в Laravel

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

Я называю разные классы проверки для Store и Update. В моем случае я не хочу обновлять все поля, поэтому у меня есть baseRules для общих полей для Create and Edit. Добавьте дополнительные классы проверки для каждого. Надеюсь, мой пример полезен. Я использую Laravel 4.

Модель:

 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); } 

Контроллер: Способ сохранения:

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

Метод обновления:

 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(); } } 
 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; } 

У меня такая же проблема. Что я сделал: добавьте в мое скрытое поле с идентификатором модели и в валидаторе проверьте уникальность, только если у меня есть некоторый идентификатор из представления.

 $this->validate( $request, [ 'index' => implode('|', ['required', $request->input('id') ? '' : 'unique:members']), 'name' => 'required', 'surname' => 'required', ] ); 
 '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); }) ], 

Вы можете попробовать код ниже

 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', ]; 

Если у вас есть другой столбец, который используется как внешний ключ или индекс, тогда вы должны указать это также в правиле, подобном этому.

 'phone' => [ "required", "phone", Rule::unique('shops')->ignore($shopId, 'id')->where(function ($query) { $query->where('user_id', Auth::id()); }), ],