1: <?php
2: /**
3: * @filesource Kotchasan/Database/Driver.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: use Kotchasan\ArrayTool;
14: use Kotchasan\Cache\CacheItem as Item;
15: use Kotchasan\Database\DbCache as Cache;
16: use Kotchasan\Log\Logger;
17: use Kotchasan\Text;
18:
19: /**
20: * Kotchasan Database driver Class (base class)
21: *
22: * @author Goragod Wiriya <admin@goragod.com>
23: *
24: * @since 1.0
25: */
26: abstract class Driver extends Query
27: {
28: /**
29: * cache class
30: *
31: * @var Cache
32: */
33: protected $cache;
34: /**
35: * Cacheitem
36: *
37: * @var Item
38: */
39: protected $cache_item;
40: /**
41: * database connection
42: *
43: * @var object
44: */
45: protected $connection = null;
46: /**
47: * database error message
48: *
49: * @var string
50: */
51: protected $error_message = '';
52: /**
53: * นับจำนวนการ query
54: *
55: * @var int
56: */
57: protected static $query_count = 0;
58: /**
59: * เก็บ Object ที่เป็นผลลัพท์จากการ query
60: *
61: * @var resource|object
62: */
63: protected $result_id;
64: /**
65: * ตัวแปรเก็บ query สำหรับการ execute
66: *
67: * @var array
68: */
69: protected $sqls;
70:
71: /**
72: * Class constructor
73: */
74: public function __construct()
75: {
76: $this->cache = Cache::create();
77: }
78:
79: /**
80: * อ่านสถานะของแคช
81: * 0 ไม่ใช้แคช
82: * 1 โหลดและบันทึกแคชอัตโนมัติ
83: * 2 โหลดข้อมูลจากแคชได้ แต่ไม่บันทึกแคชอัตโนมัติ
84: *
85: * @return int
86: */
87: public function cacheGetAction()
88: {
89: return $this->cache->getAction();
90: }
91:
92: /**
93: * เปิดการใช้งานแคช
94: * จะมีการตรวจสอบจากแคชก่อนการสอบถามข้อมูล
95: *
96: * @param bool $auto_save (options) true (default) บันทึกผลลัพท์อัตโนมัติ, false ต้องบันทึกแคชเอง
97: *
98: * @return \static
99: */
100: public function cacheOn($auto_save = true)
101: {
102: $this->cache->cacheOn($auto_save);
103: return $this;
104: }
105:
106: /**
107: * ฟังก์ชั่นบันทึก Cache
108: * สำเร็จคืนค่า true ไม่สำเร็จคืนค่า false
109: *
110: * @param array $datas ข้อมูลที่จะบันทึก
111: *
112: * @return bool
113: */
114: public function cacheSave($datas)
115: {
116: if ($this->cache_item instanceof Item) {
117: return $this->cache->save($this->cache_item, $datas);
118: }
119: return false;
120: }
121:
122: /**
123: * close database
124: */
125: public function close()
126: {
127: $this->connection = null;
128: }
129:
130: /**
131: * ฟังก์ชั่นอ่านค่า resource ID ของการเชื่อมต่อปัจจุบัน
132: *
133: * @return resource
134: */
135: public function connection()
136: {
137: return $this->connection;
138: }
139:
140: /**
141: * ฟังก์ชั่นสร้าง query builder
142: *
143: * @return \Kotchasan\Database\QueryBuilder
144: */
145: public function createQuery()
146: {
147: return new QueryBuilder($this);
148: }
149:
150: /**
151: * ฟังก์ชั่นประมวลผลคำสั่ง SQL สำหรับสอบถามข้อมูล คืนค่าผลลัพท์เป็นแอเรย์ของข้อมูลที่ตรงตามเงื่อนไข
152: * คืนค่าผลการทำงานเป็น record ของข้อมูลทั้งหมดที่ตรงตามเงื่อนไข ไม่พบคืนค่าแอเรย์ว่าง
153: *
154: *
155: * @param string $sql query string
156: * @param bool $toArray default false คืนค่าผลลัทเป็น Object, true คืนค่าเป็น Array
157: * @param array $values ถ้าระบุตัวแปรนี้จะเป็นการบังคับใช้คำสั่ง prepare แทน query
158: *
159: * @return array
160: */
161: public function customQuery($sql, $toArray = false, $values = array())
162: {
163: $result = $this->doCustomQuery($sql, $values);
164: if ($result && !$toArray) {
165: foreach ($result as $i => $item) {
166: $result[$i] = (object) $item;
167: }
168: }
169: return $result;
170: }
171:
172: /**
173: * ฟังก์ชั่นตรวจสอบว่ามี database หรือไม่
174: * คืนค่า true หากมีฐานข้อมูลนี้อยู่ ไม่พบคืนค่า false
175: *
176: * @param string $database ชื่อฐานข้อมูล
177: *
178: * @return bool
179: */
180: public function databaseExists($database)
181: {
182: $search = $this->doCustomQuery("SELECT 1 FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='$database'");
183: return $search && count($search) == 1 ? true : false;
184: }
185:
186: /**
187: * ฟังก์ชั่นลบ record
188: * สำเร็จคืนค่าจำนวนแถวที่มีผล ไม่สำเร็จคืนค่า false
189: *
190: * @param string $table_name ชื่อตาราง
191: * @param mixed $condition query WHERE
192: * @param int $limit จำนวนรายการที่ต้องการลบ 1 (default) รายการแรกที่เจอ, หมายถึงลบทุกรายการ
193: * @param string $operator AND (default) หรือ OR
194: *
195: * @return int|bool
196: */
197: public function delete($table_name, $condition, $limit = 1, $operator = 'AND')
198: {
199: $condition = $this->buildWhere($condition, $operator);
200: if (is_array($condition)) {
201: $values = $condition[1];
202: $condition = $condition[0];
203: } else {
204: $values = array();
205: }
206: $sql = 'DELETE FROM '.$table_name.' WHERE '.$condition;
207: if (is_int($limit) && $limit > 0) {
208: $sql .= ' LIMIT '.$limit;
209: }
210: return $this->doQuery($sql, $values);
211: }
212:
213: /**
214: * ฟังก์ชั่นลบข้อมูลทั้งหมดในตาราง
215: * คืนค่า true ถ้าสำเร็จ.
216: *
217: * @param string $table_name table name
218: *
219: * @return bool
220: */
221: public function emptyTable($table_name)
222: {
223: return $this->query("TRUNCATE TABLE $table_name") === false ? false : true;
224: }
225:
226: /**
227: * ฟังก์ชั่นประมวลผลคำสั่ง SQL จาก query builder
228: *
229: * @param array $sqls
230: * @param array $values ถ้าระบุตัวแปรนี้จะเป็นการบังคับใช้คำสั่ง prepare แทน query
231: * @param bool $debugger แสดงผล Query
232: *
233: * @return mixed
234: */
235: public function execQuery($sqls, $values = array(), $debugger = false)
236: {
237: $sql = $this->makeQuery($sqls);
238: if (isset($sqls['values'])) {
239: $values = ArrayTool::replace($sqls['values'], $values);
240: }
241: if ($debugger) {
242: echo '<pre>';
243: echo $sql;
244: if (!empty($values)) {
245: echo var_export($values, true);
246: }
247: echo '</pre>';
248: }
249: if ($sqls['function'] == 'customQuery') {
250: $result = $this->customQuery($sql, true, $values);
251: } else {
252: $result = $this->query($sql, $values);
253: }
254: return $result;
255: }
256:
257: /**
258: * จำนวนฟิลด์ทั้งหมดในผลลัพท์จากการ query
259: *
260: * @return int
261: */
262: abstract public function fieldCount();
263:
264: /**
265: * ตรวจสอบคอลัมน์ของตารางว่ามีหรือไม่
266: * คืนค่า true ถ้ามี คืนค่า false ถ้าไม่มี.
267: *
268: * @param string $table_name ชื่อตาราง
269: * @param string $column_name ชื่อคอลัมน์
270: *
271: * @return bool
272: */
273: public function fieldExists($table_name, $column_name)
274: {
275: $result = $this->customQuery("SHOW COLUMNS FROM `$table_name` LIKE '$column_name'");
276: return empty($result) ? false : true;
277: }
278:
279: /**
280: * ฟังก์ชั่น query ข้อมูล คืนค่าข้อมูลทุกรายการที่ตรงตามเงื่อนไข
281: * คืนค่า แอเรย์ของ object ไม่พบคืนค่าแอรย์ว่าง.
282: *
283: * @param string $table_name ชื่อตาราง
284: * @param mixed $condition query WHERE
285: * @param array $sort เรียงลำดับ
286: *
287: * @return array
288: */
289: public function find($table_name, $condition, $sort = array())
290: {
291: $result = array();
292: foreach ($this->select($table_name, $condition, $sort) as $item) {
293: $result[] = (object) $item;
294: }
295: return $result;
296: }
297:
298: /**
299: * ฟังก์ชั่น query ข้อมูล คืนค่าข้อมูลรายการเดียว
300: * คืนค่า object ของข้อมูล ไม่พบคืนค่า false
301: *
302: * @param string $table_name ชื่อตาราง
303: * @param mixed $condition query WHERE
304: *
305: * @return object|bool
306: */
307: public function first($table_name, $condition)
308: {
309: $result = $this->select($table_name, $condition, array(), 1);
310: return count($result) == 1 ? (object) $result[0] : false;
311: }
312:
313: /**
314: * คืนค่าข้อความผิดพลาดของฐานข้อมูล
315: *
316: * @return string
317: */
318: public function getError()
319: {
320: return $this->error_message;
321: }
322:
323: /**
324: * รายชื่อฟิลด์ทั้งหมดจากผลัพท์จองการ query
325: *
326: * @return array
327: */
328: abstract public function getFields();
329:
330: /**
331: * ฟังก์ชั่นคืนค่า ID ล่าสุดของตาราง + 1
332: * ใช้สำหรับอ่าน ID ถัดไปของตาราง (Auto Increment)
333: *
334: * @param string $table_name ชื่อตาราง
335: * @param string $primary_key ชื่อคอลัมน์ที่ต้องการอ่าน
336: *
337: * @return int
338: */
339: public function getNextId($table_name, $primary_key = 'id')
340: {
341: $result = $this->doCustomQuery("SELECT MAX(`$primary_key`) AS `Auto_increment` FROM `$table_name`");
342: return (int) $result[0]['Auto_increment'] + 1;
343: }
344:
345: /**
346: * ตรวจสอบว่ามี $index ในตารางหรือไม่
347: * คืนค่า true ถ้ามี คืนค่า false ถ้าไม่มี
348: *
349: * @param string $database_name
350: * @param string $table_name
351: * @param string $index
352: *
353: * @return bool
354: */
355: public function indexExists($database_name, $table_name, $index)
356: {
357: $result = $this->customQuery("SELECT * FROM information_schema.statistics WHERE table_schema='$database_name' AND table_name = '$table_name' AND column_name = '$index'");
358: return empty($result) ? false : true;
359: }
360:
361: /**
362: * ฟังก์ชั่นเพิ่มข้อมูลใหม่ลงในตาราง
363: * สำเร็จ คืนค่า id ที่เพิ่ม ผิดพลาด คืนค่า false
364: *
365: * @param string $table_name ชื่อตาราง
366: * @param array $save ข้อมูลที่ต้องการบันทึก
367: *
368: * @return int|bool
369: */
370: abstract public function insert($table_name, $save);
371:
372: /**
373: * ฟังก์ชั่นเพิ่มข้อมูลใหม่ลงในตาราง
374: * ถ้ามีข้อมูลเดิมอยู่แล้วจะเป็นการอัปเดต
375: * (ข้อมูลเดิมตาม KEY ที่เป็น UNIQUE)
376: * insert คืนค่า id ที่เพิ่ม
377: * update คืนค่า 0
378: * ผิดพลาด คืนค่า null
379: *
380: * @param string $table_name ชื่อตาราง
381: * @param array|object $save ข้อมูลที่ต้องการบันทึก รูปแบบ array('key1'=>'value1', 'key2'=>'value2', ...)
382: *
383: * @return int|null
384: */
385: abstract public function insertOrUpdate($table_name, $save);
386:
387: /**
388: * ฟังก์ชั่นสร้างคำสั่ง sql query
389: * คืนค่า sql command
390: *
391: * @param array $sqls คำสั่ง sql จาก query builder
392: *
393: * @return string
394: */
395: abstract public function makeQuery($sqls);
396:
397: /**
398: * ประมวลผลคำสั่ง SQL สำหรับสอบถามข้อมูล คืนค่าผลลัพท์เป็นแอเรย์ของข้อมูลที่ตรงตามเงื่อนไข
399: * คืนค่าผลการทำงานเป็น record ของข้อมูลทั้งหมดที่ตรงตามเงื่อนไข หรือคืนค่า false หามีข้อผิดพลาด.
400: *
401: * @param string $sql query string
402: * @param array $values ถ้าระบุตัวแปรนี้จะเป็นการบังคับใช้คำสั่ง prepare แทน query
403: *
404: * @return array|bool
405: */
406: abstract protected function doCustomQuery($sql, $values = array());
407:
408: /**
409: * ประมวลผลคำสั่ง SQL ที่ไม่ต้องการผลลัพท์ เช่น CREATE INSERT UPDATE
410: * สำเร็จคืนค่าจำนวนแถวที่มีผล ไม่สำเร็จคืนค่า false.
411: *
412: * @param string $sql
413: * @param array $values ถ้าระบุตัวแปรนี้จะเป็นการบังคับใช้คำสั่ง prepare แทน query
414: *
415: * @return int|bool
416: */
417: abstract protected function doQuery($sql, $values = array());
418:
419: /**
420: * ปรับปรุงตาราง
421: * คืนค่า true ถ้าสำเร็จ
422: *
423: * @param string $table_name table name
424: *
425: * @return bool
426: */
427: public function optimizeTable($table_name)
428: {
429: return $this->query("OPTIMIZE TABLE $table_name") === false ? false : true;
430: }
431:
432: /**
433: * ฟังก์ชั่นประมวลผลคำสั่ง SQL ที่ไม่ต้องการผลลัพท์ เช่น CREATE INSERT UPDATE
434: * สำเร็จคืนค่า true ไม่สำเร็จคืนค่า false
435: *
436: * @param string $sql
437: * @param array $values ถ้าระบุตัวแปรนี้จะเป็นการบังคับใช้คำสั่ง prepare แทน query
438: *
439: * @return bool
440: */
441: public function query($sql, $values = array())
442: {
443: return $this->doQuery($sql, $values);
444: }
445:
446: /**
447: * ฟังก์ชั่นอ่านจำนวน query ทั้งหมดที่ทำงาน.
448: *
449: * @return int
450: */
451: public static function queryCount()
452: {
453: return self::$query_count;
454: }
455:
456: /**
457: * ซ่อมแซมตาราง
458: * คืนค่า true ถ้าสำเร็จ.
459: *
460: * @param string $table_name table name
461: *
462: * @return bool
463: */
464: public function repairTable($table_name)
465: {
466: return $this->query("REPAIR TABLE $table_name") === false ? false : true;
467: }
468:
469: /**
470: * เรียกดูข้อมูล
471: * คืนค่าผลลัพท์ในรูป array ถ้าไม่สำเร็จ คืนค่าแอเรย์ว่าง.
472: *
473: * @param string $table_name ชื่อตาราง
474: * @param mixed $condition query WHERE
475: * @param array $sort เรียงลำดับ
476: * @param int $limit จำนวนข้อมูลที่ต้องการ
477: *
478: * @return array
479: */
480: abstract public function select($table_name, $condition, $sort = array(), $limit = 0);
481:
482: /**
483: * เลือกฐานข้อมูล
484: * คืนค่า false หากไม่สำเร็จ.
485: *
486: * @param string $database
487: *
488: * @return bool
489: */
490: abstract public function selectDB($database);
491:
492: /**
493: * ฟังก์ชั่นตรวจสอบว่ามีตาราง หรือไม่
494: * คืนค่า true หากมีตารางนี้อยู่ ไม่พบคืนค่า false.
495: *
496: * @param string $table_name ชื่อตาราง
497: *
498: * @return bool
499: */
500: public function tableExists($table_name)
501: {
502: $result = $this->doCustomQuery("SHOW TABLES LIKE '$table_name'");
503: return empty($result) ? false : true;
504: }
505:
506: /**
507: * ฟังก์ชั่นแก้ไขข้อมูล
508: * สำเร็จ คืนค่า true, ผิดพลาด คืนค่า false.
509: *
510: * @param string $table_name ชื่อตาราง
511: * @param mixed $condition query WHERE
512: * @param array $save ข้อมูลที่ต้องการบันทึก รูปแบบ array('key1'=>'value1', 'key2'=>'value2', ...)
513: *
514: * @return bool
515: */
516: abstract public function update($table_name, $condition, $save);
517:
518: /**
519: * อัปเดตข้อมูลทุก record
520: * สำเร็จ คืนค่า true, ผิดพลาด คืนค่า false.
521: *
522: * @param string $table_name table name
523: * @param array $save ข้อมูลที่ต้องการบันทึก array('key1'=>'value1', 'key2'=>'value2', ...)
524: *
525: * @return bool
526: */
527: public function updateAll($table_name, $save)
528: {
529: return $this->update($table_name, array(1, 1), $save);
530: }
531:
532: /**
533: * ฟังก์ชั่นบันทึกการ query sql.
534: *
535: * @param string $type
536: * @param string $sql
537: * @param array $values (options)
538: */
539: protected function log($type, $sql, $values = array())
540: {
541: if (DB_LOG == true) {
542: $datas = array('<b>'.$type.' :</b> '.Text::replace($sql, $values));
543: foreach (debug_backtrace() as $a => $item) {
544: if (isset($item['file']) && isset($item['line'])) {
545: if ($item['function'] == 'all' || $item['function'] == 'first' || $item['function'] == 'count' || $item['function'] == 'save' || $item['function'] == 'find' || $item['function'] == 'execute') {
546: $datas[] = '<br>['.$a.'] <b>'.$item['function'].'</b> in <b>'.$item['file'].'</b> line <b>'.$item['line'].'</b>';
547: break;
548: }
549: }
550: }
551: // บันทึก log
552: Logger::create()->info(implode('', $datas));
553: }
554: }
555: }
556: