Passed
Push — develop ( 8f4c81...0437e2 )
by Benjamin
05:36
created

Metadata   F

Complexity

Total Complexity 64

Size/Duplication

Total Lines 462
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 122
dl 0
loc 462
rs 3.28
c 0
b 0
f 0
wmc 64

22 Methods

Rating   Name   Duplication   Size   Complexity  
A getContinents() 0 3 1
A getSubContinents() 0 3 1
A getDimMet() 0 9 2
A getGoogleAnalyticsDataTypes() 0 13 4
A getColumns() 0 19 5
A getContinentCode() 0 9 3
A searchColumns() 0 12 4
A getSubContinentCode() 0 9 3
A getDimensions() 0 7 2
A getDataTypes() 0 10 1
A dimmetsFileExists() 0 9 2
A filterOptions() 0 7 2
A getMetrics() 0 7 2
A getColumnGroups() 0 13 4
A getSelectMetricOptions() 0 11 3
A getSelectOptions() 0 19 5
A _loadColumns() 0 18 4
A getSelectDimensionOptions() 0 11 3
A getFilteredOptions() 0 28 6
A _getColumnGroups() 0 11 5
A _getData() 0 5 1
A getDimmetsFilePath() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Metadata 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 Metadata, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @link      https://dukt.net/craft/analytics/
4
 * @copyright Copyright (c) 2018, Dukt
5
 * @license   https://dukt.net/craft/analytics/docs/license
6
 */
