Passed
Push — v4 ( f40c8e...89edfc )
by Benjamin
04:00
created

Metadata   F

Complexity

Total Complexity 63

Size/Duplication

Total Lines 439
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 439
rs 3.6585
c 0
b 0
f 0
wmc 63

21 Methods

Rating   Name   Duplication   Size   Complexity  
A getGoogleAnalyticsDataTypes() 0 13 4
A getContinents() 0 3 1
A getSubContinents() 0 3 1
A getDataTypes() 0 10 1
A dimmetsFileExists() 0 9 2
A getDimMet() 0 9 2
B getColumns() 0 19 5
A getContinentCode() 0 9 3
A searchColumns() 0 12 4
A getSubContinentCode() 0 9 3
A getDimensions() 0 7 2
B getColumnGroups() 0 19 8
A getSelectMetricOptions() 0 11 3
A getSelectDimensionOptions() 0 11 3
A filterOptions() 0 7 2
A getMetrics() 0 7 2
B getSelectOptions() 0 19 5
A _loadColumns() 0 18 4
B getFilteredOptions() 0 28 6
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
    public function getContinents()
113
    {
114
        return $this->_getData('continents');
115
    }
116
117
    public function getSubContinents()
118
    {
119
        return $this->_getData('subContinents');
120
    }
121
122
    /**
123
     * Get Continent Code
124
     *
125
     * @param string $label
126
     *
127
     * @return mixed
128
     */
129
    public function getContinentCode($label)
130
    {
131
        foreach ($this->_getData('continents') as $continent) {
132
            if ($continent['label'] === $label) {
133
                return $continent['code'];
134
            }
135
        }
136
137
        return null;
138
    }
139
140
    /**
141
     * Get Sub-Continent Code
142
     *
143
     * @param string $label
144
     *
145
     * @return mixed
146
     */
147
    public function getSubContinentCode($label)
148
    {
149
        foreach ($this->_getData('subContinents') as $subContinent) {
150
            if ($subContinent['label'] === $label) {
151
                return $subContinent['code'];
152
            }
153
        }
154
155
        return null;
156
    }
157
158
    /**
159
     * Get a dimension or a metric label from its id
160
     *
161
     * @param string $id
162
     *
163
     * @return mixed
164
     */
165
    public function getDimMet($id)
166
    {
167
        $columns = $this->getColumns();
168
169
        if (isset($columns[$id])) {
170
            return $columns[$id]->uiName;
171
        }
172
173
        return null;
174
    }
175
176
    /**
177
     * Returns columns based on a search string `$q`
178
     *
179
     * @param string $q
180
     *
181
     * @return array
182
     */
183
    public function searchColumns($q): array
184
    {
185
        $columns = $this->getColumns();
186
        $results = [];
187
188
        foreach ($columns as $column) {
189
            if (stripos($column->id, $q) !== false || stripos($column->uiName, $q) !== false) {
190
                $results[] = $column;
191
            }
192
        }
193
194
        return $results;
195
    }
196
197
    /**
198
     * Returns columns
199
     *
200
     * @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...
201
     *
202
     * @return array
203
     */
204
    public function getColumns($type = null): array
205
    {
206
        if (!$this->columns) {
207
            $this->columns = $this->_loadColumns();
208
        }
209
210
        if ($type) {
211
            $columns = [];
212
213
            foreach ($this->columns as $column) {
214
                if ($column->type === $type) {
215
                    $columns[] = $column;
216
                }
217
            }
218
219
            return $columns;
220
        }
221
222
        return $this->columns;
223
    }
224
225
    /**
226
     * Returns dimension columns
227
     *
228
     * @return array
229
     */
230
    public function getDimensions(): array
231
    {
232
        if (!$this->dimensions) {
233
            $this->dimensions = $this->getColumns('DIMENSION');
234
        }
235
236
        return $this->dimensions;
237
    }
238
239
    /**
240
     * Returns column groups
241
     *
242
     * @param string|null $type
243
     *
244
     * @return array
245
     */
246
    public function getColumnGroups(string $type = null): array
247
    {
248
        if ($type && isset($this->groups[$type])) {
249
            return $this->groups[$type];
250
        }
251
252
        $groups = [];
253
254
        foreach ($this->getColumns() as $column) {
255
            if (!$type || ($type && $column->type === $type)) {
256
                $groups[$column->group] = $column->group;
257
            }
258
        }
259
260
        if ($type) {
261
            $this->groups[$type] = $groups;
262
        }
263
264
        return $groups;
265
    }
266
267
    /**
268
     * Returns select dimension options
269
     *
270
     * @param array $filters
271
     *
272
     * @return array
273
     */
274
    public function getSelectDimensionOptions(array $filters = null): array
