1 | <?php |
||||
2 | |||||
3 | namespace codeonyii\yii2validators; |
||||
4 | |||||
5 | use Yii; |
||||
6 | use yii\base\InvalidConfigException; |
||||
7 | use yii\i18n\PhpMessageSource; |
||||
8 | use yii\validators\Validator; |
||||
9 | |||||
10 | /** |
||||
11 | * Checks if one or more in a list of attributes are filled. |
||||
12 | * |
||||
13 | * In the following example, the `attr1` and `attr2` attributes will |
||||
14 | * be verified. If none of them are filled `attr1` will receive an error: |
||||
15 | * |
||||
16 | * ~~~[php] |
||||
17 | * // in rules() |
||||
18 | * return [ |
||||
19 | * ['attr1', AtLeastValidator::className(), 'in' => ['attr1', 'attr2']], |
||||
20 | * ]; |
||||
21 | * ~~~ |
||||
22 | * |
||||
23 | * In the following example, the `attr1`, `attr2` and `attr3` attributes will |
||||
24 | * be verified. If at least 2 (`min`) of them are not filled, `attr1` will |
||||
25 | * receive an error: |
||||
26 | * |
||||
27 | * ~~~[php] |
||||
28 | * // in rules() |
||||
29 | * return [ |
||||
30 | * ['attr1', AtLeastValidator::className(), 'min' => 2, 'in' => ['attr1', 'attr2', 'attr3']], |
||||
31 | * ]; |
||||
32 | * ~~~ |
||||
33 | * |
||||
34 | * If you want to show errors in a summary instead in the own attributes, you can do this: |
||||
35 | * ~~~[php] |
||||
36 | * // in rules() |
||||
37 | * return [ |
||||
38 | * ['!id', AtLeastValidator::className(), 'in' => ['attr1', 'attr2', 'attr3']], // where `id` is the pk |
||||
39 | * ]; |
||||
40 | * |
||||
41 | * // view: |
||||
42 | * ... |
||||
43 | * echo yii\helpers\Html::errorSummary($model, ['class' => ['text-danger']]); |
||||
44 | * // OR, to show only `id` errors: |
||||
45 | * echo yii\helpers\Html::error($model, 'id', ['class' => ['text-danger']]); |
||||
46 | * ~~~ |
||||
47 | * |
||||
48 | * |
||||
49 | * @author Sidney Lins <[email protected]> |
||||
50 | */ |
||||
51 | class AtLeastValidator extends Validator |
||||
52 | { |
||||
53 | /** |
||||
54 | * @var integer the minimun required quantity of attributes that must to be filled. |
||||
55 | * Defaults to 1. |
||||
56 | */ |
||||
57 | public $min = 1; |
||||
58 | |||||
59 | /** |
||||
60 | * @var string|array the list of attributes that should receive the error message. Required. |
||||
61 | */ |
||||
62 | public $in; |
||||
63 | |||||
64 | /** |
||||
65 | * @inheritdoc |
||||
66 | */ |
||||
67 | public $skipOnEmpty = false; |
||||
68 | |||||
69 | /** |
||||
70 | * @inheritdoc |
||||
71 | */ |
||||
72 | public $skipOnError = false; |
||||
73 | |||||
74 | /** |
||||
75 | * @inheritdoc |
||||
76 | */ |
||||
77 | public function init() |
||||
78 | { |
||||
79 | parent::init(); |
||||
80 | if ($this->in === null) { |
||||
81 | throw new InvalidConfigException('The `in` parameter is required.'); |
||||
82 | } elseif (! is_array($this->in) && count(preg_split('/\s*,\s*/', $this->in, -1, PREG_SPLIT_NO_EMPTY)) <= 1) { |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
83 | throw new InvalidConfigException('The `in` parameter must have at least 2 attributes.'); |
||||
84 | } |
||||
85 | if (!isset(Yii::$app->get('i18n')->translations['message*'])) { |
||||
86 | Yii::$app->get('i18n')->translations['message*'] = [ |
||||
87 | 'class' => PhpMessageSource::className(), |
||||
0 ignored issues
–
show
The function
yii\base\BaseObject::className() has been deprecated: since 2.0.14. On PHP >=5.5, use `::class` instead.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
88 | 'basePath' => __DIR__ . '/messages', |
||||
89 | 'sourceLanguage' => 'en-US' |
||||
90 | ]; |
||||
91 | } |
||||
92 | if ($this->message === null) { |
||||
93 | $this->message = Yii::t('messages', 'You must fill at least {min} of the attributes {attributes}.'); |
||||
94 | } |
||||
95 | } |
||||
96 | |||||
97 | /** |
||||
98 | * @inheritdoc |
||||
99 | */ |
||||
100 | public function validateAttribute($model, $attribute) |
||||
101 | { |
||||
102 | $attributes = is_array($this->in) ? $this->in : preg_split('/\s*,\s*/', $this->in, -1, PREG_SPLIT_NO_EMPTY); |
||||
103 | $chosen = 0; |
||||
104 | |||||
105 | foreach ($attributes as $attributeName) { |
||||
106 | $value = $model->$attributeName; |
||||
107 | $attributesListLabels[] = '"' . $model->getAttributeLabel($attributeName). '"'; |
||||
108 | $chosen += !empty($value) ? 1 : 0; |
||||
109 | } |
||||
110 | |||||
111 | if (!$chosen || $chosen < $this->min) { |
||||
112 | $attributesList = implode(', ', $attributesListLabels); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
113 | $message = strtr($this->message, [ |
||||
114 | '{min}' => $this->min, |
||||
115 | '{attributes}' => $attributesList, |
||||
116 | ]); |
||||
117 | $model->addError($attribute, $message); |
||||
118 | } |
||||
119 | } |
||||
120 | |||||
121 | /** |
||||
122 | * @inheritdoc |
||||
123 | * @since: 1.1 |
||||
124 | */ |
||||
125 | public function clientValidateAttribute($model, $attribute, $view) |
||||
126 | { |
||||
127 | $attributes = is_array($this->in) ? $this->in : preg_split('/\s*,\s*/', $this->in, -1, PREG_SPLIT_NO_EMPTY); |
||||
128 | $attributes = array_map('strtolower',$attributes); // yii lowercases attributes |
||||
0 ignored issues
–
show
It seems like
$attributes can also be of type false ; however, parameter $arr1 of array_map() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
129 | $attributesJson = json_encode($attributes); |
||||
130 | |||||
131 | $attributesLabels = []; |
||||
132 | foreach ($attributes as $attr) { |
||||
133 | $attributesLabels[] = '"' . $model->getAttributeLabel($attr) . '"'; |
||||
134 | } |
||||
135 | $message = strtr($this->message, [ |
||||
136 | '{min}' => $this->min, |
||||
137 | '{attributes}' => implode(Yii::t('messages', ' or '), $attributesLabels), |
||||
138 | ]); |
||||
139 | |||||
140 | $form = $model->formName(); |
||||
141 | |||||
142 | return <<<JS |
||||
143 | function atLeastValidator() { |
||||
144 | var atributes = $attributesJson; |
||||
145 | var formName = '$form'; |
||||
146 | var chosen = 0; |
||||
147 | $.each(atributes, function(key, attr){ |
||||
148 | var obj = $('#' + formName.toLowerCase() + '-' + attr); |
||||
149 | if(obj.length == 0){ |
||||
150 | obj = $("[name=\""+formName + '[' + attr + ']'+"\"]"); |
||||
151 | } |
||||
152 | |||||
153 | var val = obj.val(); |
||||
154 | chosen += val ? 1 : 0; |
||||
155 | }); |
||||
156 | if (!chosen || chosen < $this->min) { |
||||
157 | messages.push('$message'); |
||||
158 | } else { |
||||
159 | $.each(atributes, function(key, attr){ |
||||
160 | var attrId = formName.toLowerCase() + '-' + attr; |
||||
161 | if($('#' + attrId).length == 0){ |
||||
162 | attrId = $("[name=\""+formName + '[' + attr + ']'+"\"]").attr('id'); |
||||
163 | } |
||||
164 | |||||
165 | \$form.yiiActiveForm('updateAttribute', attrId, ''); |
||||
166 | }); |
||||
167 | } |
||||
168 | } |
||||
169 | atLeastValidator(); |
||||
170 | JS; |
||||
171 | } |
||||
172 | } |
||||
173 | |||||
174 |