Overview

Namespaces

  • Kotchasan
    • Cache
    • Database
    • Http
    • Log
    • Orm
  • None
  • PHP
  • Psr
    • Cache
    • Http
      • Message
    • Log

Classes

  • ArrayIterator
  • DateInterval
  • DOMNode
  • Kotchasan
  • Kotchasan\Accordion
  • Kotchasan\ApiController
  • Kotchasan\ArrayTool
  • Kotchasan\Cache\ApcCache
  • Kotchasan\Cache\Cache
  • Kotchasan\Cache\CacheItem
  • Kotchasan\Cache\FileCache
  • Kotchasan\CKEditor
  • Kotchasan\Collection
  • Kotchasan\Config
  • Kotchasan\Controller
  • Kotchasan\Country
  • Kotchasan\Csv
  • Kotchasan\Curl
  • Kotchasan\Currency
  • Kotchasan\Database
  • Kotchasan\Database\Db
  • Kotchasan\Database\DbCache
  • Kotchasan\Database\Driver
  • Kotchasan\Database\PdoMysqlDriver
  • Kotchasan\Database\Query
  • Kotchasan\Database\QueryBuilder
  • Kotchasan\Database\Schema
  • Kotchasan\Database\Sql
  • Kotchasan\DataTable
  • Kotchasan\Date
  • Kotchasan\DOMNode
  • Kotchasan\DOMParser
  • Kotchasan\Email
  • Kotchasan\File
  • Kotchasan\Files
  • Kotchasan\Form
  • Kotchasan\Grid
  • Kotchasan\Html
  • Kotchasan\Htmldoc
  • Kotchasan\HtmlTable
  • Kotchasan\Http\AbstractMessage
  • Kotchasan\Http\AbstractRequest
  • Kotchasan\Http\Message
  • Kotchasan\Http\NotFound
  • Kotchasan\Http\Request
  • Kotchasan\Http\Response
  • Kotchasan\Http\Stream
  • Kotchasan\Http\UploadedFile
  • Kotchasan\Http\Uri
  • Kotchasan\Image
  • Kotchasan\InputItem
  • Kotchasan\Inputs
  • Kotchasan\KBase
  • Kotchasan\Language
  • Kotchasan\ListItem
  • Kotchasan\Log\AbstractLogger
  • Kotchasan\Log\Logger
  • Kotchasan\Login
  • Kotchasan\Menu
  • Kotchasan\Mime
  • Kotchasan\Model
  • Kotchasan\Number
  • Kotchasan\ObjectTool
  • Kotchasan\Orm\Field
  • Kotchasan\Orm\Recordset
  • Kotchasan\Password
  • Kotchasan\Pdf
  • Kotchasan\Province
  • Kotchasan\Router
  • Kotchasan\Session
  • Kotchasan\Singleton
  • Kotchasan\Tab
  • Kotchasan\TableRow
  • Kotchasan\Template
  • Kotchasan\Text
  • Kotchasan\Validator
  • Kotchasan\View
  • PHPMailer
  • Psr\Log\AbstractLogger
  • Psr\Log\LogLevel
  • Psr\Log\NullLogger
  • SMTP

Interfaces

  • ArrayAccess
  • Countable
  • DateTimeInterface
  • Iterator
  • IteratorAggregate
  • Psr\Cache\CacheItemInterface
  • Psr\Cache\CacheItemPoolInterface
  • Psr\Http\Message\MessageInterface
  • Psr\Http\Message\RequestInterface
  • Psr\Http\Message\ResponseInterface
  • Psr\Http\Message\ServerRequestInterface
  • Psr\Http\Message\StreamInterface
  • Psr\Http\Message\UploadedFileInterface
  • Psr\Http\Message\UriInterface
  • Psr\Log\LoggerAwareInterface
  • Psr\Log\LoggerInterface
  • SeekableIterator
  • Serializable
  • Traversable

Traits

  • Psr\Log\LoggerTrait

Exceptions

  • Exception
  • InvalidArgumentException
  • Kotchasan\ApiException
  • Kotchasan\Cache\Exception
  • Kotchasan\Database\Exception
  • Kotchasan\InputItemException
  • LogicException
  • phpmailerException
  • RuntimeException

