ActiveRecord   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 329
Duplicated Lines 0 %

Test Coverage

Coverage 15.74%

Importance

Changes 13
Bugs 2 Features 3
Metric Value
eloc 86
c 13
b 2
f 3
dl 0
loc 329
rs 8.64
ccs 17
cts 108
cp 0.1574
wmc 47

24 Methods

Rating   Name   Duplication   Size   Complexity  
A update() 0 7 3
A optimisticLock() 0 3 1
A scenarioActions() 0 3 1
A perform() 0 3 1
A tableName() 0 3 1
A insert() 0 24 5
A find() 0 5 1
A query() 0 5 1
A unlinkAll() 0 3 1
A getScenarioAction() 0 12 4
A getDb() 0 3 1
A getIsNewRecord() 0 3 1
A modelName() 0 3 1
A isScenarioDefault() 0 3 1
A instantiate() 0 3 1
A hasOne() 0 3 1
A primaryKey() 0 3 1
A batchPerform() 0 5 1
A attributes() 0 17 5
A hasMany() 0 3 1
A updateInternal() 0 24 5
A delete() 0 13 3
A getRelation() 0 3 1
A batchQuery() 0 18 5

How to fix   Complexity   

Complex Class

Complex classes like ActiveRecord often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ActiveRecord, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * ActiveRecord for API
4
 *
5
 * @link      https://github.com/hiqdev/yii2-hiart
