Laravel:验证在更新时是唯一的


79

我知道这个问题已经问过很多次了,但是没有人解释在模型中进行验证时如何获取ID。

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

我的验证规则在模型中,因此如何将记录的ID传递给验证规则。

这是我的模特/用户

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"
];

模型/基本模型

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

我也很想知道$ id如何传递给模型。我正在使用方式/数据库包,并且有相同的问题。
托马斯

我在下面添加了我的答案。
user742736 2014年

我也遇到同样的问题
user3189734

Answers:


45

一个简单的解决方案。

在您的模型中

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

在您的控制器中,操作:更新

...
$rules = User::$rules;
$rules['email_address'] = $rules['email_address'] . ',id,' . $id;
$validationCertificate  = Validator::make($input, $rules); 

2
无法访问受保护的属性App \ User :: $ rules
Soubhagya Kumar Barik

@SoubhagyaKumarBarik您的Laravel是哪个版本?
里卡多·卡内拉斯

我的Laravel版本是7.x
Soubhagya Kumar Barik

@SoubhagyaKumarBarik我的答案是在2014年,版本4.x。因此,我认为对于版本7,您有不同的解决方案。检查下面的其他答案,或者对堆栈溢出创建一个新的问题,然后在此处添加链接以帮助您。
里卡多·卡内拉斯

80

附带说明一下email_address,在Laravel的内置身份验证系统中,电子邮件字段名称为just email。这是一个示例,您可以如何验证唯一字段,即有关更新的电子邮件:

Form Request中,您这样做:

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

或者,如果您要直接在控制器中验证数据:

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

更新:如果您正在更新在用户签名的,而不是注入User模型到你的路线,你可以访问时遇到未定义的属性id$this->user。在这种情况下,请使用:

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

自Laravel 5.7起,一种更优雅的方法是:

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

PS:我添加了一些其他规则,即必填规则和电子邮件,以使新手可以清楚地了解此示例。


1
我正在使用FormRequest,而这正是我需要做的。
桑尼

1
如何在FormRequest中访问/提供$ this-> user()或其他模型?像$这个- >文章()在ArticleFormRequest
克日什托夫·Dziuba

您要访问@KrzysztofDziuba的登录用户吗?
Rehmat

3
使用\Illuminate\Validation\Rule::unique()它确实是最优雅的方法。对我来说,这是最好的建议。谢谢
Magno Alberto

这非常适合进行独特的验证。现在,我想验证图像以进行更新,但前提是用户上载新图像。
HaxxanRaxa

26

有一种优雅的方法可以做到这一点。如果您使用的是资源控制器,则用于编辑记录的链接将如下所示:

/ users / {用户} / edit/ users / 1 / edit

在您的UserRequest中,规则应如下所示:

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

或者,如果您用于编辑记录的链接如下所示:

/ users / edit / 1

您也可以尝试以下操作:

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

这个答案是最好,最优雅的。我有个问题。链接到我的编辑记录看起来像,/users/{user}/edit并且我想防止unique:users,name在其他字段可以更新的同时进行更新。我将如何实现?
chamal101 '18


6

如果我了解您想要什么:

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

在模型更新方法中,例如,应接收带有参数的$ id。

对不起,我的英语不好。


$ id参数是否传递到模型中。我已经尝试过'email'=>'unique:users,email_address,{{$ id}}',没有运气'
user742736 2014年

{{$ id}}仅适用于带刀片的视图。因此,您应该使用字符串scape:'unique:users,email_address,'。$ id”或“ unique:users,email_address”。$ id。“”
卢卡斯·席尔瓦

不起作用 下面的错误1. Symfony \ Component \ Debug \ Exception \ FatalErrorException…/ app / models / User.php42 0. Illuminate \ Exception \ Handler handleShutdown <#unknown> 0 Symfony \ Component \ Debug \ Exception \ FatalErrorException语法错误,意外'。 ',期待']'
user742736 2014年

您可以显示模型方法或从错误页面打印图像?
卢卡斯·席尔瓦

6

经过5.2版测试的更简单的解决方案

在您的模型中

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

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

1
有时,id可能是URL的一部分,因此您可以在route参数中找到它:$ this-> route({field_name})例如:$ this-> route('id')
matthiku

其始终是更好的参考提到你复制的代码laracasts.com/discuss/channels/requests/...
阿夫塔卜函数naveed

您必须传递具有ID的隐藏字段才能使用$ this-> get('id')
Anand Mainali

5

解决方法如下:

更新:

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
}

而已。快乐编码:)



3

我会通过做这样的事情来解决

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

从请求中获取ID并将其传递给规则的位置


Laravel文档警告该技术,因为它可能引入安全漏洞。想象一下,攻击者可以通过向id参数提供任何内容来修改规则。laravel.com/docs/5.8/validation#rule-unique
Peter Krebs