Functions

  • createClass
  • debug
  • getClassPath
  • Overview
  • Namespace
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * @filesource Kotchasan/DataTable.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;
  12: 
  13: use Kotchasan\Http\Uri;
  14: 
  15: /**
  16:  * คลาสสำหรับจัดการแสดงผลข้อมูลจาก Model ในรูปแบบตาราง
  17:  *
  18:  * @author Goragod Wiriya <admin@goragod.com>
  19:  *
  20:  * @since 1.0
  21:  */
  22: class DataTable extends \Kotchasan\KBase
  23: {
  24:     /**
  25:      * id ของตาราง
  26:      *
  27:      * @var string
  28:      */
  29:     private $id;
  30:     /**
  31:      * ชื่อ Model ที่ต้องการเรียกข้อมูล
  32:      *
  33:      * @var \Kotchasan\Database\QueryBuilder
  34:      */
  35:     private $model;
  36:     /**
  37:      * ข้อมูลทั้งหมดของตารางรูปแบบแอเรย์
  38:      * หากไม่ได้ใช้ตารางเชื่อมต่อกับ Model ให้กำหนดข้อมูลทั้งหมดที่นี่.
  39:      *
  40:      * @var array
  41:      */
  42:     private $datas;
  43:     /**
  44:      * database cache.
  45:      *
  46:      * @var bool
  47:      */
  48:     private $cache = false;
  49:     /**
  50:      * รายชื่อฟิลด์ที่จะ query.
  51:      *
  52:      * @var array
  53:      */
  54:     private $fields = array();
  55:     /**
  56:      * คอลัมน์ของ checkbox
  57:      * -1 ไม่แสดง checkbox.
  58:      *
  59:      * @var int
  60:      */
  61:     private $checkCol = -1;
  62:     /**
  63:      * กำหนดการแสดงผล checkbox
  64:      * ถ้าเป็น true จะซ่อน checkbox เสมอ.
  65:      *
  66:      * @var bool
  67:      */
  68:     public $hideCheckbox = false;
  69:     /**
  70:      * แสดงตารางกว้าง 100%.
  71:      *
  72:      * @var bool
  73:      */
  74:     private $fullWidth = true;
  75:     /**
  76:      * แสดงเส้นกรอบ.
  77:      *
  78:      * @var bool
  79:      */
  80:     private $border = false;
  81:     /**
  82:      * แสดงปุ่ม สำหรับเพิ่มและลบแถว.
  83:      *
  84:      * @var bool
  85:      */
  86:     private $pmButton = false;
  87:     /**
  88:      * แสดงตารางแบบ responsive.
  89:      *
  90:      * @var bool
  91:      */
  92:     private $responsive = false;
  93:     /**
  94:      * แสดงส่วนหัวของตาราง.
  95:      *
  96:      * @var bool
  97:      */
  98:     private $showCaption = true;
  99:     /**
 100:      * URL สำหรับรับค่าจาก action ต่างๆ เช่นการลบ
 101:      * เช่น index/[controller|model]/className/method.php
 102:      *
 103:      * @var string
 104:      */
 105:     private $action;
 106:     /**
 107:      * ถ้ากำหนดรายการนี้จะแสดง checkbox และ ปุ่ม action
 108:      * array('delete' => Language::get('Delete'), 'published' => Language::get('Published'))
 109:      * หมายถึงแสดง select สำหรับ ลบ และ เผยแพร่.
 110:      *
 111:      * @var array
 112:      */
 113:     public $actions = array();
 114:     /**
 115:      * ชื่อฟังก์ชั่น Javascript เรียกหลังจากทำการส่งค่าจาก action ไปประมวลผลแล้ว
 116:      * เช่น doFormSubmit.
 117:      *
 118:      * @var string
 119:      */
 120:     private $actionCallback;
 121:     /**
 122:      * ชื่อฟังก์ชั่น Javascript เรียกหลังจากคลิก action
 123:      * เช่น confirmAction(text, action, id).
 124:      *
 125:      * @var string
 126:      */
 127:     private $actionConfirm;
 128:     /**
 129:      * method สำหรับจัดการข้อมูลแต่ละแถวก่อนการแสดงผล
 130:      * function($item, $index, $prop)
 131:      * $item array ข้อมูล
 132:      * $row int ลำดับที่ของข้อมูล (key)
 133:      * $prop array property ของ tr เช่น $prop[0]['id'] = xxx.
 134:      *
 135:      * @var array array($this, methodName)
 136:      */
 137:     private $onRow;
 138:     /**
 139:      * ชื่อฟังก์ชั่น Javascript เรียกก่อนที่จะมีการลบแถว (pmButton)
 140:      * ถ้าฟังก์ชั่นคืนค่า true มา ถึงจะมีการลบแถว
 141:      * function(tr){return true;}.
 142:      *
 143:      * @var string
 144:      */
 145:     private $onBeforeDelete;
 146:     /**
 147:      * ชื่อฟังก์ชั่น Javascript เรียกเมื่อมีการลบแถวแล้ว (pmButton)
 148:      * function(){}.
 149:      *
 150:      * @var string
 151:      */
 152:     private $onDelete;
 153:     /**
 154:      * ชื่อฟังก์ชั่น Javascript เรียกเมื่อมีการเพิ่มแถวใหม่ (pmButton)
 155:      * ฟังก์ชั่นนี้จะมีการเรียกใช้ก่อนเรียกใช้ $onInitRow
 156:      * function(tr).
 157:      *
 158:      * @var string
 159:      */
 160:     private $onAddRow;
 161:     /**
 162:      * ชื่อฟังก์ชั่น Javascript เรียกเพื่อจัดการแถวใหม่
 163:      * function(tr, row).
 164:      *
 165:      * @var string
 166:      */
 167:     private $onInitRow;
 168:     /**
 169:      * ลิสต์คำสั่ง Query หลัก สำหรับคัดเลือกข้อมูล
 170:      * array('id', 1) WHERE `id` = 1 AND ...
 171:      * array('id', array(1, 2)) WHERE `id` IN (1, 2) AND ...
 172:      * array('id', '!=' , 1) WHERE `id` != 1 AND ...
 173:      *
 174:      * @var array
 175:      */
 176:     public $defaultFilters = array();
 177:     /**
 178:      * ฟิลเตอร์ข้อมูลแสดงผล
 179:      * ถ้ากำหนดรายการนี้จะแสดงตัวเลือกการ filter ด้านบนตาราง.
 180:      *
 181:      * @var array
 182:      */
 183:     public $filters = array();
 184:     /**
 185:      * รายชื่อคอลัมน์ที่ไม่ต้องแสดงผล.
 186:      *
 187:      * @var array
 188:      */
 189:     public $hideColumns = array();
 190:     /**
 191:      * รายชื่อคอลัมน์ทั้งหมด.
 192:      *
 193:      * @var array
 194:      */
 195:     public $columns = array();
 196:     /**
 197:      * รายชื่อส่วนหัวของตอลัมน์.
 198:      *
 199:      * @var array
 200:      */
 201:     public $headers = array();
 202:     /**
 203:      * รายชื่อฟิลด์ที่สามารถค้นหาได้
 204:      * ถ้ากำหนดรายการนี้จะแสดงกล่องค้นหา.
 205:      *
 206:      * @var array
 207:      */
 208:     public $searchColumns = array();
 209:     /**
 210:      * กำหนดวิธีการค้นหาจากช่อง search
 211:      * true (default) ค้นหาจาก $searchColumns โดยอัตโนมัติ
 212:      * false กำหนดการค้นหาด้วยตัวเอง.
 213:      *
 214:      * @var bool
 215:      */
 216:     public $autoSearch = true;
 217:     /**
 218:      * การแสดงฟอร์มค้นหา
 219:      * auto (default) แสดงฟอร์มค้นหา ถ้ามี $searchColumns ระบุมา
 220:      * true แสดงฟอร์มค้นหาเสมอ
 221:      * false ไม่ต้องแสดงฟอร์มค้นหา
 222:      *
 223:      * @var bool
 224:      */
 225:     public $searchForm = 'auto';
 226:     /**
 227:      * ข้อความค้นหา
 228:      *
 229:      * @var string
 230:      */
 231:     public $search = '';
 232:     /**
 233:      * จำนวนรายการต่อหน้า
 234:      * ถ้ากำหนดรายการนี้จะแสดงรายการแบ่งหน้า และตัวเลือกแสดงรายการต่อหน้า.
 235:      *
 236:      * @var int|null
 237:      */
 238:     public $perPage = null;
 239:     /**
 240:      * หน้าที่กำลังแสดงผล
 241:      *
 242:      * @var int
 243:      */
 244:     public $page = 1;
 245:     /**
 246:      * ชื่อคอลัมน์ที่ใช้เรียงลำดับ
 247:      * ค่าเริ่มต้น null สำหรับการรับค่าอัตโนมัติ.
 248:      *
 249:      * @var string|null
 250:      */
 251:     public $sort = null;
 252:     /**
 253:      * ชื่อคอลัมน์ที่ใช้เรียงลำดับเริ่มต้น
 254:      * ถ้ามีการกำหนดค่าจะเรียงลำดับตามรายการนี้ก่อนเสมอ
 255:      *
 256:      * @var string|null
 257:      */
 258:     public $defaultSort = null;
 259:     /**
 260:      * ข้อมูลการเรียงลำดับที่กำลังใช้งานอยู่.
 261:      *
 262:      * @var array
 263:      */
 264:     protected $sorts = array();
 265:     /**
 266:      * ปุ่มที่จะใส่ไว้ด้านหลังของแต่ละแถว.
 267:      *
 268:      * @var array
 269:      */
 270:     public $buttons = array();
 271:     /**
 272:      * method สำหรับเตรียมการแสดงผล button
 273:      * ถ้าคืนค่า false กลับมาจะไม่มีการสรางปุ่ม
 274:      * function($btn, $attributes, $items)
 275:      * $btn string id ของ button
 276:      * $attributes array property ของปุ่ม
 277:      * $items array ข้อมูลในแถว.
 278:      *
 279:      * @var array array($this, methodName)
 280:      */
 281:     private $onCreateButton;
 282:     /**
 283:      * method เรียกเมื่อต้องการสร้าง header
 284:      * คืนค่า tag tr ที่อยู่ภายใน header
 285:      * function().
 286:      *
 287:      * @var array array($this, methodName)
 288:      */
 289:     private $onCreateHeader;
 290:     /**
 291:      * method เรียกเมื่อต้องการสร้าง footer
 292:      * คืนค่า tag tr ที่อยู่ภายใน footer
 293:      * function().
 294:      *
 295:      * @var array array($this, methodName)
 296:      */
 297:     private $onCreateFooter;
 298:     /**
 299:      * กำหนดคอลัมน์ หากยอมให้สามารถจัดลำดับตารางด้วยการลากได้.
 300:      *
 301:      * @var int
 302:      */
 303:     private $dragColumn = -1;
 304:     /**
 305:      * ชื่อคีย์หลักของข้อมูล
 306:      * สำหรับอ่าน id ของ แถว.
 307:      *
 308:      * @var string
 309:      */
 310:     private $primaryKey = 'id';
 311:     /**
 312:      * Javascript.
 313:      *
 314:      * @var array
 315:      */
 316:     private $javascript = array();
 317:     /**
 318:      * เปิดใช้งาน Javascript ของตาราง
 319:      * true เปิดใช้งาน GTable
 320:      * false ปิดใช้งาน GTable แต่ยังแทรก Javascript อื่นๆได้.
 321:      *
 322:      * @var bool
 323:      */
 324:     public $enableJavascript = true;
 325:     /**
 326:      * Uri ปัจจุบันของหน้าเว็บ.
 327:      *
 328:      * @var Uri
 329:      */
 330:     private $uri;
 331:     /**
 332:      * ตัวเลือกจำนวนการแสดงผล.
 333:      *
 334:      * @var array
 335:      */
 336:     public $entriesList = array(10, 20, 30, 40, 50, 100);
 337:     /**
 338:      * แสดง Query ออกทางจอภาพ.
 339:      *
 340:      * @var bool
 341:      */
 342:     private $debug = false;
 343:     /**
 344:      * แสดง Explain ของ Query.
 345:      *
 346:      * @var bool
 347:      */
 348:     private $explain = false;
 349: 
 350:     /**
 351:      * Initial Class.
 352:      *
 353:      * @param array $param
 354:      */
 355:     public function __construct($param)
 356:     {
 357:         $this->id = 'datatable';
 358:         foreach ($param as $key => $value) {
 359:             $this->{$key} = $value;
 360:         }
 361:         if (empty($this->uri)) {
 362:             $this->uri = self::$request->getUri();
 363:         } elseif (is_string($this->uri)) {
 364:             $this->uri = Uri::createFromUri($this->uri);
 365:         }
 366:         // รายการต่อหน้า มาจากการเลือกภายในตาราง
 367:         if ($this->perPage !== null) {
 368:             $count = self::$request->globals(array('POST', 'GET'), 'count', $this->perPage)->toInt();
 369:             if (in_array($count, $this->entriesList)) {
 370:                 $this->perPage = $count;
 371:                 $this->uri = $this->uri->withParams(array('count' => $count));
 372:             }
 373:         }
 374:         // header ของตาราง มาจาก model หรือมาจากข้อมูล หรือ มาจากการกำหนดเอง
 375:         if (isset($this->model)) {
 376:             // แปลงฐานข้อมูลเป็น Model
 377:             $model = new \Kotchasan\Model();
 378:             $model = $model->db()->createQuery()->select();
 379:             // อ่านข้อมูลรายการแรกเพื่อใช้ชื่อฟิลด์เป็นหัวตาราง
 380:             if (is_string($this->model)) {
 381:                 // model เป็น Recordset, create Recordset
 382:                 $rs = new \Kotchasan\Orm\Recordset($this->model);
 383:                 // แปลง Recordset เป็น QueryBuilder
 384:                 $this->model = $model->from(array($rs->toQueryBuilder(), 'Z9'));
 385:             } else {
 386:                 $this->model = $model->from(array($this->model, 'Z9'));
 387:             }
 388:             // อ่านข้อมูลรายการแรก
 389:             if ($this->explain) {
 390:                 $first = $this->model->copy()->explain()->first();
 391:             } else {
 392:                 $first = $this->model->copy()->first($this->fields);
 393:             }
 394:             // อ่านคอลัมน์ของตาราง
 395:             if ($first) {
 396:                 foreach ($first as $k => $v) {
 397:                     $this->columns[$k] = array('text' => $k);
 398:                 }
 399:             } elseif (!empty($this->fields)) {
 400:                 foreach ($this->fields as $field) {
 401:                     if (is_array($field)) {
 402:                         $this->columns[$field[1]] = array('text' => $field[1]);
 403:                     } elseif (is_string($field) && preg_match('/(.*?[`\s]+)?([a-z0-9_]+)`?$/i', $field, $match)) {
 404:                         $this->columns[$match[2]] = array('text' => $match[2]);
 405:                     }
 406:                 }
 407:             }
 408:         } elseif (isset($this->datas)) {
 409:             // อ่านคอลัมน์จากข้อมูลรายการแรก
 410:             $this->columns = array();
 411:             if (!empty($this->datas)) {
 412:                 foreach (reset($this->datas) as $key => $value) {
 413:                     $this->columns[$key] = array('text' => $key);
 414:                 }
 415:             }
 416:         }
 417:         // สำหรับตรวจสอบว่าสามารถเรียงลำดับได้หรือไม่ ตาม header ที่ส่งมา
 418:         $autoSort = false;
 419:         // จัดการ header, ตรวจสอบกับค่ากำหนดมา เรียงลำดับ header ตาม columns
 420:         if (!empty($this->columns)) {
 421:             $headers = array();
 422:             foreach ($this->columns as $field => $attributes) {
 423:                 if (!in_array($field, $this->hideColumns)) {
 424:                     if (isset($this->headers[$field])) {
 425:                         $headers[$field] = $this->headers[$field];
 426:                         if (!isset($headers[$field]['text'])) {
 427:                             $headers[$field]['text'] = $field;
 428:                         }
 429:                         if (isset($headers[$field]['sort'])) {
 430:                             $autoSort = true;
 431:                         }
 432:                     } else {
 433:                         $headers[$field]['text'] = $field;
 434:                     }
 435:                 }
 436:             }
 437:             $this->headers = $headers;
 438:         }
 439:         // รับค่าการเรียงลำดับถ้ามีการกำหนด sort ไว้
 440:         if ($autoSort) {
 441:             $this->sort = self::$request->globals(array('POST', 'GET'), 'sort', $this->sort)->topic();
 442:         }
 443:         if (!empty($this->sort)) {
 444:             $this->uri = $this->uri->withParams(array('sort' => $this->sort));
 445:         }
 446:         // search
 447:         $this->search = self::$request->globals(array('POST', 'GET'), 'search')->text();
 448:     }
 449: 
 450:     /**
 451:      * กำหนด Javascript
 452:      *
 453:      * @param string $script
 454:      */
 455:     public function script($script)
 456:     {
 457:         $this->javascript[] = $script;
 458:     }
 459: 
 460:     /**
 461:      * สร้างตาราง และเริ่มต้นทำงานตาราง
 462:      * คืนค่าเป็นโค้ด HTML ของ DataTable
 463:      *
 464:      * @return string
 465:      */
 466:     public function render()
 467:     {
 468:         if (!empty($this->actions) && $this->checkCol == -1) {
 469:             $this->checkCol = 1;
 470:         }
 471:         $url_query = array();
 472:         $hidden_fields = array();
 473:         $query_string = array();
 474:         parse_str($this->uri->getQuery(), $query_string);
 475:         self::$request->map($url_query, $query_string);
 476:         foreach ($url_query as $key => $value) {
 477:             // Input สำหรับการเรียกไปยังหน้าถัดไป
 478:             if (!preg_match('/.*?([0-9]+|username|password|token|time|search|count|page|action).*?/', $key)) {
 479:                 $hidden_fields[$key] = '<input type="hidden" name="'.$key.'" value="'.htmlspecialchars($value).'">';
 480:             }
 481:         }
 482:         if (isset($this->model)) {
 483:             // รายการ Query หลัก (AND)
 484:             $qs = array();
 485:             foreach ($this->defaultFilters as $array) {
 486:                 $qs[] = $array;
 487:             }
 488:         }
 489:         // create HTML
 490:         $content = array('<div class="datatable" id="'.$this->id.'">');
 491:         // form
 492:         $form = array();
 493:         if ($this->perPage !== null) {
 494:             $entries = Language::get('entries');
 495:             $options = array();
 496:             foreach ($this->entriesList as $c) {
 497:                 if ($c == 0) {
 498:                     $options[0] = Language::get('all items');
 499:                 } else {
 500:                     $options[$c] = $c.' '.$entries;
 501:                 }
 502:             }
 503:             $form[] = $this->addFilter(array(
 504:                 'name' => 'count',
 505:                 'text' => Language::get('Show'),
 506:                 'value' => $this->perPage,
 507:                 'options' => $options,
 508:             ));
 509:         }
 510:         // รายการ Query กำหนดโดย User (AND)
 511:         foreach ($this->filters as $key => $items) {
 512:             $form[] = $this->addFilter($items);
 513:             if (isset($items['name'])) {
 514:                 unset($hidden_fields[$items['name']]);
 515:             }
 516:             if (!isset($items['default'])) {
 517:                 $items['default'] = '';
 518:             }
 519:             // ไม่ Query รายการ default
 520:             if (!empty($items['options']) && isset($items['value']) && $items['value'] !== $items['default'] && in_array($items['value'], array_keys($items['options']), true)) {
 521:                 if (isset($items['onFilter'])) {
 522:                     $q = call_user_func($items['onFilter'], $key, $items['value']);
 523:                     if ($q) {
 524:                         $qs[] = $q;
 525:                     }
 526:                 } elseif (is_string($key)) {
 527:                     $qs[] = array($key, $items['value']);
 528:                 }
 529:             }
 530:         }
 531:         if ($this->model) {
 532:             if (!empty($qs)) {
 533:                 $this->model->andWhere($qs);
 534:             }
 535:         }
 536:         // ปุ่ม Go
 537:         if (!empty($form)) {
 538:             $form[] = '<fieldset class=go>';
 539:             $form[] = '<button type=submit class="button go">'.Language::get('Go').'</button>';
 540:             $form[] = implode('', $hidden_fields);
 541:             $form[] = '</fieldset>';
 542:         }
 543:         // search
 544:         if ($this->searchForm === true || ($this->searchForm === 'auto' && !empty($this->searchColumns))) {
 545:             if (!empty($this->search)) {
 546:                 if ($this->autoSearch) {
 547:                     if (isset($this->model)) {
 548:                         $sh = array();
 549:                         foreach ($this->searchColumns as $key) {
 550:                             $sh[] = array($key, 'LIKE', '%'.$this->search.'%');
 551:                         }
 552:                         $this->model->andWhere($sh, 'OR');
 553:                     } elseif (isset($this->datas)) {
 554:                         // filter ข้อมูลจาก array
 555:                         $this->datas = ArrayTool::filter($this->datas, $this->search);
 556:                     }
 557:                 }
 558:                 $this->uri = $this->uri->withParams(array('search' => $this->search));
 559:             }
 560:             $form[] = '</div>';
 561:             $form[] = '<div class="table_search">';
 562:             $form[] = '<fieldset class=search>';
 563:             $form[] = '<label accesskey=f><input type=text name=search value="'.$this->search.'" placeholder="'.Language::get('Search').'"></label>';
 564:             $form[] = '<button type=submit>&#xe607;</button>';
 565:             $form[] = '<button type=submit class=clear_search>&#x78;</button>';
 566:             $form[] = '</fieldset>';
 567:         }
 568:         if (!$this->explain && !empty($form)) {
 569:             $content[] = '<form class="table_nav clear" method="get" action="'.$this->uri.'"><div>'.implode('', $form).'</div></form>';
 570:         }
 571:         if (isset($this->model)) {
 572:             if ($this->explain) {
 573:                 // explain mode
 574:                 $count = 0;
 575:             } else {
 576:                 // field select
 577:                 $this->model->select($this->fields);
 578:                 // จำนวนข้อมูลทั้งหมด (Query Builder)
 579:                 $model = new \Kotchasan\Model();
 580:                 $query = $model->db()->createQuery()
 581:                     ->selectCount()
 582:                     ->from(array($this->model, 'Z'));
 583:                 if ($this->cache) {
 584:                     $query->cacheOn();
 585:                 }
 586:                 $result = $query->toArray()->execute();
 587:                 $count = empty($result) ? 0 : $result[0]['count'];
 588:             }
 589:         } elseif (!empty($this->datas)) {
 590:             // จำนวนข้อมูลทั้งหมดจาก array
 591:             $count = count($this->datas);
 592:         } else {
 593:             // ไม่มีข้อมูล
 594:             $count = 0;
 595:         }
 596:         // การแบ่งหน้า
 597:         if ($this->perPage > 0) {
 598:             // หน้าที่เลือก
 599:             $this->page = max(1, self::$request->globals(array('POST', 'GET'), 'page', 1)->toInt());
 600:             // ตรวจสอบหน้าที่เลือกสูงสุด
 601:             $totalpage = round($count / $this->perPage);
 602:             $totalpage += ($totalpage * $this->perPage < $count) ? 1 : 0;
 603:             $this->page = max(1, $this->page > $totalpage ? $totalpage : $this->page);
 604:             $start = $this->perPage * ($this->page - 1);
 605:             // คำนวณรายการที่แสดง
 606:             $s = $start < 0 ? 0 : $start + 1;
 607:             $e = min($count, $s + $this->perPage - 1);
 608:         } else {
 609:             $start = 0;
 610:             $totalpage = 1;
 611:             $this->page = 1;
 612:             $s = 1;
 613:             $e = $count;
 614:             $this->perPage = 0;
 615:         }
 616:         // table caption
 617:         if ($this->showCaption) {
 618:             if (empty($this->search)) {
 619:                 $caption = Language::get('All :count entries, displayed :start to :end, page :page of :total pages');
 620:             } else {
 621:                 $caption = Language::get('Search <strong>:search</strong> found :count entries, displayed :start to :end, page :page of :total pages');
 622:             }
 623:             $caption = str_replace(array(':search', ':count', ':start', ':end', ':page', ':total'), array($this->search, number_format($count), number_format($s), number_format($e), number_format($this->page), number_format($totalpage)), $caption);
 624:         }
 625:         // เรียงลำดับ
 626:         $orders = array();
 627:         if (!empty($this->defaultSort)) {
 628:             foreach (explode(',', $this->defaultSort) as $sort) {
 629:                 if (preg_match('/^([a-z0-9_\-]+)([\s]+(desc|asc))?$/i', trim($sort), $match)) {
 630:                     $sortType = isset($match[3]) && strtolower($match[3]) == 'desc' ? 'desc' : 'asc';
 631:                     $orders[] = $match[1].' '.$sortType;
 632:                     $this->sorts[$match[1]] = $sortType;
 633:                 }
 634:             }
 635:         }
 636:         if (!empty($this->sort)) {
 637:             $sorts = array();
 638:             foreach (explode(',', $this->sort) as $sort) {
 639:                 if (preg_match('/^([a-z0-9_\-]+)([\s]+(desc|asc))?$/i', trim($sort), $match)) {
 640:                     if (isset($this->headers[$match[1]]['sort'])) {
 641:                         $sort = $this->headers[$match[1]]['sort'];
 642:                     } elseif (isset($this->columns[$match[1]])) {
 643:                         $sort = $match[1];
 644:                     } elseif ($this->model && isset($this->columns[$match[1]])) {
 645:                         $sort = $match[1];
 646:                     } else {
 647:                         $sort = null;
 648:                     }
 649:                     if ($sort) {
 650:                         $sortType = isset($match[3]) && strtolower($match[3]) == 'desc' ? 'desc' : 'asc';
 651:                         $this->sorts[$sort] = $sortType;
 652:                         $sorts[] = $sort.' '.$sortType;
 653:                     }
 654:                 }
 655:             }
 656:             $this->sort = implode(',', $sorts);
 657:             $orders = array_merge($orders, $sorts);
 658:         }
 659: 
 660:         if (isset($this->model)) {
 661:             if (!empty($orders)) {
 662:                 $this->model->order($orders);
 663:             }
 664:         } elseif (!empty($this->sorts)) {
 665:             reset($this->sorts);
 666:             $sort = key($this->sorts);
 667:             $this->datas = ArrayTool::sort($this->datas, $sort, $this->sorts[$sort]);
 668:         }
 669:         if (isset($this->model)) {
 670:             if ($this->explain) {
 671:                 $this->model->explain();
 672:             }
 673:             if ($this->debug === true) {
 674:                 // debug Query
 675:                 $this->debug = $this->model->toArray()->limit($this->perPage, $start)->text();
 676:             }
 677:             // query ข้อมูล
 678:             $this->datas = $this->model->toArray()->limit($this->perPage, $start)->execute();
 679:             // รายการสุดท้าย
 680:             $end = $this->perPage + 1;
 681:             // รายการแรก
 682:             $start = -1;
 683:         } elseif (isset($this->datas)) {
 684:             if ($this->debug === true) {
 685:                 // debug ข้อมูลแอเรย์
 686:                 $this->debug = var_export($this->datas, true);
 687:             }
 688:             // รายการสุดท้าย
 689:             $end = $start + $this->perPage - 1;
 690:             // รายการแรก
 691:             $start = $start - 2;
 692:         } else {
 693:             $end = 0;
 694:         }
 695:         // property ของ ตาราง
 696:         $prop = array();
 697:         $c = array();
 698:         if (isset($this->class)) {
 699:             $c[] = $this->class;
 700:         }
 701:         if ($this->border) {
 702:             $c[] = 'border';
 703:         }
 704:         if ($this->responsive) {
 705:             $c[] = 'responsive-v';
 706:         }
 707:         if ($this->fullWidth) {
 708:             $c[] = 'fullwidth';
 709:         }
 710:         if (count($c) > 0) {
 711:             $prop[] = ' class="'.implode(' ', $c).'"';
 712:         }
 713:         // table
 714:         $content[] = '<div class="tablebody"><table'.implode('', $prop).'>';
 715:         if ($this->showCaption) {
 716:             $content[] = '<caption>'.$caption.'</caption>';
 717:         }
 718:         $colCount = 0;
 719:         // table header
 720:         $thead = null;
 721:         if (!$this->explain && isset($this->onCreateHeader)) {
 722:             $thead = call_user_func($this->onCreateHeader);
 723:         } elseif (!empty($this->headers)) {
 724:             $row = array();
 725:             $i = 0;
 726:             $colspan = 0;
 727:             foreach ($this->headers as $key => $attributes) {
 728:                 if ($colspan === 0) {
 729:                     if (!$this->explain) {
 730:                         if (!$this->hideCheckbox && $i == $this->checkCol) {
 731:                             $row[] = '<th class="check-column"><a class="checkall icon-uncheck"></a></th>';
 732:                             ++$colCount;
 733:                         }
 734:                         if ($i == $this->dragColumn) {
 735:                             $row[] = '<th></th>';
 736:                             ++$colCount;
 737:                         }
 738:                     }
 739:                     if (isset($attributes['colspan'])) {
 740:                         $colspan = $attributes['colspan'] - 1;
 741:                     }
 742:                     $row[] = $this->th($i, $key, $attributes);
 743:                     ++$i;
 744:                 } else {
 745:                     --$colspan;
 746:                 }
 747:                 ++$colCount;
 748:             }
 749:             if (!$this->explain && !$this->hideCheckbox && $colCount == $this->checkCol) {
 750:                 $row[] = '<th class="check-column"><a class="checkall icon-uncheck"></a></th>';
 751:                 ++$colCount;
 752:             }
 753:             if ($colspan === 0) {
 754:                 if (!empty($this->buttons)) {
 755:                     $row[] = $this->th($i, '', array('text' => ''));
 756:                     ++$colCount;
 757:                     ++$i;
 758:                 }
 759:             } else {
 760:                 --$colspan;
 761:             }
 762:             if ($colspan === 0) {
 763:                 if ($this->pmButton) {
 764:                     $row[] = $this->th($i, '', array('text' => ''));
 765:                     ++$colCount;
 766:                 }
 767:             } else {
 768:                 --$colspan;
 769:             }
 770:             $thead = '<tr>'.implode('', $row).'</tr>';
 771:         }
 772:         if (!empty($thead) && is_string($thead)) {
 773:             $content[] = '<thead>'.$thead.'</thead>';
 774:         }
 775:         // tbody
 776:         if (!empty($this->datas)) {
 777:             $content[] = '<tbody>'.$this->tbody($start, $end).'</tbody>';
 778:         }
 779:         if (!$this->explain) {
 780:             // tfoot
 781:             $tfoot = null;
 782:             if (isset($this->onCreateFooter)) {
 783:                 $tfoot = call_user_func($this->onCreateFooter);
 784:             } elseif (!$this->hideCheckbox && $this->checkCol > -1) {
 785:                 $tfoot = '<tr>';
 786:                 $tfoot .= '<td colspan="'.$this->checkCol.'"></td>';
 787:                 $tfoot .= '<td class="check-column"><a class="checkall icon-uncheck"></a></td>';
 788:                 $tfoot .= '<td colspan="'.($colCount - $this->checkCol - 1).'"></td>';
 789:                 $tfoot .= '</tr>';
 790:             }
 791:             if (!empty($tfoot) && is_string($tfoot)) {
 792:                 $content[] = '<tfoot>'.$tfoot.'</tfoot>';
 793:             }
 794:         }
 795:         $content[] = '</table></div>';
 796:         $table_nav = array();
 797:         if (!empty($this->actions) && is_array($this->actions)) {
 798:             foreach ($this->actions as $item) {
 799:                 $table_nav[] = $this->addAction($item);
 800:             }
 801:         }
 802:         if (!empty($this->addNew) && is_array($this->addNew)) {
 803:             $prop = array();
 804:             foreach ($this->addNew as $k => $v) {
 805:                 if ($k != 'text') {
 806:                     $prop[$k] = $k.'="'.$v.'"';
 807:                 }
 808:             }
 809:             $text = isset($this->addNew['text']) ? $this->addNew['text'] : '';
 810:             if (preg_match('/^((.*)\s+)?(icon-[a-z0-9\-_]+)(\s+(.*))?$/', $this->addNew['class'], $match)) {
 811:                 $prop['class'] = 'class="'.trim($match[2].' '.(isset($match[5]) ? $match[5] : '')).'"';
 812:                 if (isset($prop['href'])) {
 813:                     $table_nav[] = '<a '.implode(' ', $prop).'><span class="'.$match[3].'">'.$text.'</span></a>';
 814:                 } else {
 815:                     $table_nav[] = '<button '.implode(' ', $prop).' type="button"><span class="'.$match[3].'">'.$text.'</span></button>';
 816:                 }
 817:             } elseif (isset($prop['href'])) {
 818:                 $table_nav[] = '<a '.implode(' ', $prop).'>'.$text.'</a>';
 819:             } else {
 820:                 $table_nav[] = '<button '.implode(' ', $prop).' type="button">'.$text.'</button>';
 821:             }
 822:         }
 823:         if (!$this->explain) {
 824:             if (!empty($table_nav)) {
 825:                 $content[] = '<div class="table_nav clear action">'.implode('', $table_nav).'</div>';
 826:             }
 827:             // แบ่งหน้า
 828:             if ($this->perPage > 0) {
 829:                 $content[] = '<div class="splitpage">'.$this->uri->pagination($totalpage, $this->page).'</div>';
 830:             }
 831:         }
 832:         $content[] = '</div>';
 833:         if ($this->enableJavascript && !$this->explain) {
 834:             $script = array(
 835:                 'page' => $this->page,
 836:                 'search' => $this->search,
 837:                 'sort' => $this->sort,
 838:                 'action' => $this->action,
 839:                 'actionCallback' => $this->actionCallback,
 840:                 'actionConfirm' => $this->actionConfirm,
 841:                 'onBeforeDelete' => $this->onBeforeDelete,
 842:                 'onDelete' => $this->onDelete,
 843:                 'onInitRow' => $this->onInitRow,
 844:                 'onAddRow' => $this->onAddRow,
 845:                 'pmButton' => $this->pmButton,
 846:                 'dragColumn' => $this->dragColumn,
 847:                 'debug' => is_string($this->debug) ? $this->debug : '',
 848:             );
 849:             $this->javascript[] = 'var table = new GTable("'.$this->id.'", '.json_encode($script).');';
 850:         }
 851:         if (!empty($this->javascript)) {
 852:             $content[] = "<script>\n".implode("\n", $this->javascript)."\n</script>";
 853:         }
 854:         return implode("\n", $content);
 855:     }
 856: 
 857:     /**
 858:      * render tbody
 859:      *
 860:      * @param int $start ข้อมูลเริ่มต้น (เริ่มที่ 1)
 861:      *
 862:      * @return string
 863:      */
 864:     private function tbody($start, $end)
 865:     {
 866:         $row = array();
 867:         $n = 0;
 868:         foreach ($this->datas as $o => $items) {
 869:             if ($this->perPage <= 0 || ($n > $start && $n < $end)) {
 870:                 $src_items = $items;
 871:                 // id ของข้อมูล
 872:                 $id = isset($items[$this->primaryKey]) ? $items[$this->primaryKey] : $o;
 873:                 $prop = (object) array(
 874:                     'id' => $this->id.'_'.$id,
 875:                 );
 876:                 $buttons = array();
 877:                 if (!$this->explain) {
 878:                     if (!empty($this->buttons)) {
 879:                         foreach ($this->buttons as $btn => $attributes) {
 880:                             if (isset($this->onCreateButton)) {
 881:                                 // event ปุ่มหลัก
 882:                                 $attributes = call_user_func($this->onCreateButton, $btn, $attributes, $items);
 883:                             }
 884:                             if ($attributes && $attributes !== false) {
 885:                                 $buttons[] = $this->button($btn, $attributes, $items);
 886:                             }
 887:                         }
 888:                     }
 889:                     if (isset($this->onRow)) {
 890:                         $items = call_user_func($this->onRow, $items, $o, $prop);
 891:                     }
 892:                     if (isset($this->dragColumn)) {
 893:                         $prop->class = (empty($prop->class) ? 'sort' : $prop->class.' sort');
 894:                     }
 895:                 }
 896:                 // แถว
 897:                 $p = array();
 898:                 foreach ($prop as $k => $v) {
 899:                     $p[] = $k.'="'.$v.'"';
 900:                 }
 901:                 $row[] = '<tr '.implode(' ', $p).'>';
 902:                 // แสดงผลข้อมูล
 903:                 $i = 0;
 904:                 foreach ($this->headers as $field => $attributes) {
 905:                     if (!empty($field) && !in_array($field, $this->hideColumns)) {
 906:                         if (!$this->explain) {
 907:                             if (!$this->hideCheckbox && $i == $this->checkCol) {
 908:                                 $row[] = '<td headers="r'.$id.'" class="check-column"><a id="check_'.$id.'" class="icon-uncheck"></a></td>';
 909:                             }
 910:                             if ($i == $this->dragColumn) {
 911:                                 $row[] = '<td class=center><a id="move_'.$id.'" title="'.Language::get('Drag and drop to reorder').'" class="icon-move"></a></td>';
 912:                             }
 913:                         }
 914:                         $properties = isset($this->cols[$field]) ? $this->cols[$field] : array();
 915:                         $text = isset($items[$field]) ? $items[$field] : '';
 916:                         $th = isset($attributes['text']) ? $attributes['text'] : $field;
 917:                         $row[] = $this->td($id, $i, $properties, $text, $th);
 918:                         ++$i;
 919:                     }
 920:                 }
 921:                 if (!$this->explain) {
 922:                     if (!$this->hideCheckbox && $i == $this->checkCol) {
 923:                         $row[] = '<td headers="r'.$id.'" class="check-column"><a id="check_'.$id.'" class="icon-uncheck"></a></td>';
 924:                         ++$i;
 925:                     }
 926:                     if (!empty($this->buttons)) {
 927:                         if (!empty($buttons)) {
 928:                             $patt = array();
 929:                             $replace = array();
 930:                             $keys = array_keys($src_items);
 931:                             rsort($keys);
 932:                             foreach ($keys as $k) {
 933:                                 if (!is_array($src_items[$k])) {
 934:                                     $patt[] = ":$k";
 935:                                     $replace[] = $src_items[$k];
 936:                                 }
 937:                             }
 938:                             $prop = array('class' => 'buttons');
 939:                             if (isset($this->cols['buttons']) && isset($this->cols['buttons']['class'])) {
 940:                                 $prop = array('class' => $this->cols['buttons']['class'].' buttons');
 941:                             }
 942:                             $row[] = str_replace($patt, $replace, $this->td($id, $i, $prop, implode('', $buttons), ''));
 943:                         } else {
 944:                             $row[] = $this->td($id, $i, array(), '', '');
 945:                         }
 946:                     }
 947:                     if ($this->pmButton) {
 948:                         $row[] = '<td class="icons"><div><a class="icon-plus" title="'.Language::get('Add New').'"></a><a class="icon-minus" title="'.Language::get('Remove').'"></a></div></td>';
 949:                     }
 950:                 }
 951:                 $row[] = '</tr>';
 952:             }
 953:             ++$n;
 954:         }
 955:         return implode("\n", $row);
 956:     }
 957: 
 958:     /**
 959:      * render th.
 960:      *
 961:      * @param int    $i          ลำดับคอลัมน์
 962:      * @param string $column     ชื่อคอลัมน์
 963:      * @param array  $properties properties ของ th
 964:      *
 965:      * @return string
 966:      */
 967:     private function th($i, $column, $properties)
 968:     {
 969:         $c = array();
 970:         $c['id'] = 'id="c'.$i.'"';
 971:         if (!empty($properties['sort'])) {
 972:             $sort = isset($this->sorts[$properties['sort']]) ? $this->sorts[$properties['sort']] : 'none';
 973:             $properties['class'] = 'sort_'.$sort.' col_'.$column.(empty($properties['class']) ? '' : ' '.$properties['class']);
 974:         }
 975:         foreach ($properties as $key => $value) {
 976:             if ($key !== 'sort' && $key !== 'text') {
 977:                 $c[$key] = $key.'="'.$value.'"';
 978:             }
 979:         }
 980:         return '<th '.implode(' ', $c).'>'.(isset($properties['text']) ? $properties['text'] : $column).'</th>';
 981:     }
 982: 
 983:     /**
 984:      * render td.
 985:      *
 986:      * @param int    $id         id ของ แถว
 987:      * @param int    $i          ลำดับคอลัมน์
 988:      * @param array  $properties ชื่อคอลัมน์
 989:      * @param string $text       ข้อความใน td
 990:      *
 991:      * @return string
 992:      */
 993:     private function td($id, $i, $properties, $text, $th)
 994:     {
 995:         $c = array('data-text' => 'data-text="'.$th.'"');
 996:         foreach ($properties as $key => $value) {
 997:             $c[$key] = $key.'="'.$value.'"';
 998:         }
 999:         $c = implode(' ', $c);
1000:         if ($i == 0) {
1001:             $c .= ' id="r'.$id.'" headers="c'.$i.'"';
1002:             return '<th '.$c.'>'.$text.'</th>';
1003:         } else {
1004:             $c .= ' headers="c'.$i.' r'.$id.'"';
1005:             return '<td '.$c.'>'.$text.'</td>';
1006:         }
1007:     }
1008: 
1009:     /**
1010:      * render button.
1011:      *
1012:      * @param string $btn ชื่อ button
1013:      * @param array $properties properties ของ button
1014:      * @param array $items ข้อมูลในแถว
1015:      *
1016:      * @return string
1017:      */
1018:     private function button($btn, $properties, $items)
1019:     {
1020:         if (isset($properties['submenus'])) {
1021:             $innerHTML = '';
1022:             $li = '';
1023:             $attributes = array();
1024:             foreach ($properties as $name => $item) {
1025:                 if ($name == 'submenus') {
1026:                     foreach ($item as $btn => $menu) {
1027:                         $prop = array();
1028:                         if (isset($this->onCreateButton)) {
1029:                             // event ปุ่ม submenus
1030:                             $menu = call_user_func($this->onCreateButton, $btn, $menu, $items);
1031:                         }
1032:                         if ($menu && $menu !== false) {
1033:                             $text = '';
1034:                             foreach ($menu as $key => $value) {
1035:                                 if ($key == 'text') {
1036:                                     $text = $value;
1037:                                 } else {
1038:                                     $prop[$key] = $key.'="'.$value.'"';
1039:                                 }
1040:                             }
1041:                             $li .= '<li><a '.implode(' ', $prop).'>'.$text.'</a></li>';
1042:                         }
1043:                     }
1044:                 } elseif ($name == 'text') {
1045:                     $innerHTML = $item;
1046:                 } elseif ($name == 'class') {
1047:                     $attributes['class'] = 'class="'.$item.' menubutton"';
1048:                 } else {
1049:                     $attributes[$name] = $name.'="'.$item.'"';
1050:                 }
1051:             }
1052:             if (!isset($attributes['class'])) {
1053:                 $attributes['class'] = 'class="menubutton"';
1054:             }
1055:             if (!isset($attributes['tabindex'])) {
1056:                 $attributes['tabindex'] = 'tabindex="0"';
1057:             }
1058: 
1059:             return '<div '.implode(' ', $attributes).'>'.$innerHTML.'<ul>'.$li.'</ul></div>';
1060:         } else {
1061:             $prop = array();
1062:             foreach ($properties as $key => $value) {
1063:                 if ($key === 'id') {
1064:                     $prop[$key] = $key.'="'.$btn.'_'.$value.'"';
1065:                 } elseif ($key !== 'text') {
1066:                     $prop[$key] = $key.'="'.$value.'"';
1067:                 }
1068:             }
1069:             if (!empty($properties['class']) && preg_match('/(.*)\s?(icon\-[a-z0-9\-_]+)($|\s(.*))/', $properties['class'], $match)) {
1070:                 $class = array();
1071:                 foreach (array(1, 4) as $i) {
1072:                     if (!empty($match[$i])) {
1073:                         $class[] = $match[$i];
1074:                     }
1075:                 }
1076:                 if (empty($properties['text'])) {
1077:                     $class[] = 'notext';
1078:                     $prop['class'] = 'class="'.implode(' ', $class).'"';
1079:                     return '<a '.implode(' ', $prop).'><span class="'.$match[2].'"></span></a>';
1080:                 } else {
1081:                     $prop['class'] = 'class="'.implode(' ', $class).'"';
1082:                     if (!isset($prop['title'])) {
1083:                         $prop['title'] = 'title="'.strip_tags($properties['text']).'"';
1084:                     }
1085: 
1086:                     return '<a '.implode(' ', $prop).'><span class="'.$match[2].' button_w_text"><span class=mobile>'.$properties['text'].'</span></span></a>';
1087:                 }
1088:             } else {
1089:                 return '<a'.(empty($prop) ? '' : ' '.implode(' ', $prop)).'></a>';
1090:             }
1091:         }
1092:     }
1093: 
1094:     /**
1095:      * สร้าง select หรือ button ด้านล่างตาราง (actions).
1096:      *
1097:      * @param array $item
1098:      *
1099:      * @return string
1100:      */
1101:     private function addAction($item)
1102:     {
1103:         if (isset($item['class']) && preg_match('/^((.*)\s+)?(icon-[a-z0-9\-_]+)(\s+(.*))?$/', $item['class'], $match)) {
1104:             $match[2] = trim($match[2].' '.(isset($match[5]) ? $match[5] : ''));
1105:         }
1106:         if (isset($item['options'])) {
1107:             // select
1108:             $rows = array();
1109:             foreach ($item['options'] as $key => $text) {
1110:                 $rows[] = '<option value="'.$key.'">'.$text.'</option>';
1111:             }
1112: 
1113:             return '<fieldset><select id="'.$item['id'].'">'.implode('', $rows).'</select><label for="'.$item['id'].'" class="button '.$item['class'].' action"><span>'.$item['text'].'</span></label></fieldset>';
1114:         } elseif (isset($item['type'])) {
1115:             $prop = array(
1116:                 'type="'.$item['type'].'"',
1117:             );
1118:             $prop2 = array(
1119:                 'button' => 'class="button action"',
1120:             );
1121:             foreach ($item as $key => $value) {
1122:                 if ($key == 'id') {
1123:                     $prop[] = 'id="'.$value.'"';
1124:                     $prop2[] = 'for="'.$value.'"';
1125:                 } elseif ($key == 'class') {
1126:                     $prop2['button'] = 'class="button '.$value.' action"';
1127:                 } elseif (!in_array($key, array('type', 'text'))) {
1128:                     $prop[] = $key.'="'.$value.'"';
1129:                 }
1130:             }
1131:             return '<fieldset><input '.implode(' ', $prop).'><label '.implode(' ', $prop2).'><span>'.$item['text'].'</span></label></fieldset>';
1132:         } else {
1133:             // link, button
1134:             $prop = array();
1135:             $text = isset($item['text']) ? $item['text'] : '';
1136:             if ($text != '' && !isset($item['title'])) {
1137:                 $item['title'] = strip_tags($text);
1138:             }
1139:             if (isset($item['class'])) {
1140:                 if (empty($match[3])) {
1141:                     $prop[] = 'class="'.$item['class'].'"';
1142:                 } else {
1143:                     $text = '<span class="'.$match[3].'">'.$text.'</span>';
1144:                     $prop[] = 'class="'.$match[2].'"';
1145:                 }
1146:             }
1147:             foreach ($item as $k => $v) {
1148:                 if ($k != 'class' && $k != 'text') {
1149:                     $prop[] = $k.'="'.$v.'"';
1150:                 }
1151:             }
1152:             if (isset($item['href'])) {
1153:                 // link
1154:                 return '<a '.implode(' ', $prop).'>'.$text.'</a>';
1155:             } else {
1156:                 // button
1157:                 return '<button '.implode(' ', $prop).' type="button">'.$text.'</button>';
1158:             }
1159:         }
1160:     }
1161: 
1162:     /**
1163:      * สร้าง select ด้านบนตาราง (filters)
1164:      *
1165:      * @param array $item
1166:      *
1167:      * @return string
1168:      */
1169:     private function addFilter($item)
1170:     {
1171:         if (isset($item['datalist'])) {
1172:             $item['type'] = 'text';
1173:         }
1174:         if (isset($item['type'])) {
1175:             $prop = array();
1176:             $datalist = '';
1177:             foreach ($item as $key => $value) {
1178:                 if ($key == 'datalist') {
1179:                     foreach ($value as $k => $v) {
1180:                         $datalist .= '<option value="'.$k.'">'.$v.'</option>';
1181:                     }
1182:                 } elseif ($key != 'text' && $key != 'default' && !is_array($value)) {
1183:                     $prop[$key] = $key.'="'.$value.'"';
1184:                 }
1185:             }
1186:             if ($datalist != '' && isset($item['name'])) {
1187:                 if (!isset($item['id'])) {
1188:                     $item['id'] = $item['name'];
1189:                     $prop['id'] = 'id="'.$item['name'].'"';
1190:                 }
1191:                 $prop['autocomplete'] = 'autocomplete="off"';
1192:                 $this->javascript[] = 'new Datalist("'.$item['id'].'");';
1193:                 $prop['list'] = 'list="'.$item['name'].'-datalist"';
1194:                 $datalist = '<datalist id="'.$item['name'].'-datalist">'.$datalist.'</datalist>';
1195:             } else {
1196:                 $datalist = '';
1197:             }
1198:             $row = '<fieldset><label>'.(isset($item['text']) ? $item['text'] : '').' <input '.implode(' ', $prop).'>'.$datalist.'</label></fieldset>';
1199:         } elseif (isset($item['items'])) {
1200:             $button = '';
1201:             foreach ($item['items'] as $link) {
1202:                 foreach ($link as $key => $value) {
1203:                     if ($key != 'text') {
1204:                         $prop[$key] = $key.'="'.$value.'"';
1205:                     }
1206:                 }
1207:                 if (isset($item['name'])) {
1208:                     $prop['name'] = 'name="'.$item['name'].'"';
1209:                 }
1210:                 if (isset($item['href'])) {
1211:                     $button .= '<a '.implode(' ', $prop).'>'.(isset($link['text']) ? $link['text'] : '').'</a>';
1212:                 } else {
1213:                     $button .= '<button '.implode(' ', $prop).'>'.(isset($link['text']) ? $link['text'] : '').'</button>';
1214:                 }
1215:             }
1216:             $row = '<fieldset class="buttons"><label>'.(isset($item['text']) ? $item['text'] : '').'</label>'.$button.'</fieldset>';
1217:         } elseif (isset($item['href'])) {
1218:             foreach ($item as $key => $value) {
1219:                 if ($key != 'text') {
1220:                     $prop[$key] = $key.'="'.$value.'"';
1221:                 }
1222:             }
1223:             $row = '<a '.implode(' ', $prop).'>'.(isset($item['text']) ? $item['text'] : '').'</a>';
1224:         } else {
1225:             $prop = array();
1226:             foreach ($item as $key => $value) {
1227:                 if ($key != 'options' && $key != 'value' && $key != 'text' && $key != 'default' && !is_array($value)) {
1228:                     $prop[$key] = $key.'="'.$value.'"';
1229:                 }
1230:             }
1231:             $row = '<fieldset><label>'.(isset($item['text']) ? $item['text'] : '').' <select '.implode(' ', $prop).'>';
1232:             if (!empty($item['options'])) {
1233:                 foreach ($item['options'] as $key => $text) {
1234:                     $sel = isset($item['value']) && (string) $key == $item['value'] ? ' selected' : '';
1235:                     $row .= '<option value="'.$key.'"'.$sel.'>'.$text.'</option>';
1236:                 }
1237:             }
1238:             $row .= '</select></label></fieldset>';
1239:         }
1240:         return $row;
1241:     }
1242: }
1243: 
Kotchasan API documentation generated by ApiGen