Skip to content

Commit ce4ce91

Browse files
committed
1. 'Date/year/month/day/time' functions were added.
2. 'Advanced Joins' implemented ( which involves the solution for the #2 issue ) 3. Licence file added 4. Some work on Aliased Tables (on from and joins)
1 parent c680a83 commit ce4ce91

File tree

12 files changed

+240
-54
lines changed

12 files changed

+240
-54
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Rexhep Shijaku
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

examples/data_time.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
use RexShijaku\SQLToLaravelBuilder\SQLToLaravelBuilder;
4+
5+
require_once dirname(__FILE__) . '/../vendor/autoload.php';
6+
7+
//==========================================================
8+
$converter = new SQLToLaravelBuilder();
9+
10+
$sql = 'SELECT * FROM members WHERE DATE(created_at) = "2021-03-31" ';
11+
echo $converter->convert($sql);
12+
// prints
13+
// DB::table('members')
14+
// ->whereDate('created_at', '=', '2021-03-31')
15+
// ->get();
16+
17+
//==========================================================
18+
19+
$sql = 'SELECT * FROM members WHERE YEAR(created_at) = 1991';
20+
echo $converter->convert($sql);
21+
// prints
22+
// DB::table('members')
23+
// ->whereYear('created_at', '=', 1991)
24+
// ->get();
25+
26+
//==========================================================
27+
28+
$sql = 'SELECT * FROM members WHERE MONTH(created_at) = 12 ';
29+
echo $converter->convert($sql);
30+
// DB::table('members')
31+
// ->whereMonth('created_at', '=', 12)
32+
// ->get();
33+
34+
//==========================================================
35+
36+
$sql = 'SELECT * FROM members WHERE DAY(created_at) = 15 ';
37+
echo $converter->convert($sql);
38+
// prints
39+
// DB::table('members')
40+
// ->whereDay('created_at', '=', 15)
41+
// ->get();
42+
43+
//==========================================================
44+
45+
$sql = 'SELECT * FROM members WHERE TIME(created_at) = "11:20:45" ';
46+
echo $converter->convert($sql);
47+
// prints
48+
// DB::table('members')
49+
// ->whereTime('created_at', '=', '11:20:45')
50+
// ->get();

examples/join.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,26 @@
4343
// ->crossJoin('details')
4444
// ->get();
4545

46-
//==========================================================
46+
//===================Advanced Join Clauses========================
47+
48+
$sql = 'SELECT * FROM members JOIN details
49+
ON members.id = details.members_id
50+
AND age > 10 AND age NOT BETWEEN 10 AND 20
51+
AND title IS NOT NULL AND NOT age > 10 AND NAME LIKE "%Jo%"
52+
AND age NOT IN (10,20,30)
53+
LEFT JOIN further_details fd
54+
ON details.id = fd.details_id';
55+
echo $converter->convert($sql);
56+
// prints
57+
// DB::table('members')
58+
// ->join('details', function ($join) {
59+
// $join->on('members.id', '=', 'details.members_id')
60+
// ->where('age', '>', 10)
61+
// ->whereNotBetween('age', [10, 20])
62+
// ->whereNotNull('title')
63+
// ->whereRaw(' NOT age > ? ', [10])
64+
// ->where('NAME', 'LIKE', '%Jo%')
65+
// ->whereNotIn('age', [10, 20, 30]);
66+
// })
67+
// ->leftJoin(DB::raw('further_details fd'), 'details.id', '=', 'fd.details_id')
68+
// ->get();

src/Options.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Options
1010
'facade' => 'DB::',
1111
'group' => true
1212
);
13+
private $supporting_fn = array('date', 'month', 'year' ,'day', 'time');
1314

1415
public function __construct($options)
1516
{
@@ -31,6 +32,7 @@ public function set(): void
3132

3233
unset($this->options['settings']); // unset reserved
3334
$this->options['settings']['agg'] = $this->aggregate_fn;
35+
$this->options['settings']['fns'] = $this->supporting_fn;
3436
}
3537