3
$rules = [
    "email" => "email|unique:users, email, '.$id.', user_id"
];

在照亮\验证\规则\唯一;

唯一验证将字符串验证解析为Rule对象

唯一验证具有模式:唯一:%s,%s,%s,%s,%s'

对应于:表名,列,忽略,id列,格式

/**
 * 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()
    ), ',');
}

我喜欢你到那里。但是我不确定如何将其应用于模型规则。我应该在哪里替换{{$ id}}?
dmmd

3

最佳选择是在对数据进行唯一验证时,只需尝试一次,无需更多代码

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

email是字段名称, users 是表名称, email_address 是您想要唯一的表属性名称,并且 $userid 正在更新行ID


2

找到了最简单的方法,在我使用Laravel 5.2时工作正常

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

就我而言,我无法使用“:id”提取ID,所以我做了如下操作:if (in_array($this->method(), ['PUT', 'PATCH'])) { $rules['order'] .= ",{$this->route('videos')->id}"; }
Igor de Lorenzi

2

用于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),
            ]
        ]);
}

2

如果您不想对表名进行硬编码,则还可以使用模型类路径。

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

这里$ this-> id是0或要更新的记录ID。


2

你可以试试看

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"
];

1

这就是我最终要做的。我敢肯定有一种更有效的方法可以做到这一点,但这就是我想出的。

型号/User.php

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

型号/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;

     }
     ....

  }

我将$ user_id传递给控制器​​中的验证

Controller / UserController.php

public function update($id) { 

   .....

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

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

    ....


}

我最终只使用了watson / validation
Thomas

谢谢,我也来看看。
user742736 2014年

1

在更新任何现有数据写入验证器时,如下所示:

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

这将跳过/忽略针对特定列的现有用户ID的唯一值匹配。


1

测试以下代码:

$validator = Validator::make(
            array(
              'E-mail'=>$request['email'],
             ),
            array(
              'E-mail' => 'required|email|unique:users,email,'.$request['id'],
             ));

尽量不要在验证规则中直接使用请求参数。攻击者可以修改提供给控制器的id参数(甚至使参数等于0|nullable或更差)。laravel.com/docs/5.8/validation#rule-unique
Peter Krebs

1

有一种简单而优雅的方法可以做到这一点。如果要在正文请求中或通过查询参数传递user_id。例如

/update/profile?user_id=

然后在您的请求规则中

  public function rules(Request $request)
    {
        return [
            'first_name' => 'required|string',
            'last_name' => 'required|string',
            'email' => ['required','email', 'string', Rule::unique('users')->ignore($request->user_id )],
            'phone_number' => ['required', 'string', Rule::unique('users')->ignore($request->user_id )],
        ];
    }

更妙的是,您可以auth->id()代替$request->user_id获取登录用户ID。


0

我的解决方案:

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

然后在验证中:

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

逻辑是,如果表单中的电子邮件地址不同,我们需要对其进行验证,如果电子邮件没有更改,则不需要进行验证,因此请从验证中删除该规则。


0

对于unique控制器中的规则-store方法和update方法显然会有所不同,我通常在控制器内创建一个函数,该函数rules将返回规则数组。

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

然后在各自storeupdate方法中

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

这样就不必为存储和更新方法定义两个不同的规则集。

如果您可以牺牲一些可读性,也可以

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


不要直接使用这样的请求参数。攻击者可以将id参数修改为任意值。laravel.com/docs/5.8/validation#rule-unique
Peter Krebs

0

我读了上一篇文章,但没有一个解决真正的问题。我们需要使用唯一的规则来应用于添加和编辑案例。我在编辑和添加大小写时使用此规则,并且工作正常。

在我的解决方案中,我使用Request Class中的rule函数。

  1. 我通过编辑表单上的隐藏输入表单字段发送了ID。
  2. 在Rule函数上,我们按唯一列查找并获得记录。
  3. 现在评估情况。如果存在记录和ID相等,则必须不激活唯一标识(这意味着编辑记录)。

在代码上:

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


    }

0

由于执行更新时您将要忽略要更新的记录,因此您将希望使用ignore其他人提到的记录。但我更喜欢收到一个实例UserID而不是ID。此方法还允许您对其他模型执行相同的操作

控制者

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

        return back();
    }

用户请求

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

更新:ignoreModel代替ignore


0

就像有人可以尝试的魅力一样。在这里,我使用了软删除检查器。您可以省略最后一个:id,deleted_at, NULL如果您的模型没有软删除实现。

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

谢谢。


0

在对laravel验证主题(包括唯一列)进行了大量研究之后,终于找到了最佳方法。请看一看

在您的控制器中

    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 可以调用方法来添加/更新用户记录。

在你的模特儿

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!',
        ...
    ];
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.