7
8
namespace dukt\analytics\services;
9
10
use Craft;
11
use yii\base\Component;
12
use craft\helpers\Json;
13
use dukt\analytics\models\Column;
14
use dukt\analytics\Plugin as Analytics;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, dukt\analytics\services\Analytics. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
15
16
class Metadata extends Component
17
{
18
    // Properties
19
    // =========================================================================
20
21
    /**
22
     * @var array|null
23
     */
24
    private $groups;
25
26
    /**
27
     * @var array|null
28
     */
29
    private $dimensions;
30
31
    /**
32
     * @var array|null
33
     */
34
    private $metrics;
35
36
    /**
37
     * @var array|null
38
     */
39
    private $columns;
40
41
    /**
42
     * @var array|null
43
     */
44
    private $selectDimensionOptions;
45
46
    /**
47
     * @var array|null
48
     */
49
    private $selectMetricOptions;
50
51
    // Public Methods
52
    // =========================================================================
53
54
    /**
55
     * Checks whether the dimensions & metrics file exists
56
     *
57
     * @return bool
58
     */
59
    public function dimmetsFileExists(): bool
60
    {
61
        $path = Analytics::$plugin->metadata->getDimmetsFilePath();
62
63
        if (file_exists($path)) {
64
            return true;
65
        }
66
67
        return false;
68
    }
69
70
    /**
71
     * Returns available data types for Google Analytics
72
     *
73
     * @param mixed
74
     *
75
     * @return array
76
     */
77
    public function getGoogleAnalyticsDataTypes(): array
78
    {
79
        $columns = $this->getColumns();
80
81
        $dataTypes = [];
82
83
        foreach ($columns as $column) {
84
            if (!isset($dataTypes[$column->dataType]) && !empty($column->dataType)) {
85
                $dataTypes[$column->dataType] = $column->dataType;
86
            }
87
        }
88
89
        return $dataTypes;
90
    }
91
92
    /**
93
     * Returns available data types
94
     *
95
     * @param mixed
96
     *
97
     * @return array
98
     */
99
    public function getDataTypes(): array
100
    {
101
        return [
102
            'string',
103
            'integer',
104
            'percent',
105
            'time',
106
            'currency',
107
            'float',
108
            'date'
109
        ];
110
    }
111
112
    /**
113
     * Get continents.
114
     *
115
     * @return array
116
     */
117
    public function getContinents(): array
118
    {
119
        return $this->_getData('continents');
120
    }
121
122
    /**
123
     * Get subcontinents.
124
     *
125
     * @return array
126
     */
127
    public function getSubContinents(): array
128
    {
129
        return $this->_getData('subContinents');
130
    }
131
132
    /**
133
     * Get Continent Code
134
     *
135
     * @param string $label
136
     *
137
     * @return mixed
138
     */
139
    public function getContinentCode($label)
140
    {
141
        foreach ($this->_getData('continents') as $continent) {
142
            if ($continent['label'] === $label) {
143
                return $continent['code'];
144
            }
145
        }
146
147
        return null;
148
    }
149
150
    /**
151
     * Get Sub-Continent Code
152
     *
153
     * @param string $label
154
     *
155
     * @return mixed
156
     */
157
    public function getSubContinentCode($label)
158
    {
159
        foreach ($this->_getData('subContinents') as $subContinent) {
160
            if ($subContinent['label'] === $label) {
161
                return $subContinent['code'];
162
            }
163
        }
164
165
        return null;
166
    }
167
168
    /**
169
     * Get a dimension or a metric label from its id
170
     *
171
     * @param string $id
172
     *
173
     * @return mixed
174
     */
175
    public function getDimMet($id)
176
    {
177
        $columns = $this->getColumns();
178
179
        if (isset($columns[$id])) {
180
            return $columns[$id]->uiName;
181
        }
182
183
        return null;
184
    }
185
186
    /**
187
     * Returns columns based on a search string `$q`
188
     *
189
     * @param string $q
190
     *
191
     * @return array
192
     */
193
    public function searchColumns($q): array
194
    {
195
        $columns = $this->getColumns();
196
        $results = [];
197
198
        foreach ($columns as $column) {
199
            if (stripos($column->id, $q) !== false || stripos($column->uiName, $q) !== false) {
200
                $results[] = $column;
201
            }
202
        }
203
204
        return $results;
205
    }
206
207
    /**
208
     * Returns columns
209
     *
210
     * @param null $type
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $type is correct as it would always require null to be passed?
Loading history...
211
     *
212
     * @return array
213
     */
214
    public function getColumns($type = null): array
215
    {
216
        if (!$this->columns) {
217
            $this->columns = $this->_loadColumns();
218
        }
219
220
        if ($type) {
221
            $columns = [];
222
223
            foreach ($this->columns as $column) {
224
                if ($column->type === $type) {
225
                    $columns[] = $column;
226
                }
227
            }
228
229
            return $columns;
230
        }
231
232
        return $this->columns;
233
    }
234
235
    /**
236
     * Returns dimension columns
237
     *
238
     * @return array
239
     */
240
    public function getDimensions(): array
241
    {
242
        if (!$this->dimensions) {
243
            $this->dimensions = $this->getColumns('DIMENSION');
244
        }
245
246
        return $this->dimensions;
247
    }
248
249
    /**
250
     * Returns column groups
251
     *
252
     * @param string|null $type
253
     *
254
     * @return array
255
     */
256
    public function getColumnGroups(string $type = null): array
257
    {
258
        if ($type && isset($this->groups[$type])) {
259
            return $this->groups[$type];
260
        }
261
262
        $groups = $this->_getColumnGroups($type);
263
264
        if ($type) {
265
            $this->groups[$type] = $groups;
266
        }
267
268
        return $groups;
269
    }
270
271
    /**
272
     * Returns select dimension options
273
     *
274
     * @param array $filters
275
     *
276
     * @return array
277
     */
278
    public function getSelectDimensionOptions(array $filters = null): array
279
    {
280
        if (!$this->selectDimensionOptions) {
281
            $this->selectDimensionOptions = $this->getSelectOptions('DIMENSION');
282
        }
283
284
        if ($filters) {
285
            $this->selectDimensionOptions = $this->filterOptions($this->selectDimensionOptions, $filters);
286
        }
287
288
        return $this->selectDimensionOptions;
289
    }
290
291
    /**
292
     * Returns select metric options
293
     *
294
     * @param array $filters
295
     *
296
     * @return array
297
     */
298
    public function getSelectMetricOptions(array $filters = null): array
299
    {
300
        if (!$this->selectMetricOptions) {
301
            $this->selectMetricOptions = $this->getSelectOptions('METRIC');
302
        }
303
304
        if ($filters) {
305
            $this->selectMetricOptions = $this->filterOptions($this->selectMetricOptions, $filters);
306
        }
307
308
        return $this->selectMetricOptions;
309
    }
310
311
    /**
312
     * Returns select options
313
     *
314
     * @param null  $type
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $type is correct as it would always require null to be passed?
Loading history...
315
     * @param array $filters
316
     *
317
     * @return array
318
     */
319
    public function getSelectOptions($type = null, array $filters = null): array
320
    {
321
        $options = [];
322
323
        foreach ($this->getColumnGroups($type) as $group) {
324
            $options[]['optgroup'] = Craft::t('analytics', $group);
325
326
            foreach ($this->getColumns($type) as $column) {
327
                if ($column->group === $group) {
328
                    $options[$column->id] = Craft::t('analytics', $column->uiName);
329
                }
330
            }
331
        }
332
333
        if ($filters) {
334
            $options = $this->filterOptions($options, $filters);
335
        }
336
337
        return $options;
338
    }
339
340
    /**
341
     * Returns the metrics
342
     *
343
     * @return array
344
     */
345
    public function getMetrics(): array
346
    {
347
        if (!$this->metrics) {
348
            $this->metrics = $this->getColumns('METRIC');
349
        }
350
351
        return $this->metrics;
352
    }
353
354
    /**
355
     * Returns the file path of the dimensions-metrics.json file
356
     *
357
     * @return string|bool
358
     */
359
    public function getDimmetsFilePath()
360
    {
361
        return Craft::getAlias('@dukt/analytics/etc/data/dimensions-metrics.json');
362
    }
363
364
    // Private Methods
365
    // =========================================================================
366
367
    /**
368
     * Loads the columns from the dimensions-metrics.json file
369
     *
370
     * @return array
371
     */
372
    private function _loadColumns(): array
373
    {
374
        $cols = [];
375
        $path = Analytics::$plugin->metadata->getDimmetsFilePath();
376
        $contents = file_get_contents($path);
377
        $columnsResponse = Json::decode($contents);
378
379
        if ($columnsResponse) {
380
            foreach ($columnsResponse as $columnResponse) {
381
                $cols[$columnResponse['id']] = new Column($columnResponse);
382
383
                if ($columnResponse['id'] === 'ga:countryIsoCode') {
384
                    $cols[$columnResponse['id']]->uiName = 'Country';
385
                }
386
            }
387
        }
388
389
        return $cols;
390
    }
391
392
    /**
393
     * Get Data
394
     *
395
     * @param $name
396
     *
397
     * @return array
398
     * @internal param string $label
399
     *
400
     */
401
    private function _getData($name): array
402
    {
403
        $jsonData = file_get_contents(Craft::getAlias('@dukt/analytics/etc/data/'.$name.'.json'));
404
405
        return json_decode($jsonData, true);
406
    }
407
408
    /**
409
     * @param array $options
410
     * @param array $filters
411
     *
412
     * @return array
413
     */
414
    private function filterOptions(array $options, array $filters): array
415
    {
416
        if (\count($filters) === 0) {
417
            return $options;
418
        }
419
420
        return $this->getFilteredOptions($options, $filters);
421
    }
422
423
    /**
424
     * Get filtered options.
425
     *
426
     * @param array $options
427
     * @param array $filters
428
     *
429
     * @return array
430
     */
431
    private function getFilteredOptions(array $options, array $filters): array
432
    {
433
        $filteredOptions = [];
434
        $optgroup = null;
435
        $lastOptgroup = null;
436
437
        foreach ($options as $id => $option) {
438
            if (isset($option['optgroup'])) {
439
                $optgroup = null;
440
                $lastOptgroup = $option['optgroup'];
441
                continue;
442
            }
443
444
            foreach ($filters as $filter) {
445
                if ($id !== $filter) {
446
                    continue;
447
                }
448
449
                if (!$optgroup) {
450
                    $optgroup = $lastOptgroup;
451
                    $filteredOptions[]['optgroup'] = $optgroup;
452
                }
453
454
                $filteredOptions[$id] = $option;
455
            }
456
        }
457
458
        return $filteredOptions;
459
    }
460
461
    /**
462
     * Get column groups.
463
     *
464
     * @param string|null $type
465
     * @return array
466
     */
467
    private function _getColumnGroups(string $type = null): array
468
    {
469
        $groups = [];
470
471
        foreach ($this->getColumns() as $column) {
472
            if (!$type || ($type && $column->type === $type)) {
473
                $groups[$column->group] = $column->group;
474
            }
475
        }
476
477
        return $groups;
478
    }
479
}
480