[英]yii2 unique validator only when field not empty
In Yii2 I have two fields in my Database: email
and shopId
在 Yii2 中,我的数据库中有两个字段:
email
和shopId
Email
and shopId
should be unique
together Email
和shopId
一起应该是unique
Email
could also be empty
( NULL
) while shopId
is always an integer Email
也可以为empty
( NULL
) 而shopId
始终是一个整数These are my rules in the Model:这些是我在模型中的规则:
[['email'],'default','value' => NULL],
[['shopId'], 'integer'],
[['email','shopId'], 'unique', 'targetAttribute' => ['email', 'shopId'], 'message' => 'Already taken!'],
This is not working when I have two entries with eg email="NULL"
and shopId="1"
.当我有两个条目时,这不起作用,例如
email="NULL"
和shopId="1"
。
How can I solve this?我该如何解决这个问题?
While your solution is working it isn't technically correct.当您的解决方案有效时,它在技术上并不正确。 Validation rule
验证规则
[['email','shopId'], 'unique', 'targetAttribute' => ['email', 'shopId']]
will validate email
to be unique with given shopId
if email
is not empty (desired functionality), but it will also validate shopId
to be unique with given email
if shopId
is not empty (unwanted).如果
email
不为空(所需功能),将验证email
与给定shopId
唯一性,但如果shopId
不为空(不需要),它也会验证shopId
与给定email
唯一性。 You are validating two fields with two queries to DB.您正在使用对 DB 的两个查询来验证两个字段。
Validation rule that fits your needs is符合您需求的验证规则是
[['email'], 'unique', 'targetAttribute' => ['email', 'shopId']]
saying "If email
is not empty, check if combination of email
and shopId
is unique and bind result to email
attribute".说“如果
email
不为空,请检查email
和shopId
组合是否唯一并将结果绑定到email
属性”。
将skipOnEmpty
属性设置为false
http://www.yiiframework.com/doc-2.0/yii-validators-validator.html# $skipOnEmpty-detail
I used the when condition inside the rule我在规则中使用了 when 条件
[
['email', 'shopId'],
'unique',
'targetAttribute' => ['email', 'shopId'],
'message' => 'Diese E-Mail Adresse ist bereits registriert!',
'when' => function ($model) {
return !empty($model->email);
}
],
I have created a solution to enforce uniqueness across a set of fields, including nullable fields, which utilises the yii\\validators\\UniqueValidator::$filter callback in a generic PHP Trait that can be simply dropped into any Yii2 model.我创建了一个解决方案来强制跨一组字段的唯一性,包括可空字段,它利用yii\\validators\\UniqueValidator::$filter回调在通用 PHP Trait 中可以简单地放入任何 Yii2 模型中。 Full code examples below:
完整代码示例如下:
Generic Trait [common/models/traits/UniqueValidatorFilterTrait.php]通用特征[common/models/traits/UniqueValidatorFilterTrait.php]
<?php
namespace common\models\traits;
use yii\db\ActiveQuery;
/**
* Trait UniqueValidatorFilterTrait provides custom anonymous function callback methods.
*
* @package common\models\traits
* @author Alec Pritchard <ajmedway@gmail.com>
*/
trait UniqueValidatorFilterTrait
{
/**
* Custom anonymous function for [[yii\validators\UniqueValidator::$filter]], used to modify the passed-in
* [[\yii\db\ActiveRecord]] query to be applied to the DB query used to check the uniqueness of the input value.
*
* @param ActiveQuery $query
* @param array $nullableUniqueFields the field names to ensure are considered and validated for the uniqueness
* of a set of model attributes, even if some have empty/null values.
* @see \yii\validators\UniqueValidator::$filter
*/
public function filterFunctionUniqueWithNull(ActiveQuery $query, array $nullableUniqueFields = [])
{
// check if at least one of the $nullableUniqueFields currently has an empty value loaded
$hasEmptyUniqueField = false;
foreach ($nullableUniqueFields as $field) {
if (empty($this->{$field})) {
$hasEmptyUniqueField = true;
break;
}
}
// proceed to modify AR query
if ($hasEmptyUniqueField) {
// change query item for the $nullableUniqueFields, which only checks against empty string by design
// @link https://github.com/yiisoft/yii2/issues/4333#issuecomment-57739619
// ensure the composite unique constraint is applied to include NULL values for all $nullableUniqueFields
foreach ($query->where as $whereItemKey => $whereItem) {
if (!is_array($whereItem)) continue;
foreach ($whereItem as $columnName => $value) {
// check if this column is one of the unique fields and if it currently has an empty string
if (str_replace($nullableUniqueFields, '', $columnName) != $columnName
&& $value === '') {
// change from '' to NULL
$query->where[$whereItemKey][$columnName] = null;
}
}
}
}
}
}
Example Model Usage示例模型使用
<?php
namespace common\models;
use Yii;
use common\models\traits\UniqueValidatorFilterTrait;
/**
* This is the model class for table "my_table".
*
* @property int $id
* @property null|string $email
* @property int $shop_id
*/
class MyTable extends \yii\db\ActiveRecord
{
use UniqueValidatorFilterTrait;
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'my_table';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[
['email', 'shop_id'],
'unique',
'targetAttribute' => ['email', 'shop_id'],
// ensure the composite unique constraint is applied to include NULL values for specified nullable fields
'filter' => function (MyTableQuery $query) {
/* @see \common\models\traits\UniqueValidatorFilterTrait::filterFunctionUniqueWithNull() */
$this->filterFunctionUniqueWithNull($query, ['email', 'shop_id']);
},
// ensure that we don't skip on empty values, namely the `email` and/or `shop_id`
'skipOnEmpty' => false,
],
];
}
See the full discussion about this shortcoming of the Yii2 core unique validator, and reasons given by core dev team here: https://github.com/yiisoft/yii2/issues/4333关于 Yii2 核心唯一验证器的这个缺点的完整讨论,以及核心开发团队给出的原因在这里: https : //github.com/yiisoft/yii2/issues/4333
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.