275
    {
276
        if (!$this->selectDimensionOptions) {
277
            $this->selectDimensionOptions = $this->getSelectOptions('DIMENSION');
278
        }
279
280
        if ($filters) {
281
            $this->selectDimensionOptions = $this->filterOptions($this->selectDimensionOptions, $filters);
282
        }
283
284
        return $this->selectDimensionOptions;
285
    }
286
287
    /**
288
     * Returns select metric options
289
     *
290
     * @param array $filters
291
     *
292
     * @return array
293
     */
294
    public function getSelectMetricOptions(array $filters = null): array
295
    {
296
        if (!$this->selectMetricOptions) {
297
            $this->selectMetricOptions = $this->getSelectOptions('METRIC');
298
        }
299
300
        if ($filters) {
301
            $this->selectMetricOptions = $this->filterOptions($this->selectMetricOptions, $filters);
302
        }
303
304
        return $this->selectMetricOptions;
305
    }
306
307
    /**
308
     * Returns select options
309
     *
310
     * @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...
311
     * @param array $filters
312
     *
313
     * @return array
314
     */
315
    public function getSelectOptions($type = null, array $filters = null): array
316
    {
317
        $options = [];
318
319
        foreach ($this->getColumnGroups($type) as $group) {
320
            $options[]['optgroup'] = Craft::t('analytics', $group);
321
322
            foreach ($this->getColumns($type) as $column) {
323
                if ($column->group === $group) {
324
                    $options[$column->id] = Craft::t('analytics', $column->uiName);
325
                }
326
            }
327
        }
328
329
        if ($filters) {
330
            $options = $this->filterOptions($options, $filters);
331
        }
332
333
        return $options;
334
    }
335
336
    /**
337
     * Returns the metrics
338
     *
339
     * @return array
340
     */
341
    public function getMetrics(): array
342
    {
343
        if (!$this->metrics) {
344
            $this->metrics = $this->getColumns('METRIC');
345
        }
346
347
        return $this->metrics;
348
    }
349
350
    /**
351
     * Returns the file path of the dimensions-metrics.json file
352
     *
353
     * @return string|bool
354
     */
355
    public function getDimmetsFilePath()
356
    {
357
        return Craft::getAlias('@dukt/analytics/etc/data/dimensions-metrics.json');
358
    }
359
360
    // Private Methods
361
    // =========================================================================
362
363
    /**
364
     * Loads the columns from the dimensions-metrics.json file
365
     *
366
     * @return array
367
     */
368
    private function _loadColumns(): array
369
    {
370
        $cols = [];
371
        $path = Analytics::$plugin->metadata->getDimmetsFilePath();
372
        $contents = file_get_contents($path);
373
        $columnsResponse = Json::decode($contents);
374
375
        if ($columnsResponse) {
376
            foreach ($columnsResponse as $columnResponse) {
377
                $cols[$columnResponse['id']] = new Column($columnResponse);
378
379
                if ($columnResponse['id'] === 'ga:countryIsoCode') {
380
                    $cols[$columnResponse['id']]->uiName = 'Country';
381
                }
382
            }
383
        }
384
385
        return $cols;
386
    }
387
388
    /**
389
     * Get Data
390
     *
391
     * @param $name
392
     *
393
     * @return array
394
     * @internal param string $label
395
     *
396
     */
397
    private function _getData($name): array
398
    {
399
        $jsonData = file_get_contents(Craft::getAlias('@dukt/analytics/etc/data/'.$name.'.json'));
400
401
        return json_decode($jsonData, true);
402
    }
403
404
    /**
405
     * @param array $options
406
     * @param array $filters
407
     *
408
     * @return array
409
     */
410
    private function filterOptions(array $options, array $filters): array
411
    {
412
        if (\count($filters) === 0) {
413
            return $options;
414
        }
415
416
        return $this->getFilteredOptions($options, $filters);
417
    }
418
419
    /**
420
     * Get filtered options.
421
     *
422
     * @param array $options
423
     * @param array $filters
424
     *
425
     * @return array
426
     */
427
    private function getFilteredOptions(array $options, array $filters): array
428
    {
429
        $filteredOptions = [];
430
        $optgroup = null;
431
        $lastOptgroup = null;
432
433
        foreach ($options as $id => $option) {
434
            if (isset($option['optgroup'])) {
435
                $optgroup = null;
436
                $lastOptgroup = $option['optgroup'];
437
                continue;
438
            }
439
440
            foreach ($filters as $filter) {
441
                if ($id !== $filter) {
442
                    continue;
443
                }
444
445
                if (!$optgroup) {
446
                    $optgroup = $lastOptgroup;
447
                    $filteredOptions[]['optgroup'] = $optgroup;
448
                }
449
450
                $filteredOptions[$id] = $option;
451
            }
452
        }
453
454
        return $filteredOptions;
455
    }
456
}
457