6
 * @package   yii2-hiart
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2015-2019, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\hiart;
12
13
use yii\base\InvalidConfigException;
14
use yii\base\NotSupportedException;
15
use yii\db\ActiveQueryInterface;
16
use yii\db\BaseActiveRecord;
17
use yii\helpers\Inflector;
18
use yii\helpers\StringHelper;
19
20
class ActiveRecord extends BaseActiveRecord
21
{
22
    /**
23
     * Returns the database connection used by this AR class.
24
     * By default, the "hiart" application component is used as the database connection.
25
     * You may override this method if you want to use a different database connection.
26
     *
27
     * @return AbstractConnection the database connection used by this AR class
28
     */
29
    public static function getDb()
30
    {
31
        return AbstractConnection::getDb();
32
    }
33
34
    /**
35
     * {@inheritdoc}
36
     * @return ActiveQuery the newly created [[ActiveQuery]] instance
37
     */
38 2
    public static function find()
39
    {
40 2
        $class = static::getDb()->activeQueryClass;
41
42 2
        return new $class(get_called_class());
43
    }
44
45
    public function isScenarioDefault()
46
    {
47
        return $this->scenario === static::SCENARIO_DEFAULT;
48
    }
49
50
    /**
51
     * This method defines the attribute that uniquely identifies a record.
52
     *
53
     * The primaryKey for HiArt objects is the `id` field by default. This field is not part of the
54
     * ActiveRecord attributes so you should never add `_id` to the list of [[attributes()|attributes]].
55
     *
56
     * You may override this method to define the primary key name.
57
     *
58
     * Note that HiArt only supports _one_ attribute to be the primary key. However to match the signature
59
     * of the [[\yii\db\ActiveRecordInterface|ActiveRecordInterface]] this methods returns an array instead of a
60
     * single string.
61
     *
62
     * @return string[] array of primary key attributes. Only the first element of the array will be used.
63
     */
64
    public static function primaryKey()
65
    {
66
        return ['id'];
67
    }
68
69
    /**
70
     * Returns the list of attribute names.
71
     * By default, this method returns all attributes mentioned in rules.
72
     * You may override this method to change the default behavior.
73
     * @return string[] list of attribute names
74
     */
75 2
    public function attributes()
76
    {
77 2
        $attributes = [];
78 2
        foreach ($this->rules() as $rule) {
79 2
            $names = reset($rule);
80 2
            if (is_string($names)) {
81 2
                $names = [$names];
82
            }
83 2
            foreach ($names as $name) {
84 2
                if (substr_compare($name, '!', 0, 1) === 0) {
85
                    $name = mb_substr($name, 1);
86
                }
87 2
                $attributes[$name] = $name;
88
            }
89
        }
90
91 2
        return array_values($attributes);
92
    }
93
94
    /**
95
     * Creates an active record instance.
96
     *
97
     * This method is called together with [[populateRecord()]] by [[ActiveQuery]].
98
     * It is not meant to be used for creating new records directly.
99
     *
100
     * You may override this method if the instance being created
101
     * depends on the row data to be populated into the record.
102
     * For example, by creating a record based on the value of a column,
103
     * you may implement the so-called single-table inheritance mapping.
104
     *
105
     * @return static the newly created active record
106
     */
107 2
    public static function instantiate($row)
108
    {
109 2
        return new static();
110
    }
111
112
    /**
113
     * @return string the name of the entity of this record
114
     */
115 2
    public static function tableName()
116
    {
117 2
        return Inflector::camel2id(StringHelper::basename(get_called_class()), '-');
118
    }
119
120
    /**
121
     * Declares the name of the model associated with this class.
122
     * By default this method returns the class name by calling [[Inflector::camel2id()]].
123
     *
124
     * @return string the module name
125
     */
126
    public static function modelName()
127
    {
128
        return Inflector::camel2id(StringHelper::basename(get_called_class()));
129
    }
130
131
    public function insert($runValidation = true, $attributes = null, $options = [])
132
    {
133
        if ($runValidation && !$this->validate($attributes)) {
134
            return false;
135
        }
136
137
        if (!$this->beforeSave(true)) {
138
            return false;
139
        }
140
141
        $values = $this->getDirtyAttributes($attributes);
142
        $data   = array_merge($values, $options, ['id' => $this->getOldPrimaryKey()]);
143
        $result = $this->query('insert', $data);
144
145
        $pk        = static::primaryKey()[0];
146
        $this->$pk = $result['id'];
147
        if ($pk !== 'id') {
148
            $values[$pk] = $result['id'];
149
        }
150
        $changedAttributes = array_fill_keys(array_keys($values), null);
151
        $this->setOldAttributes($values);
152
        $this->afterSave(true, $changedAttributes);
153
154
        return true;
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160
    public function delete($options = [])
161
    {
162
        if (!$this->beforeDelete()) {
163
            return false;
164
        }
165
166
        $data   = array_merge($options, ['id' => $this->getOldPrimaryKey()]);
167
        $result = $this->query('delete', $data);
168
169
        $this->setOldAttributes(null);
170
        $this->afterDelete();
171
172
        return $result === false ? false : true;
0 ignored issues
show
introduced by
The condition $result === false is always false.
Loading history...
173
    }
174
175
    public function update($runValidation = true, $attributeNames = null, $options = [])
176
    {
177
        if ($runValidation && !$this->validate($attributeNames)) {
178
            return false;
179
        }
180
181
        return $this->updateInternal($attributeNames, $options);
182
    }
183
184
    protected function updateInternal($attributes = null, $options = [])
185
    {
186
        if (!$this->beforeSave(false)) {
187
            return false;
188
        }
189
190
        $values = $this->getAttributes($attributes);
191
        if (empty($values)) {
192
            $this->afterSave(false, $values);
193
194
            return 0;
195
        }
196
197
        $result = $this->query('update', $values, $options);
198
199
        $changedAttributes = [];
200
        foreach ($values as $name => $value) {
201
            $changedAttributes[$name] = $this->getOldAttribute($name);
202
            $this->setOldAttribute($name, $value);
203
        }
204
205
        $this->afterSave(false, $changedAttributes);
206
207
        return $result === false ? false : true;
0 ignored issues
show
introduced by
The condition $result === false is always false.
Loading history...
208
    }
209
210
    /**
211
     * Perform batch query.
212
     * Attention: takes bulk data and returns bulk result.
213
     * @param string $defaultScenario
214
     * @param array $data bulk data
215
     * @param array $options
216
     * @return array bulk results
217
     */
218
    public function batchQuery($defaultScenario, $data = [], array $options = [])
219
    {
220
        $batch = isset($options['batch']) ? (bool) $options['batch'] : true;
221
        $options['batch'] = $batch;
222
223
        if (!$batch) {
224
            $val = reset($data);
225
            $key = key($data);
226
            $data = $val;
227
        }
228
229
        $result = $this->query($defaultScenario, $data, $options);
230
231
        if (!$batch) {
232
            $result = [$key => $result];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $key does not seem to be defined for all execution paths leading up to this point.
Loading history...
233
        }
234
235
        return is_array($result) ? $result : [];
0 ignored issues
show
introduced by
The condition is_array($result) is always true.
Loading history...
236
    }
237
238
    /**
239
     * Perform query.
240
     * @param string $defaultScenario
241
     * @param array $data data
242
     * @param array $options
243
     * @return array result
244
     */
245
    public function query($defaultScenario, $data = [], array $options = [])
246
    {
247
        $action = $this->getScenarioAction($defaultScenario);
248
249
        return static::perform($action, $data, $options);
250
    }
251
252
    public static function batchPerform($action, $data = [], array $options = [])
253
    {
254
        $options['batch'] = true;
255
256
        return static::perform($action, $data, $options);
257
    }
258
259
    public static function perform($action, $data = [], array $options = [])
260
    {
261
        return static::getDb()->createCommand()->perform($action, static::tableName(), $data, $options)->getData();
262
    }
263
264
    /**
265
     * Converts scenario name to action.
266
     * @param string $default default action name
267
     * @throws InvalidConfigException
268
     * @throws NotSupportedException
269
     * @return string
270
     */
271
    public function getScenarioAction($default = '')
272
    {
273
        if ($this->isScenarioDefault()) {
274
            if (empty($default)) {
275
                throw new InvalidConfigException('Scenario not specified');
276
            }
277
278
            return $default;
279
        } else {
280
            $actions = static::scenarioActions();
0 ignored issues
show
Bug Best Practice introduced by
The method hiqdev\hiart\ActiveRecord::scenarioActions() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

280
            /** @scrutinizer ignore-call */ 
281
            $actions = static::scenarioActions();
Loading history...
281
282
            return isset($actions[$this->scenario]) ? $actions[$this->scenario] : $this->scenario;
283
        }
284
    }
285
286
    /**
287
     * Provides a correspondance array: scenario -> API action.
288
     * E.g. ['update-name' => 'set-name'].
289
     * @return array
290
     */
291
    public function scenarioActions()
292
    {
293
        return [];
294
    }
295
296
    /**
297
     * @return bool
298
     */
299
    public function getIsNewRecord()
300
    {
301
        return !$this->getPrimaryKey();
302
    }
303
304
    /**
305
     * This method has no effect in HiArt ActiveRecord.
306
     */
307
    public function optimisticLock()
308
    {
309
        return null;
310
    }
311
312
    /**
313
     * Destroys the relationship in current model.
314
     *
315
     * This method is not supported by HiArt.
316
     */
317
    public function unlinkAll($name, $delete = false)
318
    {
319
        throw new NotSupportedException('unlinkAll() is not supported by HiArt, use unlink() instead.');
320
    }
321
322
    /**
323
     * {@inheritdoc}
324
     *
325
     * @return ActiveQueryInterface|ActiveQuery the relational query object. If the relation does not exist
326
     *                                          and `$throwException` is false, null will be returned.
327
     */
328
    public function getRelation($name, $throwException = true)
329
    {
330
        return parent::getRelation($name, $throwException);
331
    }
332
333
    /**
334
     * {@inheritdoc}
335
     * @return ActiveQuery the relational query object
336
     */
337
    public function hasOne($class, $link)
338
    {
339
        return parent::hasOne($class, $link);
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::hasOne($class, $link) returns the type yii\db\ActiveQuery which is incompatible with the documented return type hiqdev\hiart\ActiveQuery.
Loading history...
340
    }
341
342
    /**
343
     * {@inheritdoc}
344
     * @return ActiveQuery the relational query object
345
     */
346
    public function hasMany($class, $link)
347
    {
348
        return parent::hasMany($class, $link);
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::hasMany($class, $link) returns the type yii\db\ActiveQuery which is incompatible with the documented return type hiqdev\hiart\ActiveQuery.
Loading history...
349
    }
350
}
351