ElementMetric   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 312
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 0
Metric Value
wmc 26
lcom 2
cbo 6
dl 0
loc 312
rs 10
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A find() 0 14 1
A resetScore() 0 5 1
A beforeSave() 0 7 1
A instantiate() 0 5 1
A rules() 0 52 1
A displayName() 0 9 1
A getWeight() 0 4 1
A getVersion() 0 4 1
A getScore() 0 8 2
calculateScore() 0 1 ?
A init() 0 13 4
A populateRecord() 0 8 1
A getDateCalculated() 0 17 4
A setDateCalculated() 0 10 3
A defaultDateCalculated() 0 4 1
A getSettingsValue() 0 8 1
A setSettingsValue() 0 10 1
A toConfig() 0 4 1
1
<?php
2
3
/**
4
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://flipboxfactory.com/software/scorecard/license
6
 * @link       https://www.flipboxfactory.com/software/scorecard/
7
 */
8
9
namespace flipbox\craft\scorecard\records;
10
11
use Craft;
12
use craft\helpers\DateTimeHelper;
13
use craft\helpers\Db;
14
use DateTime;
15
use flipbox\craft\ember\records\ActiveRecordWithId;
16
use flipbox\craft\ember\records\ElementAttributeTrait;
17
use flipbox\craft\scorecard\queries\ElementMetricQuery;
18
use flipbox\craft\scorecard\helpers\MetricHelper;
19
use flipbox\craft\scorecard\metrics\SavableMetricInterface;
20
use flipbox\craft\scorecard\validators\ElementMetricValidator;
21
22
/**
23
 * @author Flipbox Factory <[email protected]>
24
 * @since 1.0.0
25
 *
26
 * @property int $parentId
27
 * @property string $class
28
 * @property float $score
29
 * @property float $weight
30
 * @property string $version
31
 * @property array|null $settings
32
 * @property string $dateCalculated
33
 */
34
abstract class ElementMetric extends ActiveRecordWithId implements SavableMetricInterface
35
{
36
    use ElementAttributeTrait;
37
38
    /**
39
     * The default score weight
40
     */
41
    const WEIGHT = 1;
42
43
    /**
44
     * The default metric version
45
     */
46
    const VERSION = '1.0';
47
48
    /**
49
     * The table alias
50
     */
51
    const TABLE_ALIAS = 'scorecard_element_metrics';
52
53
    /**
54
     * The Active Query class
55
     */
56
    const ACTIVE_QUERY_CLASS = ElementMetricQuery::class;
57
58
    /**
59
     * @inheritdoc
60
     */
61
    protected $getterPriorityAttributes = ['elementId', 'score', 'dateCalculated'];
62
63
    /**
64
     * @inheritdoc
65
     */
66
    protected $setterPriorityAttributes = ['dateCalculated'];
67
68
    /**
69
     * @return float
70
     */
71
    abstract protected function calculateScore(): float;
72
73
    /**
74
     * @inheritdoc
75
     */
76
    public function init()
77
    {
78
        parent::init();
79
80
        // Always this class
81
        $this->class = static::class;
82
83
        // Defaults
84
        if ($this->getIsNewRecord()) {
85
            $this->weight = $this->weight ?: static::WEIGHT;
86
            $this->version = $this->version ?: static::VERSION;
87
        }
88
    }
89
90
    /**
91
     * @inheritdoc
92
     */
93
    public static function populateRecord($record, $row)
94
    {
95
        parent::populateRecord($record, $row);
96
97
        $record->version = static::VERSION;
98
        $record->weight = static::WEIGHT;
99
        $record->class = static::class;
100
    }
101
102
    /**
103
     * @inheritdoc
104
     * @throws \yii\base\InvalidConfigException
105
     * @return ElementMetricQuery
106
     */
107
    public static function find()
108
    {
109
        /** @noinspection PhpUnhandledExceptionInspection */
110
        /** @noinspection PhpIncompatibleReturnTypeInspection */
111
        return Craft::createObject(
112
            static::ACTIVE_QUERY_CLASS,
113
            [
114
                get_called_class(),
115
                [
116
                    'class' => static::class
117
                ]
118
            ]
119
        );
120
    }
121
122
    /**
123
     * @inheritdoc
124
     */
125
    public function resetScore()
126
    {
127
        $this->setAttribute('score', null);
128
        return $this;
129
    }
130
131
    /**
132
     * @inheritdoc
133
     */
134
    public function beforeSave($insert)
135
    {
136
        // Make sure score is calculated
137
        $this->getScore();
138
139
        return parent::beforeSave($insert);
140
    }
141
142
    /**
143
     * @inheritdoc
144
     */
145
    public static function instantiate($row)
146
    {
147
        $class = $row['class'] ?? static::class;
148
        return new $class;
149
    }
150
151
    /**
152
     * @inheritdoc
153
     */
154
    public function rules()
155
    {
156
        return array_merge(
157
            parent::rules(),
158
            $this->elementRules(),
159
            [
160
                [
161
                    [
162
                        'class'
163
                    ],
164
                    ElementMetricValidator::class
165
                ],
166
                [
167
                    [
168
                        'parentId',
169
                    ],
170
                    'number',
171
                    'integerOnly' => true
172
                ],
173
                [
174
                    [
175
                        'score',
176
                        'weight',
177
                    ],
178
                    'number'
179
                ],
180
                [
181
                    [
182
                        'elementId',
183
                        'class',
184
                        'weight',
185
                        'version',
186
                    ],
187
                    'required'
188
                ],
189
                [
190
                    [
191
                        'class',
192
                        'settings',
193
                        'score',
194
                        'weight',
195
                        'version',
196
                        'dateCalculated'
197
                    ],
198
                    'safe',
199
                    'on' => [
200
                        self::SCENARIO_DEFAULT
201
                    ]
202
                ]
203
            ]
204
        );
205
    }
206
207
208
    /*******************************************
209
     * METRIC INTERFACE
210
     *******************************************/
211
212
    /**
213
     * @inheritdoc
214
     * @throws \ReflectionException
215
     */
216
    public static function displayName(): string
217
    {
218
        return preg_replace(
219
            '/(?<!^)([A-Z])/',
220
            ' $0',
221
            (new \ReflectionClass(static::class))
222
                ->getShortName()
223
        );
224
    }
225
226
    /**
227
     * @inheritdoc
228
     */
229
    public function getWeight(): float
230
    {
231
        return (float)$this->getAttribute('weight');
232
    }
233
234
    /**
235
     * @inheritdoc
236
     */
237
    public function getVersion(): string
238
    {
239
        return (string)$this->getAttribute('version');
240
    }
241
242
    /**
243
     * @inheritdoc
244
     */
245
    public function getScore(): float
246
    {
247
        if ($this->getAttribute('score') === null) {
248
            $this->setAttribute('score', $this->calculateScore() * $this->getWeight());
249
        }
250
251
        return (float)$this->getAttribute('score');
252
    }
253
254
    /**
255
     * Returns an immutable calculated date.
256
     *
257
     * @return DateTime
258
     */
259
    public function getDateCalculated(): DateTime
260
    {
261
        if (null === ($dateCalculated = $this->getAttribute('dateCalculated'))) {
262
            $dateCalculated = $this->defaultDateCalculated();
263
            $this->setAttribute('dateCalculated', $dateCalculated);
264
        }
265
266
        if (!$dateCalculated instanceof DateTime) {
267
            if (is_array($dateCalculated)) {
268
                $dateCalculated = $dateCalculated['date'] ?? $dateCalculated;
269
            }
270
271
            $dateCalculated = DateTimeHelper::toDateTime($dateCalculated);
272
        }
273
274
        return $dateCalculated;
275
    }
276
277
    /**
278
     * @return DateTime|string
279
     */
280
    public function setDateCalculated($date = null): self
281
    {
282
        if ($date instanceof \DateTime || DateTimeHelper::isIso8601($date)) {
283
            $date = Db::prepareDateForDb($date);
284
        }
285
286
        $this->setAttribute('dateCalculated', $date);
287
288
        return $this;
289
    }
290
291
    /**
292
     * @return string
293
     */
294
    protected function defaultDateCalculated(): string
295
    {
296
        return DateTimeHelper::currentUTCDateTime()->format('Y-m-d');
297
    }
298
299
300
    /*******************************************
301
     * SETTINGS
302
     *******************************************/
303
304
    /**
305
     * @param string $attribute
306
     * @return mixed
307
     */
308
    public function getSettingsValue(string $attribute)
309
    {
310
        $settings = MetricHelper::resolveSettings(
311
            $this->getAttribute('settings')
312
        );
313
314
        return $settings[$attribute] ?? null;
315
    }
316
317
    /**
318
     * @param string $attribute
319
     * @param $value
320
     * @return $this
321
     */
322
    public function setSettingsValue(string $attribute, $value)
323
    {
324
        $settings = MetricHelper::resolveSettings(
325
            $this->getAttribute('settings')
326
        );
327
        $settings[$attribute] = $value;
328
        $this->setAttribute('settings', $settings);
329
330
        return $this;
331
    }
332
333
334
    /*******************************************
335
     * CONFIGURATION
336
     *******************************************/
337
338
    /**
339
     * @return array
340
     */
341
    public function toConfig(): array
342
    {
343
        return $this->toArray();
344
    }
345
}
346