StorageMapper::create()   B
last analyzed

Complexity

Conditions 9
Paths 120

Size

Total Lines 63
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 44
nc 120
nop 8
dl 0
loc 63
rs 7.5271
c 0
b 0
f 0

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * Analytics
4
 *
5
 * SPDX-FileCopyrightText: 2019-2022 Marcel Scherello
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8
9
namespace OCA\Analytics\Db;
10
11
use OCP\DB\Exception;
0 ignored issues
show
Bug introduced by
The type OCP\DB\Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use OCP\DB\QueryBuilder\IQueryBuilder;
0 ignored issues
show
Bug introduced by
The type OCP\DB\QueryBuilder\IQueryBuilder was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use OCP\IDBConnection;
0 ignored issues
show
Bug introduced by
The type OCP\IDBConnection was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use OCP\IL10N;
0 ignored issues
show
Bug introduced by
The type OCP\IL10N was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use Psr\Log\LoggerInterface;
0 ignored issues
show
Bug introduced by
The type Psr\Log\LoggerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
17
class StorageMapper
18
{
19
    private $userId;
20
    private $l10n;
21
    private $db;
22
    private $logger;
23
    const TABLE_NAME = 'analytics_facts';
24
25
    public function __construct(
26
        $userId,
27
        IL10N $l10n,
28
        IDBConnection $db,
29
        LoggerInterface $logger
30
    )
31
    {
32
        $this->userId = $userId;
33
        $this->l10n = $l10n;
34
        $this->db = $db;
35
        $this->logger = $logger;
36
        self::TABLE_NAME;
37
    }
38
39
    /**
40
     * create data
41
     * @param int $datasetId
42
     * @param $dimension1
43
     * @param $dimension2
44
     * @param $value
45
     * @param string|null $user_id
46
     * @param null $timestamp
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $timestamp is correct as it would always require null to be passed?
Loading history...
47
     * @param $bulkInsert
48
     * @param $aggregation
49
     * @return string
50
     * @throws Exception
51
     */
52
    public function create(int $datasetId, $dimension1, $dimension2, $value, string $user_id = null, $timestamp = null, $bulkInsert = null, $aggregation = null)
53
    {
54
        $dimension1 = $dimension1 !== null ? str_replace('*', '', $dimension1) : null;
55
        $dimension2 = $dimension2 !== null ? str_replace('*', '', $dimension2) : null;
56
        $timestamp = $timestamp ?? time();
57
        $result = '';
58
59
        if ($user_id) $this->userId = $user_id;
60
61
        // if the data source option to delete all date before loading is "true"
62
        // in this case, bulkInsert is set to true. Then no further checks for existing records is needed
63
        if ($bulkInsert === null) {
64
            $sql = $this->db->getQueryBuilder();
65
            $sql->from(self::TABLE_NAME)
66
                ->addSelect('value')
67
                ->where($sql->expr()->eq('user_id', $sql->createNamedParameter($this->userId)))
68
                ->andWhere($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
69
70
            // if the dimension value is null, a different where clause is required
71
            $expression1 = $dimension1 === null ? $sql->expr()->isNull('dimension1') : $sql->expr()->eq('dimension1', $sql->createNamedParameter($dimension1));
72
            $expression2 = $dimension2 === null ? $sql->expr()->isNull('dimension2') : $sql->expr()->eq('dimension2', $sql->createNamedParameter($dimension2));
73
74
            $sql->andWhere($expression1)
75
                ->andWhere($expression2);
76
77
            $statement = $sql->executeQuery();
78
            $result = $statement->fetch();
79
            $statement->closeCursor();
80
        }
81
82
        if ($result) {
83
            $sql = $this->db->getQueryBuilder();
84
            $sql->update(self::TABLE_NAME)
85
                ->set('timestamp', $sql->createNamedParameter($timestamp))
86
                ->where($sql->expr()->eq('user_id', $sql->createNamedParameter($this->userId)))
87
                ->andWhere($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)))
88
                ->andWhere($sql->expr()->eq('dimension1', $sql->createNamedParameter($dimension1)))
89
                ->andWhere($sql->expr()->eq('dimension2', $sql->createNamedParameter($dimension2)));
90
91
            if ($aggregation === 'xxsummation') {
92
                // Feature not yet available
93
                // $this->logger->error('old value: ' . $result['value']);
94
                // $this->logger->error('new value: ' . $value + $result['value']);
95
                $sql->set('value', $sql->createNamedParameter($value + $result['value']));
96
            } else {
97
                $sql->set('value', $sql->createNamedParameter($value));
98
            }
99
100
            $sql->executeStatement();
101
            return 'update';
102
        } else {
103
            $sql = $this->db->getQueryBuilder();
104
            $sql->insert(self::TABLE_NAME)
105
                ->values([
106
                    'user_id' => $sql->createNamedParameter($this->userId),
107
                    'dataset' => $sql->createNamedParameter($datasetId),
108
                    'dimension1' => $sql->createNamedParameter($dimension1),
109
                    'dimension2' => $sql->createNamedParameter($dimension2),
110
                    'value' => $sql->createNamedParameter($value),
111
                    'timestamp' => $sql->createNamedParameter($timestamp),
112
                ]);
113
            $sql->executeStatement();
114
            return 'insert';
115
        }
116
    }
