具有唯一字段的 Laravel 验证服务

Posted

技术标签:

【中文标题】具有唯一字段的 Laravel 验证服务【英文标题】:Laravel validation service with unique fields 【发布时间】:2014-03-09 13:57:23 【问题描述】:

我正在使用验证服务来验证用户提交的表单输入(类似于:http://laravel.io/bin/vrk)。

使用这种方法(验证服务类)根据一组规则验证用户提交的表单数据,当规则具有unique 规则时,如何验证用户提交的数据。例如,如果用户的用户名是 John,那么当我尝试更新模型时,验证失败(因为 John 作为用户名存在,即使它属于当前模型)。

为了在 Laravel 中解决这个问题,我可以做类似'username' => 'required|alpha_dash|unique:users,username'.$id 的事情。我应该如何修改链接中的当前代码以最好地适应这一点?我是否应该根据场景使用单独的验证器类(例如,UserCreateValidator、UserUpdateValidator 等)。或者我应该在 UserValidator 类中创建单独的验证规则,然后在调用 UserValidator 时将我想要的规则作为参数传递给构造函数或 passes() 方法?

【问题讨论】:

【参考方案1】:

嗯,你在发帖做什么?

因为这是你应该做的:

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

$user->username = $input['username'];
$user->email = $input['email'];

$user->save();

更新记录。

或者

$input = array('username' => 'w0rldart', 'email' => 'hahafu@dumbledore.com');
// Retrieve the user by the attributes, or create it if it doesn't exist, 
// based on the data above, which can come from an Input::all();
$user = User::firstOrCreate($input);

...很多可能性。但你也可以这样做:

$input = array_forget($input, 'username');

为了符合您的情况,请从输入数组中删除 username 索引。

这就是我根据您提供给我们的信息告诉您的全部内容。如果您想要更多,请发布控制器的 put 方法。

更新:

这是我的 PUT 方法版本:http://laravel.io/bin/OaX

我真的认为try catch 语法毫无用处,因为很明显User 模型将永远存在。但我仍然不知道您要更新什么。即使我现在无法对其进行测试,但我认为更新不会导致该问题,如果确实如此,请通过用户名/ID 检索用户,然后取消设置输入数组中的 username 索引,并更新它根据您的规格。

【讨论】:

详细地说,控制器看起来像这样laravel.io/bin/WbD。我想使用验证器服务验证表单提交,并且只有在验证通过时才调用 save 。这适用于创建,但由于用户名字段的唯一约束,创建时失败。 更新失败,你想说吧?正如我在评论中提到的那样,您要更新什么?用户名?【参考方案2】:

我认为你可以这样做

第一次更新UserValidator这样的规则。

class UserValidator extends Validator 

    // Override parent class $rules
    protected $rules = [
        'default' => [
            'username' => 'required|alpha_dash|unique:users',
            'password' => 'required|between:6,16|confirmed',
            'password_confirmation' => 'required|between:6,16'
        ],
        'update' => [
            'username' => null,
        ]
    ];
 

然后像这样修改Validatorpasses方法

public function passes($rule = null) 

    $rules = $this->rules['default'];
    if ($rule && isset($this->rules[$rule])) 
        $rules = array_merge($rules, $this->rules[$rule]);
    

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

    if ($validator->fails()) 
        $this->validator = $validator;
        return false;
    

    return true;

然后在控制器的PUT 方法中,这会将update 规则合并到default 规则

$rule = 'update';

// user has changed his username
if ($input['username'] !== $old_username) 
    $rule = 'create'; // validate uniqueness


else 
    unset($input['username']); // remove it, we don't validate it anymore since it's the same


$validator->passes($rule); // override 'default' rules with 'update' rules

您不必更改控制器的 POST 方法,它会保持不变

$validator->passes(); // use 'default' rules

【讨论】:

如果我将 username 规则更改为 required|alpha_dash 是否意味着可以传入与表中另一个用户名匹配的用户名并且仍然有效? 是的,更新用户名的逻辑是这样的,如果新用户名不等于旧用户名,那么我们必须验证唯一性,如果相同则忽略它。【参考方案3】:

如果我的理解是正确的,那么由于模型的主键限制,您在更新数据时遇到了问题。您需要做的是创建两组规则,一组用于插入,一组用于更新。

假设你有一组这样的规则:

protected $rules = [
'id' => 'required|unique:users'
]

你应该实现这样的东西:

protected $rules = [
'id' => 'required|unique|unique:users,id,' . $this->id
];

这应该告诉 laravel 忽略表 users 中指定 id 的重复 id,在这种情况下,是当前对象的 id。

您可以在 http://laravel.com/docs/validation 的 laravel 文档中阅读更多相关信息

unique:table,column,except,idColumn

验证中的字段在给定的数据库表上必须是唯一的。 如果未指定列选项,将使用字段名称。

【讨论】:

谢谢,但我的具体意思是我应该将这两组规则存储在一个类中,例如 UserValidator,还是有单独的特定验证器,如 UserUpdateValidator、UserCreatorValidator 等?我会在问题中详细说明,道歉 当你使用验证器时,你必须告诉它应该使用的规则集。您可以参考这个其他问题以获得进一步说明:***.com/questions/18447404/…【参考方案4】:

UserValidator类的一点修改

class UserValidator extends Validator 

    // Override parent class $rules
    protected $rules = [
        'username' => 'required|alpha_dash|unique:users',
        'password' => 'required|between:6,16|confirmed',
        'password_confirmation' => 'required|between:6,16'
    ];

    // ADD THIS
    public function __construct(Array $rules = array())
    
        parent::__construct();
        if(count($rules))
            foreach($rules as $k => $v) $this->rules[$k] = $v;
        
    
    

在你的控制器putUpdate方法中

$user = User::whereUsername($username)->firstOrFail();
$rules = ['username' => 'required|alpha_dash|unique:users,username,'. $user->id];
// Pass the rule to update the rule for username in this method
$validator = \Services\Validators\UserValidator(Input::all(), $rules);

检查the manual here。

【讨论】:

以上是关于具有唯一字段的 Laravel 验证服务的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5.7 具有 JSON 字段类型的存在/唯一验证

Laravel 验证:唯一,当名称相同,但其他字段不同时

Laravel更新模型,具有属性的唯一验证规则

Laravel 5.5 对具有不同列名的单独表的唯一验证规则

laravel 中的唯一字段验证问题

laravel 表单验证 多个字段组合后唯一