3638
public function get()

src/builders/CriterionBuilder.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ public function build(array $parts, array &$skip_bag = array())
104104
$fn = $this->getValue($part['sep']) == 'or' ? 'orWhereRaw' : 'whereRaw';
105105
$query_val .= '->' . $fn . '(' . $this->quote($part['field'] . ' AGAINST ' . $part['value']) . ')';
106106
break;
107+
case CriterionTypes::Function:
108+
$fn = $this->getValue($part['sep']) == 'or' ? 'orWhere' : 'where';
109+
$fn = $this->fnMerger(array($fn, $part['fn']));
110+
$op = $part['operator'];
111+
$inner = $this->quote($part['field']) . ',' . $this->quote(strtoupper($op)) . ',' . $this->wrapValue($part['value']['value']);
112+
$query_val .= '->' . $fn . '(' . $inner . ')';
113+
break;
107114
default:
108115
break;
109116
}

src/builders/JoinBuilder.php

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,56 @@ public function build(array $parts, array &$skip_bag = array())
2222

2323
foreach ($parts as $join) {
2424

25-
$condition = implode('', $join['condition_separators']);
2625
if ($this->getValue($join['type']) !== 'join') { // left,right,cross etc
2726
$fn = $this->fnMerger(array(strtolower($join['type']), 'join'));
2827
} else
2928
$fn = $this->fnMerger(array('join'));
3029

31-
$qb .= "->" . $fn . "(" . $this->quote($join['table']);
32-
if (!empty($join['condition_fields'])) { // in cross e.g are empty
33-
$qb .= "," . $this->quote($join['condition_fields'][0])
34-
. "," . $this->quote($condition)
35-
. "," . $this->quote($join['condition_fields'][1]);
30+
$qb .= "->" . $fn . "(" . $this->buildRawable($join['table'], $join['table_is_raw']);
31+
if (isset($join['on_clause']) && count($join['on_clause']) > 0) // in cross join no on_clause!
32+
{
33+
// everything except columns are raw !
34+
if (count($join['on_clause']) == 1
35+
&& $join['on_clause'][0]['type'] !== 'between'
36+
&& $join['on_clause'][0]['raw_field'] === false
37+
&& $join['on_clause'][0]['raw_value'] === false) {
38+
39+
$on_clause = $join['on_clause'][0];
40+
$qb .= "," . $this->quote($on_clause['field'])
41+
. "," . $this->quote(implode(' ', $on_clause['operators']))
42+
. "," . $this->quote($on_clause['value']);
43+
} else {
44+
45+
$qb .= ',' . 'function($join) {';
46+
$qb .= '$join';
47+
48+
foreach ($join['on_clause'] as $on_clause) {
49+
50+
if ($on_clause['type'] == 'between' || $on_clause['raw_field'] || $on_clause['raw_value']) {
51+
if (isset($on_clause['const_value']))
52+
$on_clause['raw_value'] = !$on_clause['const_value'];
53+
$builder = new CriterionBuilder($this->options);
54+
$q = $builder->build(array($on_clause));
55+
$qb .= $q;
56+
} else {
57+
// no raw found and not between
58+
$operators = implode(' ', $on_clause['operators']);
59+
$fn_parts = $on_clause['sep'] == 'and' ? array('on') : array('or', 'on');
60+
61+
$qb .= '->';
62+
$qb .= $this->fnMerger($fn_parts);
63+
$qb .= '(';
64+
65+
$qb .= $this->quote($on_clause['field'], $on_clause['raw_field'])
66+
. "," . $this->quote($operators)
67+
. "," . $this->quote($on_clause['value'],
68+
!$on_clause['const_value'] && $on_clause['raw_value']);
69+
70+
$qb .= ')';
71+
}
72+
}
73+
$qb .= '; }';
74+
}
3675
}
3776
$qb .= ")";
3877
}

src/extractors/AbstractExtractor.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ function mergeExpressionParts($parts)
119119
return (implode('', $parts));
120120
}
121121

