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