Passed
Push — master ( 2ee843...cfc51d )
by Tony
09:10
created

QueryBuilderFluentParser::parseRuleToQuery()   D

Complexity

Conditions 24
Paths 25

Size

Total Lines 45
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 39
dl 0
loc 45
rs 4.1666
c 0
b 0
f 0
cc 24
nc 25
nop 3

How to fix   Complexity   

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
 * QueryBuilderFluentParser.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  2019 Tony Murray
23
 * @author     Tony Murray <[email protected]>
24
 */
25
26
namespace LibreNMS\Alerting;
27
28
use DB;
29
use Illuminate\Database\Query\Builder;
30
use Log;
31
32
class QueryBuilderFluentParser extends QueryBuilderParser
33
{
34
    /**
35
     * Convert the query builder rules to a Laravel Fluent builder
36
     *
37
     * @return Builder
38
     */
39
    public function toQuery()
40
    {
41
        if (empty($this->builder) || !array_key_exists('condition', $this->builder)) {
42
            return null;
43
        }
44
45
        $query = DB::table('devices');
46
47
        $this->joinTables($query);
48
49
        $this->parseGroupToQuery($query, $this->builder);
50
51
        return $query;
52
    }
53
54
    /**
55
     * @param Builder $query
56
     * @param array $rule
57
     * @param string $condition AND or OR
58
     * @return Builder
59
     */
60
    protected function parseGroupToQuery($query, $rule, $condition = 'AND')
61
    {
62
        foreach ($rule['rules'] as $group_rule) {
63
            if (array_key_exists('condition', $group_rule)) {
64
                $query->where(function ($query) use ($group_rule) {
65
                    $this->parseGroupToQuery($query, $group_rule, $group_rule['condition']);
66
                }, null, null, $condition);
67
            } else {
68
                $this->parseRuleToQuery($query, $group_rule, $condition);
69
            }
70
        }
71
        return $query;
72
    }
73
74
    /**
75
     * @param Builder $query
76
     * @param array $rule
77
     * @param string $condition AND or OR
78
     * @return Builder
79
     */
80
    protected function parseRuleToQuery($query, $rule, $condition = 'AND')
81
    {
82
        list($field, $op, $value) = $this->expandRule($rule);
83
84
        switch ($op) {
85
            case 'equal':
86
            case 'not_equal':
87
            case 'less':
88
            case 'less_or_equal':
89
            case 'greater':
90
            case 'greater_or_equal':
91
            case 'regex':
92
            case 'not_regex':
93
                return $query->where($field, self::$operators[$op], $value, $condition);
94
            case 'contains':
95
            case 'not_contains':
96
                return $query->where($field, self::$operators[$op], "%$value%", $condition);
97
            case 'begins_with':
98
            case 'not_begins_with':
99
                return $query->where($field, self::$operators[$op], "$value%", $condition);
100
            case 'ends_with':
101
            case 'not_ends_with':
102
                return $query->where($field, self::$operators[$op], "%$value", $condition);
103
            case 'is_empty':
104
            case 'is_not_empty':
105
                return $query->where($field, self::$operators[$op], '');
106
            case 'is_null':
107
            case 'is_not_null':
108
                return $query->whereNull($field, $condition, $op == 'is_not_null');
109
            case 'between':
110
            case 'not_between':
111
                return $query->whereBetween($field, $value, $condition, $op == 'not_between');
112
            case 'in':
113
            case 'not_in':
114
                $values = preg_split('/[, ]/', $value);
115
                if ($values !== false) {
116
                    return $query->whereIn($field, $values, $condition, $op == 'not_in');
117
                }
118
                Log::error('Could not parse in values, use comma or space delimiters');
119
                break;
120
            default:
121
                Log::error('Unhandled QueryBuilderFluentParser operation: ' . $op);
122
        }
123
124
        return $query;
125
    }
126
127
    /**
128
     * Extract field, operator and value from the rule and expand macros and raw values
129
     *
130
     * @param array $rule
131
     * @return array [field, operator, value]
132
     */
133
    protected function expandRule($rule)
134
    {
135
        $field = $rule['field'];
136
        if (starts_with($field, 'macros.')) {
137
            $field = DB::raw($this->expandMacro($field));
138
        }
139
140
        $op = $rule['operator'];
141
142
        $value = $rule['value'];
143
        if (!is_array($value) && starts_with($value, '`') && ends_with($value, '`')) {
144
            $value = DB::raw($this->expandMacro(trim($value, '`')));
145
        }
146
147
        return [$field, $op, $value];
148
    }
149
150
    /**
151
     * @param Builder $query
152
     * @return Builder
153
     */
154
    protected function joinTables($query)
155
    {
156
        foreach ($this->generateGlue() as $glue) {
157
            list($left, $right) = explode(' = ', $glue, 2);
158
            if (str_contains($right, '.')) { // last line is devices.device_id = ? for alerting... ignore it
159
                list($rightTable, $rightKey) = explode('.', $right);
160
                $query->leftJoin($rightTable, $left, $right);
161
            }
162
        }
163
164
        return $query;
165
    }
166
}
167