122-
protected function getWithAlias($val)
122+
protected function getWithAlias($val, &$is_raw)
123123
{
124124
if ($val['expr_type'] === 'table')
125125
$return = $val['table']; // no alias here, if any, it will be added at the end
@@ -130,8 +130,12 @@ protected function getWithAlias($val)
130130
$return = $val['base_expr'];
131131
}
132132
}
133-
if ($this->hasAlias($val))
134-
$return .= ' ' . $val['alias']['base_expr'];
133+
if ($this->hasAlias($val)) {
134+
$return .= ' ';
135+
if ($val['alias']['as'] === false) // because Laravel escapes 'table t' expressions entirely!
136+
$is_raw = true;
137+
$return .= $val['alias']['base_expr'];
138+
}
135139
return $return;
136140
}
137141

src/extractors/CriterionExtractor.php

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,16 @@ function getCriteriaParts($value, &$parts = array(), $context = CriterionContext
8181
'value' => $res_value['value'],
8282
'raw_field' => $res_field['is_raw'],
8383
'raw_value' => $res_value['is_raw'],
84-
'sep' => $logical_operator
84+
'sep' => $logical_operator,
85+
'const_value' => $res_value['is_const']
8586
);
8687
break;
8788
case 'is':
8889

8990
$this->handle_outer_negation = true;
9091

9192
$res_field = $this->getLeft($index, $value);
92-
$res_value = $this->getRight($index, $value, $curr_index);
93+
$res_value = $this->getRight($index, $value, $curr_index, $context);
9394

9495
$operators_ = array('is');
9596
if ($res_value['has_negation'])
@@ -102,7 +103,9 @@ function getCriteriaParts($value, &$parts = array(), $context = CriterionContext
102103
'value' => $res_value['value'],
103104
'raw_field' => $res_field['is_raw'],
104105
'raw_value' => $res_value['is_raw'],
105-
'sep' => $logical_operator); // now combine fields + operators
106+
'sep' => $logical_operator,
107+
'const_value' => $res_value['is_const']
108+
); // now combine fields + operators
106109
break;
107110
case "between":
108111
$btw_operators = array();
@@ -131,7 +134,7 @@ function getCriteriaParts($value, &$parts = array(), $context = CriterionContext
131134
$like_operators[] = 'like';
132135

133136
$res_field = $this->getLeft($index, $value);
134-
$res_val = $this->getRight($index, $value, $curr_index);
137+
$res_val = $this->getRight($index, $value, $curr_index, $context);
135138

136139

137140
$parts[] = array(
@@ -141,7 +144,8 @@ function getCriteriaParts($value, &$parts = array(), $context = CriterionContext
141144
'value' => $res_val['value'],
142145
'raw_field' => $res_field['is_raw'],
143146
'raw_value' => $res_val['is_raw'],
144-
'sep' => $logical_operator);
147+
'sep' => $logical_operator,
148+
'const_value' => $res_val['is_const']);
145149
break;
146150
case "in":
147151

@@ -161,8 +165,8 @@ function getCriteriaParts($value, &$parts = array(), $context = CriterionContext
161165
'raw_field' => $res_field['is_raw'],
162166
'raw_value' => $res_val['is_raw'],
163167
'sep' => $logical_operator,
164-
'as_php_arr' => $res_val['value_type'] == 'in-list'
165-
);
168+
'as_php_arr' => $res_val['value_type'] == 'in-list',
169+
'const_value' => $res_val['is_const']);
166170

167171
break;
168172
case "not":
@@ -196,6 +200,37 @@ function getCriteriaParts($value, &$parts = array(), $context = CriterionContext
196200
'sep' => $logical_operator
197201
);
198202

