Passed
Push — v4 ( 516250...2a72ae )
by Benjamin
04:11
created

Metadata::getFilteredOptions()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 16
nc 3
nop 2
dl 0
loc 28
rs 8.439
c 0
b 0
f 0
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 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...
243
     *
244
     * @return array
245
     */
246
    public function getColumnGroups($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
        // ksort($groups);
261
262
        if ($type) {
263
            $this->groups[$type] = $groups;
264
        }
265
266
        return $groups;
267
    }
268
269
    /**
270
     * Returns select dimension options
271
     *
272
     * @param array $filters
273
     *
274
     * @return array
275
     */
276
    public function getSelectDimensionOptions(array $filters = null): array
277
    {
278
        if (!$this->selectDimensionOptions) {
279
            $this->selectDimensionOptions = $this->getSelectOptions('DIMENSION');
280
        }
281
282
        if ($filters) {
283
            $this->selectDimensionOptions = $this->filterOptions($this->selectDimensionOptions, $filters);
284
        }
285
286
        return $this->selectDimensionOptions;
287
    }
288
289
    /**
290
     * Returns select metric options
291
     *
292
     * @param array $filters
293
     *
294
     * @return array
295
     */
296
    public function getSelectMetricOptions(array $filters = null): array
297
    {
298
        if (!$this->selectMetricOptions) {
299
            $this->selectMetricOptions = $this->getSelectOptions('METRIC');
300
        }
301
302
        if ($filters) {
303
            $this->selectMetricOptions = $this->filterOptions($this->selectMetricOptions, $filters);
304
        }
305
306
        return $this->selectMetricOptions;
307
    }
308
309
    /**
310
     * Returns select options
311
     *
312
     * @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...
313
     * @param array $filters
314
     *
315
     * @return array
316
     */
317
    public function getSelectOptions($type = null, array $filters = null): array
318
    {
319
        $options = [];
320
321
        foreach ($this->getColumnGroups($type) as $group) {
322
            $options[]['optgroup'] = Craft::t('analytics', $group);
323
324
            foreach ($this->getColumns($type) as $column) {
325
                if ($column->group === $group) {
326
                    $options[$column->id] = Craft::t('analytics', $column->uiName);
327
                }
328
            }
329
        }
330
331
332
        // filters
333
334
        if ($filters) {
335
            $options = $this->filterOptions($options, $filters);
336
        }
337
338
        return $options;
339
    }
340
341
    /**
342
     * Returns the metrics
343
     *
344
     * @return array
345
     */
346
    public function getMetrics(): array
347
    {
348
        if (!$this->metrics) {
349
            $this->metrics = $this->getColumns('METRIC');
350
        }
351
352
        return $this->metrics;
353
    }
354
355
    /**
356
     * Returns the file path of the dimensions-metrics.json file
357
     *
358
     * @return string|bool
359
     */
360
    public function getDimmetsFilePath()
361
    {
362
        return Craft::getAlias('@dukt/analytics/etc/data/dimensions-metrics.json');
363
    }
364
365
    // Private Methods
366
    // =========================================================================
367
368
    /**
369
     * Loads the columns from the dimensions-metrics.json file
370
     *
371
     * @return array
372
     */
373
    private function _loadColumns(): array
374
    {
375
        $cols = [];
376
377
        $path = Analytics::$plugin->metadata->getDimmetsFilePath();
378
379
380
        $contents = file_get_contents($path);
381
382
        $columnsResponse = Json::decode($contents);
383
384
        if ($columnsResponse) {
385
            foreach ($columnsResponse as $columnResponse) {
386
                $cols[$columnResponse['id']] = new Column($columnResponse);
387
388
                if ($columnResponse['id'] === 'ga:countryIsoCode') {
389
                    $cols[$columnResponse['id']]->uiName = 'Country';
390
                }
391
            }
392
        }
393
394
        return $cols;
395
    }
396
397
    /**
398
     * Get Data
399
     *
400
     * @param $name
401
     *
402
     * @return array
403
     * @internal param string $label
404
     *
405
     */
406
    private function _getData($name): array
407
    {
408
        $jsonData = file_get_contents(Craft::getAlias('@dukt/analytics/etc/data/'.$name.'.json'));
409
410
        return json_decode($jsonData, true);
411
    }
412
413
    /**
414
     * @param array $options
415
     * @param array $filters
416
     *
417
     * @return array
418
     */
419
    private function filterOptions(array $options, array $filters): array
420
    {
421
        if (\count($filters) === 0) {
422
            return $options;
423
        }
424
425
        return $this->getFilteredOptions($options, $filters);
426
    }
427
428
    /**
429
     * Get filtered options.
430
     *
431
     * @param array $options
432
     * @param array $filters
433
     *
434
     * @return array
435
     */
436
    private function getFilteredOptions(array $options, array $filters): array
437
    {
438
        $filteredOptions = [];
439
        $optgroup = null;
440
        $lastOptgroup = null;
441
442
        foreach ($options as $id => $option) {
443
            if (isset($option['optgroup'])) {
444
                $optgroup = null;
445
                $lastOptgroup = $option['optgroup'];
446
                continue;
447
            }
448
449
            foreach ($filters as $filter) {
450
                if ($id !== $filter) {
451
                    continue;
452
                }
453
454
                if (!$optgroup) {
455
                    $optgroup = $lastOptgroup;
456
                    $filteredOptions[]['optgroup'] = $optgroup;
457
                }
458
459
                $filteredOptions[$id] = $option;
460
            }
461
        }
462
463
        return $filteredOptions;
464
    }
465
}
466