117
118
    /**
119
     * read data for dataset
120
     * @param int $dataset
121
     * @param array $options
122
     * @return array
123
     */
124
    public function read(int $dataset, $options = '')
125
    {
126
        $sql = $this->db->getQueryBuilder();
127
        $sql->from(self::TABLE_NAME)
128
            ->where($sql->expr()->eq('dataset', $sql->createNamedParameter($dataset)))
129
            ->addgroupBy('dataset');
130
131
        // loop the available dimensions and check if any is hidden by the drill down selection of the user
132
        // if the dimension is not part of the drill down filter, it is not hidden => to be displayed
133
        $availableDimensions = array('dimension1', 'dimension2');
134
        foreach ($availableDimensions as $dimension) {
135
            if (!isset($options['drilldown'][$dimension])) {
136
                $sql->addSelect($dimension)
137
                    ->addGroupBy($dimension)
138
                    ->addOrderBy($dimension, 'ASC');
139
            }
140
        }
141
142
        // value column deeds to be at the last position in the select. So it needs to be after the dynamic selects
143
        $sql->addSelect($sql->func()->sum('value'));
144
145
        // add the where clauses depending on the filter selection of the
146
        if (isset($options['filter'])) {
147
            foreach ($options['filter'] as $key => $value) {
148
                $this->sqlWhere($sql, $key, $value['option'], $value['value']);
149
            }
150
        }
151
152
        $statement = $sql->executeQuery();
153
        $rows = $statement->fetchAll();
154
        $statement->closeCursor();
155
156
        // reindex result to get rid of the column headers as the frontend works incremental
157
        foreach ($rows as &$row) {
158
            $row = array_values($row);
159
        }
160
        return $rows;
161
    }
162
163
    /**
164
     * delete data
165
     * @param int $datasetId
166
     * @param $dimension1
167
     * @param $dimension2
168
     * @param string|null $user_id
169
     * @return bool
170
     * @throws Exception
171
     */
172
    public function delete(int $datasetId, $dimension1, $dimension2, string $user_id = null)
