Completed
Push — master ( 49a2b5...6ccf11 )
by Leandro
13s
created

Translator::init()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
ccs 19
cts 19
cp 1
cc 1
eloc 21
nc 1
nop 0
crap 1
1
<?php
2
3
namespace leandrogehlen\querybuilder;
4
5
use yii\base\Object;
6
use yii\helpers\ArrayHelper;
7
8
/**
9
 * Translator is used to build WHERE clauses from rules configuration
10
 *
11
 * The typical usage of Translator is as follows,
12
 *
13
 * ```php
14
 * public function actionIndex()
15
 * {
16
 *     $query = Customer::find();
17
 *     $rules = Yii::$app->request->post('rules');
18
 *
19
 *     if ($rules) {
20
 *         $translator = new Translator(Json::decode($rules));
21
 *         $query->andWhere($translator->where())
22
 *               ->addParams($translator->params());
23
 *     }
24
 *
25
 *     $dataProvider = new ActiveDataProvider([
26
 *         'query' => $query,
27
 *     ]);
28
 *
29
 *     return $this->render('index', [
30
 *         'dataProvider' => $dataProvider,
31
 *     ]);
32
 * }
33
 * ```
34
 * @author Leandro Gehlen <[email protected]>
35
 */
36
class Translator extends Object
37
{
38
    private $_where;
39
    private $_params = [];
40
    private $_operators;
41
42
    /**
43
     * Constructors.
44
     * @param array $data Rules configuraion
45
     * @param array $config the configuration array to be applied to this object.
46
     */
47 1
    public function __construct($data, $config = [])
48
    {
49 1
        parent::__construct($config);
50 1
        $this->_where = $this->buildWhere($data);
51 1
    }
52
53
    /**
54
     * @inheritdoc
55
     */
56 1
    public function init()
57
    {
58 1
        $this->_operators = [
59 1
            'equal' =>            '= ?',
60 1
            'not_equal' =>        '<> ?',
61
            'in' =>               ['op' => 'IN (?)',     'list' => true, 'sep' => ', ' ],
62
            'not_in' =>           ['op' => 'NOT IN (?)', 'list' => true, 'sep' => ', '],
63 1
            'less' =>             '< ?',
64 1
            'less_or_equal' =>    '<= ?',
65 1
            'greater' =>          '> ?',
66 1
            'greater_or_equal' => '>= ?',
67
            'between' =>          ['op' => 'BETWEEN ?',   'list' => true, 'sep' => ' AND '],
68 1
            'begins_with' =>      ['op' => 'LIKE ?',     'fn' => function($value){ return "$value%"; } ],
69 1
            'not_begins_with' =>  ['op' => 'NOT LIKE ?', 'fn' => function($value){ return "$value%"; } ],
70 1
            'contains' =>         ['op' => 'LIKE ?',     'fn' => function($value){ return "%$value%"; } ],
71 1
            'not_contains' =>     ['op' => 'NOT LIKE ?', 'fn' => function($value){ return "%$value%"; } ],
72 1
            'ends_with' =>        ['op' => 'LIKE ?',     'fn' => function($value){ return "%$value"; } ],
73 1
            'not_ends_with' =>    ['op' => 'NOT LIKE ?', 'fn' => function($value){ return "%$value"; } ],
74 1
            'is_empty' =>         '= ""',
75 1
            'is_not_empty' =>     '<> ""',
76 1
            'is_null' =>          'IS NULL',
77 1
            'is_not_null' =>      'IS NOT NULL'
78
        ];
79 1
    }
80
81
82
    /**
83
     * Encodes filter rule into SQL condition
84
     * @param string $field field name
85
     * @param string|array $type operator type
86
     * @param string|array $params query parameters
87
     * @return string encoded rule
88
     */
89 1
    protected function encodeRule($field, $type, $params)
90
    {
91 1
        $pattern = $this->_operators[$type];
92 1
        $keys = array_keys($params);
93
94 1
        if (is_string($pattern)) {
95 1
            $replacement = !empty($keys) ? $keys[0] : null;
96
        } else {
97 1
            $op = ArrayHelper::getValue($pattern, 'op');
98 1
            $list = ArrayHelper::getValue($pattern, 'list');
99 1
            if ($list){
100 1
                $sep = ArrayHelper::getValue($pattern, 'sep');
101 1
                $replacement = implode($sep, $keys);
102
            } else {
103 1
                $fn = ArrayHelper::getValue($pattern, 'fn');
104 1
                $replacement = key($params);
105 1
                $params[$replacement] = call_user_func($fn, $params[$replacement]);
106
            }
107 1
            $pattern = $op;
108
        }
109
110 1
        $this->_params = array_merge($this->_params, $params);
111 1
        return $field . " " . ($replacement ? str_replace("?", $replacement, $pattern) : $pattern);
112
    }
113
114
    /**
115
     * @param array $data rules configuration
116
     * @return string the WHERE clause
117
     */
118 1
    protected function buildWhere($data)
119
    {
120 1
        if (!isset($data['rules']) || !$data['rules']) {
121
            return '';
122
        }
123
124 1
        $where = [];
125 1
        $condition = " " . $data['condition'] . " ";
126
127 1
        foreach ($data['rules'] as $rule) {
128 1
            if (isset($rule['condition'])) {
129 1
                $where[] = $this->buildWhere($rule);
130
            } else {
131 1
                $params = [];
132 1
                $operator = $rule['operator'];
133 1
                $field = $rule['field'];
134 1
                $value = ArrayHelper::getValue($rule, 'value');
135
136 1
                if ($value !== null) {
137 1
                    $i = count($this->_params);
138
139 1
                    if (!is_array($value)) {
140 1
                        $value = [$value];
141
                    }
142
143 1
                    foreach ($value as $v) {
144 1
                        $params[":p$i"] = $v;
145 1
                        $i++;
146
                    }
147
                }
148 1
                $where[] = $this->encodeRule($field, $operator, $params);
149
            }
150
        }
151 1
        return "(" . implode($condition, $where) . ")";
152
    }
153
154
    /**
155
     * Returns query WHERE condition.
156
     * @return string
157
     */
158 1
    public function where()
159
    {
160 1
        return $this->_where;
161
    }
162
163
    /**
164
     * Returns the parameters to be bound to the query.
165
     * @return array
166
     */
167 1
    public function params()
168
    {
169 1
        return $this->_params;
170
    }
171
}