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

时间:2017-06-06 17:16:34

标签: php yii2

在Yii2中,我的数据库中有两个字段:emailshopId

  • EmailshopId应该unique在一起
  • Email也可以是emptyNULL),而shopId始终是整数

这些是我在模型中的规则:

[['email'],'default','value' => NULL],
[['shopId'], 'integer'],
[['email','shopId'], 'unique', 'targetAttribute' => ['email', 'shopId'], 'message' => 'Already taken!'],

如果我有两个条目,例如,这不起作用email="NULL"shopId="1"

我该如何解决这个问题?

4 个答案:

答案 0 :(得分:2)

false属性设置为jmap -dump:file=heapdump.hprof,format=b http://www.yiiframework.com/doc-2.0/yii-validators-validator.html# $ skipOnEmpty-detail

答案 1 :(得分:2)

虽然您的解决方案正常运行但技术上并不正确。 验证规则

[['email','shopId'], 'unique', 'targetAttribute' => ['email', 'shopId']]
如果email不为空(所需功能),

将验证shopId对于给定email是唯一的,但它也会验证shopId在给定email时是唯一的{1}}如果shopId不为空(不需要)。您正在使用两个查询来验证两个字段。

符合您需求的验证规则是

[['email'], 'unique', 'targetAttribute' => ['email', 'shopId']]

说“如果email不为空,请检查emailshopId的组合是否唯一,并将结果绑定到email属性”。

答案 2 :(得分:0)

我在规则

中使用了when条件
[
    ['email', 'shopId'],
    'unique',
    'targetAttribute' => ['email', 'shopId'],
    'message' => 'Diese E-Mail Adresse ist bereits registriert!',
    'when' => function ($model) {
        return !empty($model->email);
    }
],

答案 3 :(得分:0)

我已经创建了一个解决方案来强制跨一组字段的唯一性,包括可空字段,它在可以简单地放入任何 Yii2 模型的通用 PHP Trait 中利用 yii\validators\UniqueValidator::$filter 回调。完整代码示例如下:

通用特征 [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;
                    }
                }
            }
        }
    }
}

示例模型使用

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

查看关于 Yii2 核心唯一验证器的这个缺点的完整讨论,以及核心开发团队给出的原因:https://github.com/yiisoft/yii2/issues/4333