Completed
Pull Request — 2.1 (#12704)
by Robert
08:59
created

DynamicModel   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Test Coverage

Coverage 68.42%

Importance

Changes 0
Metric Value
wmc 21
lcom 2
cbo 3
dl 0
loc 147
ccs 39
cts 57
cp 0.6842
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 3
A __get() 0 8 2
A __set() 0 8 2
A __isset() 0 8 2
A __unset() 0 8 2
A undefineAttribute() 0 4 1
A addRule() 0 7 1
B validateData() 0 22 6
A attributes() 0 4 1
A defineAttribute() 0 4 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 47
    public function __construct(array $attributes = [], $config = [])
67
    {
68 47
        foreach ($attributes as $name => $value) {
69 29
            if (is_int($name)) {
70 26
                $this->_attributes[$value] = null;
71 26
            } else {
72 3
                $this->_attributes[$name] = $value;
73
            }
74 47
        }
75 47
        parent::__construct($config);
76 47
    }
77
78
    /**
79
     * @inheritdoc
80
     */
81 32
    public function __get($name)
82
    {
83 32
        if (array_key_exists($name, $this->_attributes)) {
84 31
            return $this->_attributes[$name];
85
        } else {
86 2
            return parent::__get($name);
87
        }
88
    }
89
90
    /**
91
     * @inheritdoc
92
     */
93 19
    public function __set($name, $value)
94
    {
95 19
        if (array_key_exists($name, $this->_attributes)) {
96 19
            $this->_attributes[$name] = $value;
97 19
        } else {
98
            parent::__set($name, $value);
99
        }
100 19
    }
101
102
    /**
103
     * @inheritdoc
104
     */
105
    public function __isset($name)
106
    {
107
        if (array_key_exists($name, $this->_attributes)) {
108
            return isset($this->_attributes[$name]);
109
        } else {
110
            return parent::__isset($name);
111
        }
112
    }
113
114
    /**
115
     * @inheritdoc
116
     */
117
    public function __unset($name)
118
    {
119
        if (array_key_exists($name, $this->_attributes)) {
120
            unset($this->_attributes[$name]);
121
        } else {
122
            parent::__unset($name);
123
        }
124
    }
125
126
    /**
127
     * Defines an attribute.
128
     * @param string $name the attribute name
129
     * @param mixed $value the attribute value
130
     */
131 17
    public function defineAttribute($name, $value = null)
132
    {
133 17
        $this->_attributes[$name] = $value;
134 17
    }
135
136
    /**
137
     * Undefines an attribute.
138
     * @param string $name the attribute name
139
     */
140
    public function undefineAttribute($name)
141
    {
142
        unset($this->_attributes[$name]);
143
    }
144
145
    /**
146
     * Adds a validation rule to this model.
147
     * You can also directly manipulate [[validators]] to add or remove validation rules.
148
     * This method provides a shortcut.
149
     * @param string|array $attributes the attribute(s) to be validated by the rule
150
     * @param mixed $validator the validator for the rule.This can be a built-in validator name,
151
     * a method name of the model class, an anonymous function, or a validator class name.
152
     * @param array $options the options (name-value pairs) to be applied to the validator
153
     * @return $this the model itself
154
     */
155 8
    public function addRule($attributes, $validator, $options = [])
156
    {
157 8
        $validators = $this->getValidators();
158 8
        $validators->append(Validator::createValidator($validator, $this, (array) $attributes, $options));
159
160 8
        return $this;
161
    }
162
163
    /**
164
     * Validates the given data with the specified validation rules.
165
     * This method will create a DynamicModel instance, populate it with the data to be validated,
166
     * create the specified validation rules, and then validate the data using these rules.
167
     * @param array $data the data (name-value pairs) to be validated
168
     * @param array $rules the validation rules. Please refer to [[Model::rules()]] on the format of this parameter.
169
     * @return static the model instance that contains the data being validated
170
     * @throws InvalidConfigException if a validation rule is not specified correctly.
171
     */
172 1
    public static function validateData(array $data, $rules = [])
173
    {
174
        /* @var $model DynamicModel */
175 1
        $model = new static($data);
176 1
        if (!empty($rules)) {
177 1
            $validators = $model->getValidators();
178 1
            foreach ($rules as $rule) {
179 1
                if ($rule instanceof Validator) {
180
                    $validators->append($rule);
181 1
                } elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type
182 1
                    $validator = Validator::createValidator($rule[1], $model, (array) $rule[0], array_slice($rule, 2));
183 1
                    $validators->append($validator);
184 1
                } else {
185
                    throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');
186
                }
187 1
            }
188 1
        }
189
190 1
        $model->validate();
191
192 1
        return $model;
193
    }
194
195
    /**
196
     * @inheritdoc
197
     */
198
    public function attributes()
199
    {
200
        return array_keys($this->_attributes);
201
    }
202
}
203