203+
} else if (CriterionContext::Where == $context) {
204+
$fn = $this->getValue($val['base_expr']);
205+
206+
if (in_array($fn, $this->options['settings']['fns'])) {
207+
208+
if($val['sub_tree'] !== false && $this->isRaw($val['sub_tree'][0]))
209+
continue;
210+
211+
$params = ''; // params is field in this context
212+
$this->getFnParams($val, $params);
213+
214+
$temp_index = $curr_index;
215+
$curr_index = $index = ($index + 1); // move to operator
216+
$sep = $this->getValue($value[$curr_index]['base_expr']);
217+
$res_val = $this->getRight($index, $value, $curr_index, $context);
218+
219+
if ($res_val['is_raw']) {
220+
$curr_index = $index = $temp_index;
221+
continue;
222+
}
223+
224+
225+
$parts[] = array(
226+
'type' => CriterionTypes::Function,
227+
'fn' => $fn,
228+
'field' => $params,
229+
'value' => $res_val,
230+
'operator' => $sep,
231+
'sep' => $logical_operator
232+
);
233+
}
199234
}
200235

201236
}
@@ -307,6 +342,8 @@ function getRight($index, $value, &$curr_index, $context = CriterionContext::Whe
307342
$right_operator = '';
308343
$is_raw = false;
309344

345+
$is_const = null;
346+
310347
while (!$this->isLogicalOperator($right_operator)) { // x > 2 and (until you find first logical operator keep looping)
311348
$right_ind++;
312349
if ($right_ind < count($value)) {
@@ -317,7 +354,10 @@ function getRight($index, $value, &$curr_index, $context = CriterionContext::Whe
317354
$right_operator = $this->getValue($value[$right_ind]['base_expr']);
318355
else {
319356
$value_ .= $value[$right_ind]['base_expr'];
320-
$is_raw = true; // if some operation is happening then the expression should not be escaped
357+
if ($context === CriterionContext::Join) // because on x=x+5, x+5 is escaped entirely !
358+
$is_const = false;
359+
else
360+
$is_raw = true; // if some operation is happening then the expression should not be escaped
321361
}
322362

323363
if ($right_operator == 'not')
@@ -329,8 +369,21 @@ function getRight($index, $value, &$curr_index, $context = CriterionContext::Whe
329369
break;
330370
} else {
331371
$value_type = $value[$right_ind]['expr_type'];
332-
if ($value[$right_ind]['expr_type'] != 'const')
333-
$is_raw = true;
372+
if ($context === CriterionContext::Join) { // on x = y (both x,y must be column)
373+
if ($value[$right_ind]['expr_type'] != 'colref')
374+
$is_raw = true;
375+
376+
if (!isset($is_const)) {
377+
if ($value[$right_ind]['expr_type'] == 'const')
378+
$is_const = true;
379+
else
380+
$is_const = false;
381+
}
382+
383+
} else {
384+
if ($value[$right_ind]['expr_type'] != 'const')
385+
$is_raw = true;
386+
}
334387

335388
if ($value_type == 'subquery')
336389
$value_type = 'field_only';
@@ -342,7 +395,8 @@ function getRight($index, $value, &$curr_index, $context = CriterionContext::Whe
342395
break;
343396
}
344397
$curr_index = $right_ind;
345-
return array('value' => $value_, 'has_negation' => $has_negation, 'is_raw' => $is_raw, 'value_type' => $value_type);
398+
return array('value' => $value_, 'has_negation' => $has_negation,
399+
'is_raw' => $is_raw, 'value_type' => $value_type, 'is_const' => $is_const);
346400
}
347401

348402
private function getBetweenValue($index, $value, &$curr_index)

src/extractors/FromExtractor.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,9 @@ public function extract(array $value, array $parsed = array())
3434

3535
function extractSingle($value)
3636
{
37-
return array('table' => $this->getTable($value), 'is_raw' => $value[0]['expr_type'] != 'table');
38-
}
39-
40-
private function getTable($value)
41-
{
42-
return $this->getWithAlias($value[0]);
37+
$is_raw = $value[0]['expr_type'] != 'table';
38+
$table = $this->getWithAlias($value[0], $is_raw);
39+
return array('table' => $table, 'is_raw' => $is_raw);
4340
}
4441

4542
}

0 commit comments

Comments
 (0)