1: <?php
2: /**
3: * @filesource Kotchasan/Database/Sql.php
4: *
5: * @copyright 2016 Goragod.com
6: * @license http://www.kotchasan.com/license/
7: *
8: * @see http://www.kotchasan.com/
9: */
10:
11: namespace Kotchasan\Database;
12:
13: /**
14: * SQL Function
15: *
16: * @author Goragod Wiriya <admin@goragod.com>
17: *
18: * @since 1.0
19: */
20: class Sql
21: {
22: /**
23: * คำสั่ง SQL ที่เก็บไว้
24: *
25: * @var string
26: */
27: protected $sql;
28: /**
29: * ตัวแปรเก็บพารามิเตอร์สำหรับการ bind.
30: *
31: * @var array
32: */
33: protected $values;
34:
35: /**
36: * หาค่าเฉลี่ยของคอลัมน์ที่เลือก
37: *
38: * @assert ('id')->text() [==] 'AVG(`id`)'
39: *
40: * @param string $column_name ชื่อคอลัมน์
41: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
42: * @param bool $distinct false (default) นับทุกคอลัมน์, true นับเฉพาะคอลัมน์ที่ไม่ซ้ำ
43: *
44: * @return \static
45: */
46: public static function AVG($column_name, $alias = null, $distinct = false)
47: {
48: return self::create('AVG('.($distinct ? 'DISTINCT ' : '').self::fieldName($column_name).')'.($alias ? " AS `$alias`" : ''));
49: }
50:
51: /**
52: * สร้างคำสั่ง BETWEEN ... AND ...
53: *
54: * @assert ('create_date', 'create_date', 'U.create_date')->text() [==] "`create_date` BETWEEN `create_date` AND U.`create_date`"
55: * @assert ('create_date', 'table_name.field_name', 'U.`create_date`')->text() [==] "`create_date` BETWEEN `table_name`.`field_name` AND U.`create_date`"
56: * @assert ('create_date', '`database`.`table`', '12-1-1')->text() [==] "`create_date` BETWEEN `database`.`table` AND '12-1-1'"
57: * @assert ('create_date', 0, 1)->text() [==] "`create_date` BETWEEN 0 AND 1"
58: *
59: * @param string $column_name
60: * @param string $min
61: * @param string $max
62: *
63: * @return \self
64: */
65: public static function BETWEEN($column_name, $min, $max)
66: {
67: return self::create(self::fieldName($column_name).' BETWEEN '.self::fieldName($min).' AND '.self::fieldName($max));
68: }
69:
70: /**
71: * สร้างคำสั่ง CONCAT หรือ CONCAT_WS
72: *
73: * @assert (array('fname', 'lname'))->text() [==] "CONCAT(`fname`, `lname`)"
74: * @assert (array('U.fname', 'U.`lname`'), 'displayname')->text() [==] "CONCAT(U.`fname`, U.`lname`) AS `displayname`"
75: * @assert (array('fname', 'lname'), 'displayname', ' ')->text() [==] "CONCAT_WS(' ', `fname`, `lname`) AS `displayname`"
76: *
77: * @param array $fields รายชื่อฟิลด์
78: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
79: * @param string|null $separator null (defailt) คืนค่าคำสั่ง CONCAT, ถ้าระบุเป็นค่าอื่นคืนค่าคำสั่ง CONCAT_WS
80: *
81: * @throws \InvalidArgumentException ถ้ารูปแบบของ $fields ไม่ใช่แอเรย์
82: *
83: * @return \static
84: */
85: public static function CONCAT($fields, $alias = null, $separator = null)
86: {
87: $fs = array();
88: if (is_array($fields)) {
89: foreach ($fields as $item) {
90: $fs[] = self::fieldName($item);
91: }
92: return self::create(($separator === null ? 'CONCAT(' : "CONCAT_WS('$separator', ").implode(', ', $fs).($alias ? ") AS `$alias`" : ')'));
93: } else {
94: throw new \InvalidArgumentException('$fields is array only');
95: }
96: }
97:
98: /**
99: * นับจำนวนเร็คคอร์ดของคอลัมน์ที่เลือก
100: *
101: * @assert ('id')->text() [==] 'COUNT(`id`)'
102: *
103: * @param string $column_name
104: * @param string|null $alias
105: * @param bool $distinct false (default) นับทุกคอลัมน์, true นับเฉพาะคอลัมน์ที่ไม่ซ้ำ
106: *
107: * @return \static
108: */
109: public static function COUNT($column_name = '*', $alias = null, $distinct = false)
110: {
111: $column_name = $column_name == '*' ? '*' : self::fieldName($column_name);
112: return self::create('COUNT('.($distinct ? 'DISTINCT ' : '').$column_name.')'.($alias ? " AS `$alias`" : ''));
113: }
114:
115: /**
116: * แยกวันที่ออกจากคอลัมน์ชนิด DATETIME
117: *
118: * @assert ('create_date')->text() [==] 'DATE(`create_date`)'
119: * @assert ('create_date', 'date')->text() [==] 'DATE(`create_date`) AS `date`'
120: *
121: * @param string $column_name
122: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
123: *
124: * @return \static
125: */
126: public static function DATE($column_name, $alias = null)
127: {
128: return self::create('DATE('.self::fieldName($column_name).')'.($alias ? " AS `$alias`" : ''));
129: }
130:
131: /**
132: * หาความแตกต่างระหว่างวัน (คืนค่าเป็นจำนวนวันที่แตกต่างกัน)
133: *
134: * @assert ('create_date', Sql::NOW())->text() [==] "DATEDIFF(`create_date`, NOW())"
135: * @assert ('2017-04-04', 'create_date')->text() [==] "DATEDIFF('2017-04-04', `create_date`)"
136: *
137: * @param string $column_name1
138: * @param string $column_name2
139: * @param string $alias
140: *
141: * @return \static
142: */
143: public static function DATEDIFF($column_name1, $column_name2, $alias = null)
144: {
145: return self::create('DATEDIFF('.self::fieldName($column_name1).', '.self::fieldName($column_name2).')'.($alias ? " AS `$alias`" : ''));
146: }
147:
148: /**
149: * จัดรูปแบบของวันที่ตอนแสดงผล
150: *
151: * @assert (Sql::NOW(), '%h:%i')->text() [==] "DATE_FORMAT(NOW(), '%h:%i')"
152: * @assert ('create_date', '%Y-%m-%d', 'today')->text() [==] "DATE_FORMAT(`create_date`, '%Y-%m-%d') AS `today`"
153: *
154: * @param string $column_name
155: * @param string $format
156: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
157: *
158: * @return \static
159: */
160: public static function DATE_FORMAT($column_name, $format, $alias = null)
161: {
162: return self::create('DATE_FORMAT('.self::fieldName($column_name).", '$format')".($alias ? " AS `$alias`" : ''));
163: }
164:
165: /**
166: * แยกวันที่ออกจากคอลัมน์ชนิด DATE DATETIME
167: *
168: * @assert ('date')->text() [==] 'DAY(`date`)'
169: * @assert ('date', 'd')->text() [==] 'DAY(`date`) AS `d`'
170: *
171: * @param string $column_name
172: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
173: *
174: * @return \static
175: */
176: public static function DAY($column_name, $alias = null)
177: {
178: return self::create('DAY('.self::fieldName($column_name).')'.($alias ? " AS `$alias`" : ''));
179: }
180:
181: /**
182: * คืนค่าข้ออมูลที่ไม่ซ้ำกัน
183: *
184: * @assert ('id')->text() [==] 'DISTINCT `id`'
185: *
186: * @param string $column_name
187: *
188: * @return \static
189: */
190: public static function DISTINCT($column_name)
191: {
192: return self::create('DISTINCT '.self::fieldName($column_name));
193: }
194:
195: /**
196: * จัดรูปแบบของคอลัมน์ตอนแสดงผล.
197: *
198: * @assert (Sql::NOW(), 'Y-m-d')->text() [==] "FORMAT(NOW(), 'Y-m-d')"
199: * @assert ('create_date', 'Y-m-d', 'today')->text() [==] "FORMAT(`create_date`, 'Y-m-d') AS `today`"
200: *
201: * @param string $column_name
202: * @param string $format
203: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
204: *
205: * @return \static
206: */
207: public static function FORMAT($column_name, $format, $alias = null)
208: {
209: return self::create('FORMAT('.self::fieldName($column_name).", '$format')".($alias ? " AS `$alias`" : ''));
210: }
211:
212: /**
213: * สร้างคำสั่ง GROUP_CONCAT
214: *
215: * @assert ('C.topic', 'topic', ', ')->text() [==] "GROUP_CONCAT(C.`topic` SEPARATOR ', ') AS `topic`"
216: *
217: * @param string $column_name
218: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
219: * @param string $separator ข้อความเชื่อมฟิลด์เข้าด้วยกัน ค่าเริมต้นคือ ,
220: * @param bool $distinct false (default) คืนค่ารายการที่ไม่ซ้ำ
221: * @param string|array $order เรียงลำดับ
222: *
223: * @return \self
224: */
225: public static function GROUP_CONCAT($column_name, $alias = null, $separator = ',', $distinct = false, $order = null)
226: {
227: if (!empty($order)) {
228: $orders = array();
229: if (is_array($order)) {
230: foreach ($order as $item) {
231: $orders[] = self::fieldName($item);
232: }
233: } else {
234: $orders[] = self::fieldName($order);
235: }
236: $order = empty($orders) ? '' : ' ORDER BY '.implode(',', $orders);
237: }
238: return self::create('GROUP_CONCAT('.($distinct ? 'DISTINCT ' : '').self::fieldName($column_name).$order." SEPARATOR '$separator')".($alias ? " AS `$alias`" : ''));
239: }
240:
241: /**
242: * แยกชั่วโมงออกจากคอลัมน์ชนิด DATETIME.
243: *
244: * @assert ('create_date')->text() [==] 'HOUR(`create_date`)'
245: * @assert ('create_date', 'date')->text() [==] 'HOUR(`create_date`) AS `date`'
246: *
247: * @param string $column_name
248: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
249: *
250: * @return \static
251: */
252: public static function HOUR($column_name, $alias = null)
253: {
254: return self::create('HOUR('.self::fieldName($column_name).')'.($alias ? " AS `$alias`" : ''));
255: }
256:
257: /**
258: * ฟังก์ชั่นสร้างคำสั่ง IFNULL
259: *
260: * @assert ('create_date', 'U.create_date')->text() [==] "IFNULL(`create_date`, U.`create_date`)"
261: * @assert ('create_date', 'U.create_date', 'test')->text() [==] "IFNULL(`create_date`, U.`create_date`) AS `test`"
262: *
263: * @param string $column_name1
264: * @param string $column_name2
265: * @param string|null $alias ถ้าระบุจะมีการเติม alias ให้กับคำสั่ง
266: *
267: * @return \self
268: */
269: public static function IFNULL($column_name1, $column_name2, $alias = null)
270: {
271: return self::create('IFNULL('.self::fieldName($column_name1).', '.self::fieldName($column_name2).')'.($alias ? ' AS `'.$alias.'`' : ''));
272: }
273:
274: /**
275: * ฟังก์ชั่นสร้างคำสั่ง IS NOT NULL
276: *
277: * @assert ('U.id')->text() [==] "U.`id` IS NOT NULL"
278: *
279: * @param string $column_name
280: *
281: * @return \self
282: */
283: public static function ISNOTNULL($column_name)
284: {
285: return self::create(self::fieldName($column_name).' IS NOT NULL');
286: }
287:
288: /**
289: * ฟังก์ชั่นสร้างคำสั่ง IS NULL
290: *
291: * @assert ('U.id')->text() [==] "U.`id` IS NULL"
292: *
293: * @param string $column_name
294: *
295: * @return \self
296: */
297: public static function ISNULL($column_name)
298: {
299: return self::create(self::fieldName($column_name).' IS NULL');
300: }
301:
302: /**
303: * หาค่าสูงสุด
304: *
305: * @assert ('id')->text() [==] 'MAX(`id`)'
306: *
307: * @param string $column_name
308: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
309: *
310: * @return \static
311: */
312: public static function MAX($column_name, $alias = null)
313: {
314: return self::create('MAX('.self::fieldName($column_name).')'.($alias ? " AS `$alias`" : ''));
315: }
316:
317: /**
318: * หาค่าต่ำสุด
319: *
320: * @assert ('id')->text() [==] 'MIN(`id`)'
321: *
322: * @param string $column_name
323: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
324: *
325: * @return \static
326: */
327: public static function MIN($column_name, $alias = null)
328: {
329: return self::create('MIN('.self::fieldName($column_name).')'.($alias ? " AS `$alias`" : ''));
330: }
331:
332: /**
333: * แยกนาทีออกจากคอลัมน์ชนิด DATETIME.
334: *
335: * @assert ('create_date')->text() [==] 'MINUTE(`create_date`)'
336: * @assert ('create_date', 'date')->text() [==] 'MINUTE(`create_date`) AS `date`'
337: *
338: * @param string $column_name
339: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
340: *
341: * @return \static
342: */
343: public static function MINUTE($column_name, $alias = null)
344: {
345: return self::create('MINUTE('.self::fieldName($column_name).')'.($alias ? " AS `$alias`" : ''));
346: }
347:
348: /**
349: * แยกเดือนออกจากคอลัมน์ชนิด DATE DATETIME
350: *
351: * @assert ('date')->text() [==] 'MONTH(`date`)'
352: * @assert ('date', 'm')->text() [==] 'MONTH(`date`) AS `m`'
353: *
354: * @param string $column_name
355: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
356: *
357: * @return \static
358: */
359: public static function MONTH($column_name, $alias = null)
360: {
361: return self::create('MONTH('.self::fieldName($column_name).')'.($alias ? " AS `$alias`" : ''));
362: }
363:
364: /**
365: * ฟังก์ชั่นสร้าง SQL สำหรับหาค่าสูงสุด + 1
366: * ใช้ในการหาค่า id ถัดไป
367: *
368: *
369: * @assert ('id', '`world`')->text() [==] '(1 + IFNULL((SELECT MAX(`id`) FROM `world` AS X), 0))'
370: * @assert ('id', '`world`', array(array('module_id', 'D.`id`')), 'next_id')->text() [==] '(1 + IFNULL((SELECT MAX(`id`) FROM `world` AS X WHERE `module_id` = D.`id`), 0)) AS `next_id`'
371: * @assert ('id', '`world`', array(array('module_id', 'D.`id`')), null)->text() [==] '(1 + IFNULL((SELECT MAX(`id`) FROM `world` AS X WHERE `module_id` = D.`id`), 0))'
372: *
373: * @param string $field ชื่อฟิลด์ที่ต้องการหาค่าสูงสุด
374: * @param string $table_name ชื่อตาราง
375: * @param mixed $condition (optional) query WHERE
376: * @param array $values (optional) แอเรย์สำหรับรับค่า value สำหรับการ bind
377: * @param string $alias (optional) ชื่อฟิลด์ที่ใช้คืนค่า ไม่ระบุ (null) หมายถึงไม่ต้องการชื่อฟิลด์
378: * @param string $operator (optional) เช่น AND หรือ OR
379: * @param string $id (optional )ชื่อฟิลด์ที่เป็น key
380: *
381: * @return \static
382: */
383: public static function NEXT($field, $table_name, $condition = null, $alias = null, $operator = 'AND', $id = 'id')
384: {
385: $obj = new static();
386: if (empty($condition)) {
387: $condition = '';
388: } else {
389: $condition = ' WHERE '.$obj->buildWhere($condition, $obj->values, $operator, $id);
390: }
391: $obj->sql = '(1 + IFNULL((SELECT MAX(`'.$field.'`) FROM '.$table_name.' AS X'.$condition.'), 0))';
392: if (isset($alias)) {
393: $obj->sql .= " AS `$alias`";
394: }
395:
396: return $obj;
397: }
398:
399: /**
400: * คืนค่าวันที่และเวลาปัจจุบัน
401: *
402: * @assert ()->text() [==] 'NOW()'
403: * @assert ('id')->text() [==] 'NOW() AS `id`'
404: *
405: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
406: *
407: * @return \static
408: */
409: public static function NOW($alias = null)
410: {
411: return self::create('NOW()'.($alias ? " AS `$alias`" : ''));
412: }
413:
414: /**
415: * ค้นหาข้อความ ไม่พบคืนค่า 0, ตัวแรกคือ 1
416: *
417: * @assert ('find', 'C.`topic`')->text() [==] "LOCATE('find', C.`topic`)"
418: *
419: * @param string $substr ข้อความที่ค้นหา ถ้าเป็นชื่อฟิลด์ต้องครอบด้วย ``
420: * @param string $str ข้อความต้นฉบับ ถ้าเป็นชื่อฟิลด์ต้องครอบด้วย ``
421: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
422: * @param int $pos ตำแหน่งเริ่มต้นค้นหา (default) หรือไม่ระบุ ค้นหาตั้งแต่ตัวแรก
423: *
424: * @return \self
425: */
426: public static function POSITION($substr, $str, $alias = null, $pos = 0)
427: {
428: $substr = strpos($substr, '`') === false ? "'$substr'" : $substr;
429: $str = strpos($str, '`') === false ? "'$str'" : $str;
430:
431: return self::create("LOCATE($substr, $str".(empty($pos) ? ')' : ", $pos)").($alias ? " AS `$alias`" : ''));
432: }
433:
434: /**
435: * สุ่มตัวเลข
436: *
437: * @assert ()->text() [==] 'RAND()'
438: * @assert ('id')->text() [==] 'RAND() AS `id`'
439: *
440: * @param string $column_name
441: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
442: *
443: * @return \static
444: */
445: public static function RAND($alias = null)
446: {
447: return self::create('RAND()'.($alias ? " AS `$alias`" : ''));
448: }
449:
450: /**
451: * แยกวินาทีออกจากคอลัมน์ชนิด DATETIME
452: *
453: * @assert ('create_date')->text() [==] 'SECOND(`create_date`)'
454: * @assert ('create_date', 'date')->text() [==] 'SECOND(`create_date`) AS `date`'
455: *
456: * @param string $column_name
457: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
458: *
459: * @return \static
460: */
461: public static function SECOND($column_name, $alias = null)
462: {
463: return self::create('SECOND('.self::fieldName($column_name).')'.($alias ? " AS `$alias`" : ''));
464: }
465:
466: /**
467: * ผลรวมของคอลัมน์ที่เลือก
468: *
469: * @assert ('id')->text() [==] 'SUM(`id`)'
470: * @assert ('table_name.`id`', 'id')->text() [==] 'SUM(`table_name`.`id`) AS `id`'
471: * @assert ('U.id', 'id', true)->text() [==] 'SUM(DISTINCT U.`id`) AS `id`'
472: * @assert ('U1.id', 'id', true)->text() [==] 'SUM(DISTINCT U1.`id`) AS `id`'
473: *
474: * @param string $column_name
475: * @param string|null $alias
476: * @param bool $distinct false (default) รวมทุกคอลัมน์, true รวมเฉพาะคอลัมน์ที่ไม่ซ้ำ
477: *
478: * @return \static
479: */
480: public static function SUM($column_name, $alias = '', $distinct = false)
481: {
482: return self::create('SUM('.($distinct ? 'DISTINCT ' : '').self::fieldName($column_name).')'.($alias ? " AS `$alias`" : ''));
483: }
484:
485: /**
486: * หาความแตกต่างระหว่างเวลา (คืนค่าเป็น H:m:i ที่แตกต่างกัน)
487: *
488: * @assert ('create_date', Sql::NOW())->text() [==] "TIMEDIFF(`create_date`, NOW())"
489: * @assert ('2017-04-04', 'create_date')->text() [==] "TIMEDIFF('2017-04-04', `create_date`)"
490: *
491: * @param string $column_name1
492: * @param string $column_name2
493: * @param string $alias
494: *
495: * @return \static
496: */
497: public static function TIMEDIFF($column_name1, $column_name2, $alias = null)
498: {
499: return self::create('TIMEDIFF('.self::fieldName($column_name1).', '.self::fieldName($column_name2).')'.($alias ? " AS `$alias`" : ''));
500: }
501:
502: /**
503: * หาความแตกต่างระหว่างเวลา (คืนค่าตามรูปแบบ $unit)
504: *
505: * @assert ('HOUR', 'create_date', Sql::NOW())->text() [==] "TIMESTAMPDIFF(HOUR, `create_date`, NOW())"
506: * @assert ('MONTH', '2017-04-04', 'create_date')->text() [==] "TIMESTAMPDIFF(MONTH, '2017-04-04', `create_date`)"
507: *
508: * @param string $unit FRAC_SECOND (microseconds), SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, or YEAR
509: * @param string $column_name1
510: * @param string $column_name2
511: * @param string $alias
512: *
513: * @return \static
514: */
515: public static function TIMESTAMPDIFF($unit, $column_name1, $column_name2, $alias = null)
516: {
517: return self::create('TIMESTAMPDIFF('.$unit.', '.self::fieldName($column_name1).', '.self::fieldName($column_name2).')'.($alias ? " AS `$alias`" : ''));
518: }
519:
520: /**
521: * สร้างคำสั่ง WHERE
522: *
523: * @assert WHERE(1)->text() [==] "`id` = 1"
524: * @assert WHERE('1')->text() [==] "`id` = '1'"
525: * @assert WHERE(0.1)->text() [==] "`id` = 0.1"
526: * @assert WHERE('ทดสอบ')->text() [==] "`id` = 'ทดสอบ'"
527: * @assert WHERE(null)->text() [==] "`id` = NULL"
528: * @assert WHERE(0x64656)->text() [==] "`id` = 411222"
529: * @assert WHERE('SELECT * FROM')->text() [==] "`id` = :id0"
530: * @assert WHERE(Sql::create('EXISTS SELECT FROM WHERE'))->text() [==] "EXISTS SELECT FROM WHERE"
531: * @assert WHERE(array('id', '=', 1))->text() [==] "`id` = 1"
532: * @assert WHERE(array('U.id', '2017-01-01 00:00:00'))->text() [==] "U.`id` = '2017-01-01 00:00:00'"
533: * @assert WHERE(array('id', 'IN', array(1, '2', null)))->text() [==] "`id` IN (1, '2', NULL)"
534: * @assert WHERE(array('id', 'SELECT * FROM'))->text() [==] "`id` = :id0"
535: * @assert WHERE(array('U.`id`', 'NOT IN', Sql::create('SELECT * FROM')))->text() [==] "U.`id` NOT IN SELECT * FROM"
536: * @assert WHERE(array(array('id', 'IN', array(1, '2', null))))->text() [==] "`id` IN (1, '2', NULL)"
537: * @assert WHERE(array(array('U.id', 1), array('U.id', '!=', '1')))->text() [==] "U.`id` = 1 AND U.`id` != '1'"
538: * @assert WHERE(array(array(Sql::MONTH('create_date'), 1), array(Sql::YEAR('create_date'), 1)))->text() [==] "MONTH(`create_date`) = 1 AND YEAR(`create_date`) = 1"
539: * @assert WHERE(array(array('id', array(1, 'a')), array('id', array('G.id', 'G.`id2`'))))->text() [==] "`id` IN (1, 'a') AND `id` IN (G.`id`, G.`id2`)"
540: * @assert WHERE(array(array('id', array('', 'th'))))->text() [==] "`id` IN ('', 'th')"
541: * @assert WHERE(array(Sql::YEAR('create_date'), Sql::YEAR('`create_date`')))->text() [==] "YEAR(`create_date`) = YEAR(`create_date`)"
542: * @assert WHERE(array('ip', 'NOT IN', array('', '192.168.1.2')))->text() [==] "`ip` NOT IN ('', '192.168.1.2')"
543: * @assert WHERE(array(1, 1))->text() [==] "1 = 1"
544: *
545: * @param mixed $condition
546: * @param string $operator (optional) เช่น AND หรือ OR
547: * @param string $id (optional )ชื่อฟิลด์ที่เป็น key
548: *
549: * @return \static
550: */
551: public static function WHERE($condition, $operator = 'AND', $id = 'id')
552: {
553: $obj = new static();
554: $obj->sql = $obj->buildWhere($condition, $obj->values, $operator, $id);
555: return $obj;
556: }
557:
558: /**
559: * แยกปีออกจากคอลัมน์ชนิด DATE DATETIME.
560: *
561: * @assert ('date')->text() [==] 'YEAR(`date`)'
562: * @assert ('date', 'y')->text() [==] 'YEAR(`date`) AS `y`'
563: *
564: * @param string $column_name
565: * @param string|null $alias ชื่อรองที่ต้องการ ถ้าไม่ระบุไม่มีชื่อรอง
566: *
567: * @return \static
568: */
569: public static function YEAR($column_name, $alias = null)
570: {
571: return self::create('YEAR('.self::fieldName($column_name).')'.($alias ? " AS `$alias`" : ''));
572: }
573:
574: /**
575: * class constructer
576: *
577: * @param string $sql
578: */
579: public function __construct($sql = null)
580: {
581: $this->sql = $sql;
582: $this->values = array();
583: }
584:
585: /**
586: * สร้าง Object Sql
587: *
588: * @param string $sql
589: */
590: public static function create($sql)
591: {
592: return new static($sql);
593: }
594:
595: /**
596: * ใส่ `` ครอบชื่อคอลัมน์
597: * ชื่อคอลัมน์ต้องเป็น ภาษาอังกฤษ ตัวเลข และ _ เท่านั้น
598: * ถ้ามีอักขระอื่นนอกจากนี้ คืนค่า ข้อความที่ส่งมา ครอบด้วย ''
599: *
600: * @assert ('U.id') [==] 'U.`id`'
601: * @assert ('U1.id') [==] 'U1.`id`'
602: * @assert ('U1.id DESC') [==] 'U1.`id` DESC'
603: * @assert ('field_name') [==] '`field_name`'
604: * @assert ('table_name.field_name') [==] '`table_name`.`field_name`'
605: * @assert ('`table_name`.`field_name`') [==] '`table_name`.`field_name`'
606: * @assert ('table_name.`field_name`') [==] '`table_name`.`field_name`'
607: * @assert ('`table_name`.field_name') [==] '`table_name`.`field_name`'
608: * @assert ('`table_name`.field_name ASC') [==] '`table_name`.`field_name` ASC'
609: * @assert ('0x64656') [==] "`0x64656`"
610: * @assert (0x64656) [==] 411222
611: * @assert ('DATE(day)') [==] "'DATE(day)'"
612: * @assert ('DROP table') [==] "'DROP table'"
613: * @assert (array()) [throws] InvalidArgumentException
614: *
615: * @param string $column_name
616: *
617: * @throws \InvalidArgumentException ถ้ารูปแบบของ $column_name ไม่ถูกต้อง
618: *
619: * @return string
620: */
621: public static function fieldName($column_name)
622: {
623: if ($column_name instanceof self) {
624: // Sql
625: return $column_name->text();
626: } elseif ($column_name instanceof QueryBuilder) {
627: // QueryBuilder
628: return '('.$column_name->text().')';
629: } elseif (is_string($column_name)) {
630: if (preg_match('/^(([A-Z]{1,1}[0-9]{0,1})\.)?`?([a-zA-Z0-9_]+)`?(\s(ASC|DESC|asc|desc))?$/', $column_name, $match)) {
631: // U.id, U.`id`, U1.id, U1.`id`, field_name, `field_name`
632: return ($match[2] == '' ? "`$match[3]`" : "$match[2].`$match[3]`").(empty($match[5]) ? '' : $match[4]);
633: } elseif (preg_match('/^`?([a-zA-Z0-9_]+)`?\.`?([a-zA-Z0-9_]+)`?(\s(ASC|DESC|asc|desc))?$/', $column_name, $match)) {
634: // table_name.field_name, table_name.`field_name`, `table_name`.field_name, `table_name`.`field_name`
635: return ("`$match[1]`.`$match[2]`").(empty($match[4]) ? '' : $match[3]);
636: } else {
637: // อื่นๆ คืนค่าเป็นข้อความภายใต้เครื่องหมาย ' (อัญประกาศเดี่ยว)
638: return "'$column_name'";
639: }
640: } elseif (is_numeric($column_name)) {
641: // ตัวเลขเท่านั้น
642: return $column_name;
643: }
644: throw new \InvalidArgumentException('Invalid arguments in fieldName');
645: }
646:
647: /**
648: * คืนค่าแอเร์ยเก็บพารามิเตอร์สำหรับการ bind รวมกับ $values
649: *
650: * @param array $values
651: *
652: * @return array
653: */
654: public function getValues($values = array())
655: {
656: if (empty($values)) {
657: return $this->values;
658: }
659: foreach ($this->values as $key => $value) {
660: $values[$key] = $value;
661: }
662:
663: return $values;
664: }
665:
666: /**
667: * แปลงค่า Value สำหรับใช้ใน query
668: *
669: * @assert ('id', 'ทดสอบ', $array) [==] "'ทดสอบ'"
670: * @assert ('id', 'test', $array) [==] "'test'"
671: * @assert ('id', 'abcde012345', $array) [==] "'abcde012345'"
672: * @assert ('id', 123456, $array) [==] 123456
673: * @assert ('id', 0.1, $array) [==] 0.1
674: * @assert ('id', null, $array) [==] 'NULL'
675: * @assert ('id', 'U.id', $array) [==] "U.`id`"
676: * @assert ('id', 'U.`id`', $array) [==] 'U.`id`'
677: * @assert ('id', 'domain.tld', $array) [==] "'domain.tld'"
678: * @assert ('id', 'table_name.`id`', $array) [==] '`table_name`.`id`'
679: * @assert ('id', '`table_name`.id', $array) [==] '`table_name`.`id`'
680: * @assert ('id', '`table_name`.`id`', $array) [==] '`table_name`.`id`'
681: * @assert ('id', 'INSERT INTO', $array) [==] ':id0'
682: * @assert ('id', array(1, '2', null), $array) [==] "(1, '2', NULL)"
683: * @assert ('id', '0x64656', $array) [==] ':id0'
684: * @assert ('id', 0x64656, $array) [==] 411222
685: * @assert ('`table_name`.`id`', '0x64656', $array) [==] ':tablenameid0'
686: * @assert ('U1.`id`', '0x64656', $array) [==] ':u1id0'
687: * @assert ('U.id', '0x64656', $array) [==] ':uid0'
688: *
689: * @param mixed $value
690: * @param array $values แอเรย์สำหรับรับค่า value สำหรับการ bind
691: *
692: * @throws \InvalidArgumentException ถ้ารูปแบบของ $value ไม่ถูกต้อง
693: *
694: * @return string
695: */
696: public static function quoteValue($column_name, $value, &$values)
697: {
698: if (is_array($value)) {
699: $qs = array();
700: foreach ($value as $v) {
701: $qs[] = self::quoteValue($column_name, $v, $values);
702: }
703: $sql = '('.implode(', ', $qs).')';
704: } elseif ($value === null) {
705: $sql = 'NULL';
706: } elseif ($value === '') {
707: $sql = "''";
708: } elseif (is_string($value)) {
709: if (preg_match('/^([0-9\s\r\n\t\.\_\-:]+)$/', $value)) {
710: // ตัวเลข จำนวนเงิน เบอร์โทร วันที่
711: $sql = "'$value'";
712: } elseif (preg_match('/0x[0-9]+/is', $value)) {
713: // 0x
714: $sql = ':'.strtolower(preg_replace('/[`\.\s\-_]+/', '', $column_name)).count($values);
715: $values[$sql] = $value;
716: } else {
717: if (preg_match('/^(([A-Z]{1,1}[0-9]{0,1})|`([a-zA-Z0-9_]+)`)\.`?([a-zA-Z0-9_]+)`?$/', $value, $match)) {
718: // U.id U.`id` U1.id U1.`id` `table_name`.`module_id`
719: $sql = $match[3] == '' ? "$match[2].`$match[4]`" : "`$match[3]`.`$match[4]`";
720: } elseif (preg_match('/^([a-zA-Z0-9_]+)\.`([a-zA-Z0-9_]+)`$/', $value, $match)) {
721: // table_name.`module_id`
722: $sql = "`$match[1]`.`$match[2]`";
723: } elseif (!preg_match('/[\s\r\n\t`;\(\)\*\=<>\/\'"]+/s', $value) && !preg_match('/(UNION|INSERT|DELETE|TRUNCATE|DROP|0x[0-9]+)/is', $value)) {
724: // ข้อความที่ไม่มีช่องว่างหรือรหัสที่อาจเป็น SQL
725: $sql = "'$value'";
726: } else {
727: $sql = ':'.strtolower(preg_replace('/[`\.\s\-_]+/', '', $column_name)).count($values);
728: $values[$sql] = $value;
729: }
730: }
731: } elseif (is_numeric($value)) {
732: // ตัวเลขเท่านั้น
733: $sql = $value;
734: } elseif ($value instanceof self) {
735: // Sql
736: $sql = $value->text($column_name);
737: $values = $value->getValues($values);
738: } elseif ($value instanceof QueryBuilder) {
739: // QueryBuilder
740: $sql = '('.$value->text().')';
741: $values = $value->getValues($values);
742: } else {
743: throw new \InvalidArgumentException('Invalid arguments in quoteValue');
744: }
745: return $sql;
746: }
747:
748: /**
749: * ฟังก์ชั่นสำหรับรับค่าเป็นสตริงค์เท่านั้น
750: * ผลลัพท์จะถูกครอบด้วย '' (ฟันหนู)
751: *
752: * @param string $value
753: *
754: * @return \self
755: */
756: public static function strValue($value)
757: {
758: return self::create("'$value'");
759: }
760:
761: /**
762: * คืนค่าคำสั่ง SQL เป็น string
763: * ถ้า $sql เป็น null จะคืนค่า :$key ใช้สำหรับการ bind
764: *
765: * @param string $key
766: *
767: * @return string
768: */
769: public function text($key = null)
770: {
771: if ($this->sql === null) {
772: if (is_string($key) && $key != '') {
773: return ':'.preg_replace('/[\.`]/', '', strtolower($key));
774: } else {
775: throw new \InvalidArgumentException('$key must be a non-empty string');
776: }
777: } else {
778: return $this->sql;
779: }
780: }
781:
782: /**
783: * create SQL WHERE command
784: *
785: * @param mixed $condition
786: * @param array $values แอเรย์สำหรับรับค่า value สำหรับการ bind
787: * @param string $operator เช่น AND หรือ OR
788: * @param string $id ชื่อฟิลด์ที่เป็น key
789: *
790: * @return string
791: */
792: private function buildWhere($condition, &$values, $operator, $id)
793: {
794: if (is_array($condition)) {
795: $qs = array();
796: if (is_array($condition[0])) {
797: foreach ($condition as $item) {
798: if ($item instanceof QueryBuilder) {
799: $qs[] = '('.$item->text().')';
800: $values = $item->getValues($values);
801: } elseif ($item instanceof self) {
802: $qs[] = $item->text();
803: $values = $item->getValues($values);
804: } else {
805: $qs[] = $this->buildWhere($item, $values, $operator, $id);
806: }
807: }
808: $sql = implode(' '.$operator.' ', $qs);
809: } else {
810: if ($condition[0] instanceof QueryBuilder) {
811: $key = '('.$condition[0]->text().')';
812: $values = $condition[0]->getValues($values);
813: } elseif ($condition[0] instanceof self) {
814: $key = $condition[0]->text();
815: $values = $condition[0]->getValues($values);
816: } else {
817: $key = self::fieldName($condition[0]);
818: }
819: if (count($condition) == 2) {
820: if ($condition[1] instanceof QueryBuilder) {
821: $operator = '=';
822: $value = '('.$condition[1]->text().')';
823: $values = $condition[1]->getValues($values);
824: } elseif ($condition[1] instanceof self) {
825: $operator = '=';
826: $value = $condition[1]->text();
827: $values = $condition[1]->getValues($values);
828: } else {
829: $operator = '=';
830: if (is_array($condition[1]) && $operator == '=') {
831: $operator = 'IN';
832: }
833: $value = self::quoteValue($key, $condition[1], $values);
834: }
835: } elseif (isset($condition[2])) {
836: if ($condition[2] instanceof QueryBuilder) {
837: $operator = trim($condition[1]);
838: $value = '('.$condition[2]->text().')';
839: $values = $condition[2]->getValues($values);
840: } elseif ($condition[2] instanceof self) {
841: $operator = trim($condition[1]);
842: $value = $condition[2]->text();
843: $values = $condition[2]->getValues($values);
844: } else {
845: $operator = trim($condition[1]);
846: if (is_array($condition[2]) && $operator == '=') {
847: $operator = 'IN';
848: }
849: $value = self::quoteValue($key, $condition[2], $values);
850: }
851: }
852: if (isset($value)) {
853: $sql = $key.' '.$operator.' '.$value;
854: } else {
855: $sql = $key;
856: }
857: }
858: } elseif ($condition instanceof QueryBuilder) {
859: $sql = '('.$condition->text().')';
860: $values = $condition->getValues($values);
861: } elseif ($condition instanceof self) {
862: $sql = $condition->text();
863: $values = $condition->getValues($values);
864: } else {
865: // ใช้ $id เป็น column_name
866: $sql = self::fieldName($id).' = '.self::quoteValue($id, $condition, $values);
867: }
868: return $sql;
869: }
870: }
871: