简体   繁体   English

yii2 唯一验证器仅当字段不为空时

[英]yii2 unique validator only when field not empty

In Yii2 I have two fields in my Database: email and shopId在 Yii2 中,我的数据库中有两个字段: emailshopId

  • Email and shopId should be unique together EmailshopId一起应该是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不为空,请检查emailshopId组合是否唯一并将结果绑定到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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM