QueryBuilderFilter::generateTableFilter()   B
last analyzed

Complexity

Conditions 8
Paths 2

Size

Total Lines 53
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 27
nc 2
nop 1
dl 0
loc 53
ccs 0
cts 8
cp 0
crap 72
rs 7.1199
c 1
b 0
f 0

How to fix   Long Method   

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:

1
<?php
2
/**
3
 * QueryBuilderFilter.php
4
 *
5
 * -Description-
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 * @package    LibreNMS
21
 * @link       http://librenms.org
22
 * @copyright  2016 Tony Murray
23
 * @author     Tony Murray <[email protected]>
24
 */
25
26
namespace app;
27
28
use Cache;
29
use DB;
30
use Settings;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, app\Settings.

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...
31
32
class QueryBuilderFilter
33
{
34
    public static function getAlertFilter()
35
    {
36
        return json_encode(self::generateMacroFilter('alert.macros.rule', self::generateTableFilter()));
37
    }
38
39
    private static function generateMacroFilter($setting, $filter = [])
40
    {
41
        foreach (Settings::get($setting, []) as $key => $value) {
42
            $filter[] = [
43
                'id'        => 'macros.'.$key,
44
                'type'      => 'integer',
45
                'input'     => 'radio',
46
                'values'    => ['1' => 'Yes', '0' => 'No'],
47
                'operators' => ['equal'],
48
            ];
49
        }
50
        return $filter;
51
    }
52
53
    private static function generateTableFilter($filter = [])
54
    {
55
        // check if the database schema has changed
56
        $db = DB::table('migrations')->pluck('migration');
57
        $cached = Cache::get('migrations_list', []);
58
59
        if ($db != $cached) {
60
            Cache::forget('query_builder_table_filter');
61
            Cache::forever('migrations_list', $db);
62
        }
63
64
        // return the table filter merged with $filter, fetch from cache if available
65
        return array_merge($filter, Cache::rememberForever('query_builder_table_filter', function () {
66
            $tableFilter = [];
67
            $schema = DB::getDoctrineSchemaManager();
68
69
            // Doctrine DBAL has issues with enums, pretend they are strings
70
            $schema->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
71
72
            $tables = $schema->listTables();
73
            foreach ($tables as $table) {
74
                $columns = $schema->listTableColumns($table->getName());
75
                $tableName = $table->getName();
76
77
                // only allow tables with a direct association to device_id
78
                if (!$table->hasColumn('device_id')) {
79
                    continue;
80
                }
81
82
                foreach ($columns as $column) {
83
                    $columnName = $column->getName();
84
85
                    // ignore device id columns, except in the devices table
86
                    if ($columnName == 'device_id' && $tableName != 'devices') {
87
                        continue;
88
                    }
89
90
                    $columnType = self::getColumnType($column->getType()->getName());
91
92
                    // ignore unsupported types (such as binary and blob)
93
                    if (is_null($columnType)) {
94
                        continue;
95
                    }
96
97
                    $tableFilter[] = [
98
                        'id'   => $tableName.'.'.$columnName,
99
                        'type' => $columnType,
100
                    ];
101
                }
102
            }
103
            return $tableFilter;
104
        }));
105
    }
106
107
    private static function getColumnType($type)
108
    {
109
        switch ($type) {
110
            case 'text':
111
//                return 'textarea';
112
            case 'string':
113
                return 'string';
114
115
            case 'integer':
116
            case 'smallint':
117
            case 'bigint':
118
                return 'integer';
119
120
            case 'double':
121
            case 'float':
122
            case 'decimal':
123
                return 'double';
124
125
            case 'date':
126
                return 'date';
127
128
            case 'time':
129
                return 'time';
130
131
            case 'datetime':
132
                return 'datetime';
133
134
            case 'boolean':
135
                return 'boolean';
136
        }
137
        // binary, blob
138
        return null;
139
    }
140
141
    public static function getGroupFilter()
142
    {
143
        return json_encode(self::generateMacroFilter('alert.macros.group', self::generateTableFilter()));
144
    }
145
}
146