Metadata::getColumns()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
c 2
b 0
f 0
nc 4
nop 1
dl 0
loc 11
rs 10
1
<?php
2
/**
3
 * @link      https://dukt.net/analytics/
4
 * @copyright Copyright (c) 2022, Dukt
5
 * @license   https://github.com/dukt/analytics/blob/master/LICENSE.md
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
    // Public Methods
42
    // =========================================================================
43
44
    /**
45
     * Checks whether the dimensions & metrics file exists
46
     *
47
     * @return bool
48
     */
49
    public function dimmetsFileExists(): bool
50
    {
51
        $path = Analytics::$plugin->metadata->getDimmetsFilePath();
52
53
        if (file_exists($path)) {
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type false; however, parameter $filename of file_exists() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

53
        if (file_exists(/** @scrutinizer ignore-type */ $path)) {
Loading history...
54
            return true;
55
        }
56
57
        return false;
58
    }
59
60
    /**
61
     * Returns available data types for Google Analytics
62
     *
63
     * @param mixed
64
     *
65
     * @return array
66
     */
67
    public function getGoogleAnalyticsDataTypes(): array
68
    {
69
        $columns = $this->getColumns();
70
71
        $dataTypes = [];
72
73
        foreach ($columns as $column) {
74
            if (!isset($dataTypes[$column->dataType]) && !empty($column->dataType)) {
75
                $dataTypes[$column->dataType] = $column->dataType;
76
            }
77
        }
78
79
        return $dataTypes;
80
    }
81
82
    /**
83
     * Returns available data types
84
     *
85
     * @param mixed
86
     *
87
     * @return array
88
     */
89
    public function getDataTypes(): array
90
    {
91
        return [
92
            'string',
93
            'integer',
94
            'percent',
95
            'time',
96
            'currency',
97
            'float',
98
            'date'
99
        ];
100
    }
101
102
    /**
103
     * Get a dimension or a metric label from its id
104
     *
105
     * @param string $id
106
     *
107
     * @return mixed
108
     */
109
    public function getDimMet($id)
110
    {
111
        $columns = $this->getColumns();
112
113
        if (isset($columns[$id])) {
114
            return $columns[$id]->uiName;
115
        }
116
117
        return null;
118
    }
119
120
    /**
121
     * Returns columns based on a search string `$q`
122
     *
123
     * @param string $q
124
     *
125
     * @return array
126
     */
127
    public function searchColumns($q): array
128
    {
129
        $columns = $this->getColumns();
130
        $results = [];
131
132
        foreach ($columns as $column) {
133
            if (stripos($column->id, $q) !== false || stripos($column->uiName, $q) !== false) {
134
                $results[] = $column;
135
            }
136
        }
137
138
        return $results;
139
    }
140
141
    /**
142
     * Returns columns
143
     *
144
     * @param string $type
145
     *
146
     * @return array
147
     */
148
    public function getColumns(string $type = null): array
149
    {
150
        if (!$this->columns) {
151
            $this->columns = $this->_loadColumns();
152
        }
153
154
        if (!$type) {
155
            return $this->columns;
156
        }
157
158
        return $this->getColumnsByType($type);
159
    }
160
161
    /**
162
     * Returns dimension columns
163
     *
164
     * @return array
165
     */
166
    public function getDimensions(): array
167
    {
168
        if (!$this->dimensions) {
169
            $this->dimensions = $this->getColumns('DIMENSION');
170
        }
171
172
        return $this->dimensions;
173
    }
174
175
    /**
176
     * Returns column groups
177
     *
178
     * @param string|null $type
179
     *
180
     * @return array
181
     */
182
    public function getColumnGroups(string $type = null): array
183
    {
184
        if ($type && isset($this->groups[$type])) {
185
            return $this->groups[$type];
186
        }
187
188
        $groups = $this->_getColumnGroups($type);
189
190
        if ($type) {
191
            $this->groups[$type] = $groups;
192
        }
193
194
        return $groups;
195
    }
196
197
    /**
198
     * Returns select dimension options
199
     *
200
     * @param array $filters
201
     *
202
     * @return array
203
     */
204
    public function getSelectDimensionOptions(array $filters = null): array
205
    {
206
        $selectDimensionOptions = $this->getSelectOptions('DIMENSION');
207
208
        if ($filters) {
209
            $selectDimensionOptions = $this->filterOptions($selectDimensionOptions, $filters);
210
        }
211
212
        return $selectDimensionOptions;
213
    }
214
215
    /**
216
     * Returns select metric options
217
     *
218
     * @param array $filters
219
     *
220
     * @return array
221
     */
222
    public function getSelectMetricOptions(array $filters = null): array
223
    {
224
        $selectMetricOptions = $this->getSelectOptions('METRIC');
225
226
        if ($filters) {
227
            $selectMetricOptions = $this->filterOptions($selectMetricOptions, $filters);
228
        }
229
230
        return $selectMetricOptions;
231
    }
232
233
    /**
234
     * Returns select options
235
     *
236
     * @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...
237
     * @param array $filters
238
     *
239
     * @return array
240
     */
241
    public function getSelectOptions($type = null, array $filters = null): array
242
    {
243
        $options = [];
244
245
        foreach ($this->getColumnGroups($type) as $group) {
246
            $options[]['optgroup'] = Craft::t('analytics', $group);
247
248
            foreach ($this->getColumns($type) as $column) {
249
                if ($column->group === $group) {
250
                    $options[$column->id] = Craft::t('analytics', $column->uiName);
251
                }
252
            }
253
        }
254
255
        if ($filters) {
256
            $options = $this->filterOptions($options, $filters);
257
        }
258
259
        return $options;
260
    }
261
262
    /**
263
     * Returns the metrics
264
     *
265
     * @return array
266
     */
267
    public function getMetrics(): array
268
    {
269
        if (!$this->metrics) {
270
            $this->metrics = $this->getColumns('METRIC');
271
        }
272
273
        return $this->metrics;
274
    }
275
276
    /**
277
     * Returns the file path of the dimensions-metrics.json file
278
     *
279
     * @return string|bool
280
     */
281
    public function getDimmetsFilePath()
282
    {
283
        return Craft::getAlias('@dukt/analytics/etc/data/dimensions-metrics.json');
284
    }
285
286
    // Private Methods
287
    // =========================================================================
288
289
    /**
290
     * Loads the columns from the dimensions-metrics.json file
291
     *
292
     * @return array
293
     */
294
    private function _loadColumns(): array
295
    {
296
        $cols = [];
297
        $path = Analytics::$plugin->metadata->getDimmetsFilePath();
298
        $contents = file_get_contents($path);
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type false; however, parameter $filename of file_get_contents() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

298
        $contents = file_get_contents(/** @scrutinizer ignore-type */ $path);
Loading history...
299
        $columnsResponse = Json::decode($contents);
300
301
        if (!$columnsResponse) {
302
            return $cols;
303
        }
304
305
        foreach ($columnsResponse as $columnResponse) {
306
            $cols[$columnResponse['id']] = new Column($columnResponse);
307
308
            if ($columnResponse['id'] === 'ga:countryIsoCode') {
309
                $cols[$columnResponse['id']]->uiName = 'Country';
310
            }
311
        }
312
313
        return $cols;
314
    }
315
316
    /**
317
     * @param array $options
318
     * @param array $filters
319
     *
320
     * @return array
321
     */
322
    private function filterOptions(array $options, array $filters): array
323
    {
324
        if (\count($filters) === 0) {
325
            return $options;
326
        }
327
328
        return $this->getFilteredOptions($options, $filters);
329
    }
330
331
    /**
332
     * Get filtered options.
333
     *
334
     * @param array $options
335
     * @param array $filters
336
     *
337
     * @return array
338
     */
339
    private function getFilteredOptions(array $options, array $filters): array
340
    {
341
        $filteredOptions = [];
342
        $optgroup = null;
343
        $lastOptgroup = null;
344
345
        foreach ($options as $id => $option) {
346
            if (isset($option['optgroup'])) {
347
                $optgroup = null;
348
                $lastOptgroup = $option['optgroup'];
349
                continue;
350
            }
351
352
            foreach ($filters as $filter) {
353
                if ($id !== $filter) {
354
                    continue;
355
                }
356
357
                if (!$optgroup) {
358
                    $optgroup = $lastOptgroup;
359
                    $filteredOptions[]['optgroup'] = $optgroup;
360
                }
361
362
                $filteredOptions[$id] = $option;
363
            }
364
        }
365
366
        return $filteredOptions;
367
    }
368
369
    /**
370
     * Get column groups.
371
     *
372
     * @param string|null $type
373
     * @return array
374
     */
375
    private function _getColumnGroups(string $type = null): array
376
    {
377
        $groups = [];
378
379
        foreach ($this->getColumns() as $column) {
380
            if (!$type || ($type && $column->type === $type)) {
381
                $groups[$column->group] = $column->group;
382
            }
383
        }
384
385
        return $groups;
386
    }
387
388
    /**
389
     * Get columns by type.
390
     *
391
     * @param string $type
392
     * @return array
393
     */
394
    private function getColumnsByType(string $type): array
395
    {
396
        $columns = [];
397
398
        foreach ($this->columns as $column) {
399
            if ($column->type === $type) {
400
                $columns[] = $column;
401
            }
402
        }
403
404
        return $columns;
405
    }
406
}
407