Completed
Push — master ( b933d6...aeeb6c )
by Alexander
117:59 queued 114:20
created

DynamicModel::hasAttribute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\base;
9
10
use yii\validators\Validator;
11
12
/**
13
 * DynamicModel is a model class primarily used to support ad hoc data validation.
14
 *
15
 * The typical usage of DynamicModel is as follows,
16
 *
17
 * ```php
18
 * public function actionSearch($name, $email)
19
 * {
20
 *     $model = DynamicModel::validateData(compact('name', 'email'), [
21
 *         [['name', 'email'], 'string', 'max' => 128],
22
 *         ['email', 'email'],
23
 *     ]);
24
 *     if ($model->hasErrors()) {
25
 *         // validation fails
26
 *     } else {
27
 *         // validation succeeds
28
 *     }
29
 * }
30
 * ```
31
 *
32
 * The above example shows how to validate `$name` and `$email` with the help of DynamicModel.
33
 * The [[validateData()]] method creates an instance of DynamicModel, defines the attributes
34
 * using the given data (`name` and `email` in this example), and then calls [[Model::validate()]].
35
 *
36
 * You can check the validation result by [[hasErrors()]], like you do with a normal model.
37
 * You may also access the dynamic attributes defined through the model instance, e.g.,
38
 * `$model->name` and `$model->email`.
39
 *
40
 * Alternatively, you may use the following more "classic" syntax to perform ad-hoc data validation:
41
 *
42
 * ```php
43
 * $model = new DynamicModel(compact('name', 'email'));
44
 * $model->addRule(['name', 'email'], 'string', ['max' => 128])
45
 *     ->addRule('email', 'email')
46
 *     ->validate();
47
 * ```
48
 *
49
 * DynamicModel implements the above ad-hoc data validation feature by supporting the so-called
50
 * "dynamic attributes". It basically allows an attribute to be defined dynamically through its constructor
51
 * or [[defineAttribute()]].
52
 *
53
 * @author Qiang Xue <[email protected]>
54
 * @since 2.0
55
 */
56
class DynamicModel extends Model
57
{
58
    private $_attributes = [];
59
60
61
    /**
62
     * Constructors.
63
     * @param array $attributes the dynamic attributes (name-value pairs, or names) being defined
64
     * @param array $config the configuration array to be applied to this object.
65
     */
66 117
    public function __construct(array $attributes = [], $config = [])
67
    {
68 117
        foreach ($attributes as $name => $value) {
69 74
            if (is_int($name)) {
70 35
                $this->_attributes[$value] = null;
71
            } else {
72 74
                $this->_attributes[$name] = $value;
73
            }
74
        }
75 117
        parent::__construct($config);
76 117
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81 76
    public function __get($name)
82
    {
83 76
        if ($this->hasAttribute($name)) {
84 75
            return $this->_attributes[$name];
85
        }
86
87 2
        return parent::__get($name);
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93 48
    public function __set($name, $value)
94
    {
95 48
        if ($this->hasAttribute($name)) {
96 48
            $this->_attributes[$name] = $value;
97
        } else {
98
            parent::__set($name, $value);
99
        }
100 48
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    public function __isset($name)
106
    {
107
        if ($this->hasAttribute($name)) {
108
            return isset($this->_attributes[$name]);
109
        }
110
111
        return parent::__isset($name);
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117
    public function __unset($name)
118
    {
119
        if ($this->hasAttribute($name)) {
120
            unset($this->_attributes[$name]);
121
        } else {
122
            parent::__unset($name);
123
        }
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129 1
    public function canGetProperty($name, $checkVars = true, $checkBehaviors = true)
130
    {
131 1
        return parent::canGetProperty($name, $checkVars, $checkBehaviors) || $this->hasAttribute($name);
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137 1
    public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
138
    {
139 1
        return parent::canSetProperty($name, $checkVars, $checkBehaviors) || $this->hasAttribute($name);
140
    }
141
142
    /**
143
     * Returns a value indicating whether the model has an attribute with the specified name.
144
     * @param string $name the name of the attribute
145
     * @return bool whether the model has an attribute with the specified name
146
     */
147 79
    public function hasAttribute($name)
148
    {
149 79
        return array_key_exists($name, $this->_attributes);
150
    }
151
152
    /**
153
     * Defines an attribute.
154
     * @param string $name the attribute name
155
     * @param mixed $value the attribute value
156
     */
157 41
    public function defineAttribute($name, $value = null)
158
    {
159 41
        $this->_attributes[$name] = $value;
160 41
    }
161
162
    /**
163
     * Undefines an attribute.
164
     * @param string $name the attribute name
165
     */
166
    public function undefineAttribute($name)
167
    {
168
        unset($this->_attributes[$name]);
169
    }
170
171
    /**
172
     * Adds a validation rule to this model.
173
     * You can also directly manipulate [[validators]] to add or remove validation rules.
174
     * This method provides a shortcut.
175
     * @param string|array $attributes the attribute(s) to be validated by the rule
176
     * @param mixed $validator the validator for the rule.This can be a built-in validator name,
177
     * a method name of the model class, an anonymous function, or a validator class name.
178
     * @param array $options the options (name-value pairs) to be applied to the validator
179
     * @return $this the model itself
180
     */
181 46
    public function addRule($attributes, $validator, $options = [])
182
    {
183 46
        $validators = $this->getValidators();
184 46
        $validators->append(Validator::createValidator($validator, $this, (array)$attributes, $options));
185
186 46
        return $this;
187
    }
188
189
    /**
190
     * Validates the given data with the specified validation rules.
191
     * This method will create a DynamicModel instance, populate it with the data to be validated,
192
     * create the specified validation rules, and then validate the data using these rules.
193
     * @param array $data the data (name-value pairs) to be validated
194
     * @param array $rules the validation rules. Please refer to [[Model::rules()]] on the format of this parameter.
195
     * @return static the model instance that contains the data being validated
196
     * @throws InvalidConfigException if a validation rule is not specified correctly.
197
     */
198 1
    public static function validateData(array $data, $rules = [])
199
    {
200
        /* @var $model DynamicModel */
201 1
        $model = new static($data);
202 1
        if (!empty($rules)) {
203 1
            $validators = $model->getValidators();
204 1
            foreach ($rules as $rule) {
205 1
                if ($rule instanceof Validator) {
206
                    $validators->append($rule);
207 1
                } elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type
208 1
                    $validator = Validator::createValidator($rule[1], $model, (array)$rule[0], array_slice($rule, 2));
209 1
                    $validators->append($validator);
210
                } else {
211 1
                    throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');
212
                }
213
            }
214
        }
215
216 1
        $model->validate();
217
218 1
        return $model;
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     */
224 2
    public function attributes()
225
    {
226 2
        return array_keys($this->_attributes);
227
    }
228
}
229