173
    {
174
        if ($user_id) $this->userId = $user_id;
175
176
        $sql = $this->db->getQueryBuilder();
177
        $sql->delete(self::TABLE_NAME)
178
            ->where($sql->expr()->eq('user_id', $sql->createNamedParameter($this->userId)))
179
            ->andWhere($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
180
181
        // a %(*) does not work for null columns. in case it is just %, don't apply the where clause at all
182
        if ($dimension1 !== '*') {
183
            $dimension1 = str_replace('*', '%', $dimension1);
184
            $sql->andWhere($sql->expr()->like('dimension1', $sql->createNamedParameter($dimension1)));
185
        }
186
        if ($dimension2 !== '*') {
187
            $dimension2 = str_replace('*', '%', $dimension2);
188
            $sql->andWhere($sql->expr()->like('dimension2', $sql->createNamedParameter($dimension2)));
189
        }
190
191
        $sql->executeStatement();
192
        return true;
193
    }
194
195
    /**
196
     * Simulate delete data
197
     * @param int $datasetId
198
     * @param $dimension1
199
     * @param $dimension2
200
     * @return array
201
     * @throws Exception
202
     */
203
    public function deleteSimulate(int $datasetId, $dimension1, $dimension2)
204
    {
205
        $sql = $this->db->getQueryBuilder();
206
        $sql->from(self::TABLE_NAME)
207
            ->selectAlias($sql->func()->count('*'), 'count')
208
            ->where($sql->expr()->eq('user_id', $sql->createNamedParameter($this->userId)))
209
            ->andWhere($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
210
211
        if ($dimension1 !== '*') {
212
            $dimension1 = str_replace('*', '%', $dimension1);
213
            $sql->andWhere($sql->expr()->like('dimension1', $sql->createNamedParameter($dimension1)));
214
        }
215
216
        if ($dimension2 !== '*') {
217
            $dimension2 = str_replace('*', '%', $dimension2);
218
            $sql->andWhere($sql->expr()->like('dimension2', $sql->createNamedParameter($dimension2)));
219
        }
220
221
        $statement = $sql->executeQuery();
222
        $result = $statement->fetch();
223
        $statement->closeCursor();
224
225
        return $result;
226
    }
227
228
    /**
229
     * delete data
230
     * @param int $datasetId
231
     * @param $options
232
     * @return int
233
     * @throws Exception
234
     */
235
    public function deleteWithFilter(int $datasetId, $options)
236
    {
237
        $sql = $this->db->getQueryBuilder();
238
        $sql->delete(self::TABLE_NAME)
239
            ->where($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
240
241
        // add the where clauses depending on the filter selection of the
242
        if (isset($options['filter'])) {
243
            foreach ($options['filter'] as $key => $value) {
244
                $this->sqlWhere($sql, $key, $value['option'], $value['value']);
245
            }
246
        }
247
        return $sql->executeStatement(); // number of deleted rows
248
    }
249
250
    /**
251
     * delete data
252
     * @param int $datasetId
253
     * @param $options
254
     * @return bool
255
     * @throws Exception
256
     */
257
    public function deleteWithFilterSimulate(int $datasetId, $options)
258
    {
259
        $sql = $this->db->getQueryBuilder();
260
        $sql->from(self::TABLE_NAME)
261
            ->selectAlias($sql->func()->count('*'), 'count')
262
            ->where($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
263
264
        // add the where clauses depending on the filter selection of the
265
        if (isset($options['filter'])) {
266
            foreach ($options['filter'] as $key => $value) {
267
                $this->sqlWhere($sql, $key, $value['option'], $value['value']);
268
            }
269
        }
270
271
        $statement = $sql->executeQuery();
272
        $result = $statement->fetch();
273
        $statement->closeCursor();
274
        return $result;
275
    }
276
277
    /**
278
     * delete all data of a dataset
279
     * @param int $datasetId
280
     * @return bool
281
     */
282
    public function deleteByDataset(int $datasetId)
283
    {
284
        $sql = $this->db->getQueryBuilder();
285
        $sql->delete(self::TABLE_NAME)
286
            ->where($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
287
        $sql->executeStatement();
288
        return true;
289
    }
290
291
    /**
292
     * Simulate delete data
293
     * @param int $datasetId
294
     * @param string|null $user_id
295
     * @return array
296
     * @throws Exception
297
     */
298
    public function getRecordCount(int $datasetId, string $user_id = null)
299
    {
300
        if ($user_id) $this->userId = $user_id;
301
        $sql = $this->db->getQueryBuilder();
302
        $sql->from(self::TABLE_NAME)
303
            ->selectAlias($sql->func()->count('*'), 'count')
304
            ->where($sql->expr()->eq('user_id', $sql->createNamedParameter($this->userId)))
305
            ->andWhere($sql->expr()->eq('dataset', $sql->createNamedParameter($datasetId)));
306
        $statement = $sql->executeQuery();
307
        $result = $statement->fetch();
308
        $statement->closeCursor();
309
        return $result;
310
    }
311
312
    /**
313
     * Add where statements to a query builder
314
     *
315
     * @param IQueryBuilder $sql
316
     * @param $column
317
     * @param $option
318
     * @param $value
319
     */
320
    protected function sqlWhere(IQueryBuilder $sql, $column, $option, $value)
321
    {
322
        if ($option === 'EQ') {
323
            $sql->andWhere($sql->expr()->eq($column, $sql->createNamedParameter($value)));
324
        } elseif ($option === 'GT') {
325
            $sql->andWhere($sql->expr()->gt($column, $sql->createNamedParameter($value)));
326
        } elseif ($option === 'LT') {
327
            $sql->andWhere($sql->expr()->lt($column, $sql->createNamedParameter($value)));
328
        } elseif ($option === 'IN') {
329
            $sql->andWhere($sql->expr()->in($column, $sql->createParameter('inValues')));
330
            $sql->setParameter('inValues', explode(',', $value), IQueryBuilder::PARAM_STR_ARRAY);
331
        } elseif ($option === 'LIKE') {
332
            $sql->andWhere($sql->expr()->like($column, $sql->createNamedParameter('%' . $value . '%')));
333
        }
334
    }
335
}