Author: cheerasak.s

  • DevToys รวมเครื่องมือใช้บ่อยของโปรแกรมเมอร์

    เคยมั้ยครับ

    ได้ข้อมูลรูปแบบ JSON มาอยากจัดรูปแบบให้อ่านได้ง่าย (ค้น google หาเว็บจัดรูปแบบ)

    อยาก Decode JWT ออกมาดูก่อนเริ่มเขียนโปรแกรมอ่านค่า (ค้น google หาเว็บ Decode)

    อยากทดสอบ Regex ที่เขียนว่าถูกต้องหรือไม่ (ค้น google หาเว็บทดสอบ)

    ฯลฯ

    https://devtoys.app/

    DevToys คือ ซอฟแวร์ที่รวมเอา Utility ที่เหล่า Dev ใช้งานบ่อยๆเอาไว้ที่เดียวกัน ในรูปแบบ Windows App ทำให้สะดวกต่อการเรียกใช้งานมากครับ และที่สำคัญฟรี เพราะพัฒนาขึ้นโดย Community ของ Dev ที่ทำงานบนระบบปฏิบัติการ Windows แล้วรู้สึกว่าขาดสิ่งอำนวยความสะดวกเหล่านี้ (แนวคิดคล้ายๆกับ PowerToys ชื่อยังล้อกันมาด้วย)

    โดยโปรแกรมนี้ก็จะมีเมนูที่แบ่งหมวดหมู่แยกไว้ตามแต่ละประเภท ดังรูป

    เมื่อเราคลิกเข้าไปในแต่ละเมนู ก็จะมี UI ที่เรียบง่าย สามารถเข้าใจวิธีการใช้งานได้ทันที

    ตัวอย่างการใช้งาน

  • สร้าง UI หน้าเว็บแบบ Timeline ด้วย HTML+CSS

    เนื่องจากระบบที่กำลังพัฒนาอยู่ มีความต้องการแสดงผลข้อมูลในรูปแบบ Timeline ดังรูป

    เมื่อลองหาแนวทางดูพบว่ามี how to ที่เว็บไซต์ https://www.w3schools.com/howto/howto_css_timeline.asp ลองนำมาประยุกต์ใช้ โดยปรับสี เพิ่มเงา และส่วนแสดงผลรูป และไอคอนเข้าไป โดยใช้ https://getbootstrap.com/ เวอร์ชัน 5 เพื่อช่วยในการจัดตำแหน่งเนื้อหาด้านใน ก็จะได้ CSS และ HTML สำหรับนำไปใช้ในเว็บไซต์ดังนี้

    
    CSS (ตัด media query ออก เนื่องจากใช้ flex box ช่วยในการทำ responsive อยู่แล้ว)
    
    .timeline {
        position: relative;
        /*max-width: 1200px;*/
        margin: 0 auto;
    }
    
        .timeline::after {
            content: '';
            position: absolute;
            width: 6px;
            background-color: #977c44;
            top: 0;
            bottom: 0;
            left: 50%;
            margin-left: -3px;
        }
    
    .timeline-container {
        padding: 10px 40px;
        position: relative;
        background-color: inherit;
        width: 50%;
    }
    
        .timeline-container::after {
            content: '';
            position: absolute;
            width: 25px;
            height: 25px;
            right: -12px;
            background-color: white;
            border: 4px solid #FF9F55;
            top: 15px;
            border-radius: 50%;
            z-index: 1;
        }
    
    .timeline-left {
        left: 0;
    }
    
    .timeline-right {
        left: 50%;
    }
    
    .timeline-left::before {
        content: " ";
        height: 0;
        position: absolute;
        top: 22px;
        width: 0;
        z-index: 1;
        right: 30px;
        border: medium solid white;
        border-width: 10px 0 10px 10px;
        border-color: transparent transparent transparent white;
    }
    
    .timeline-right::before {
        content: " ";
        height: 0;
        position: absolute;
        top: 22px;
        width: 0;
        z-index: 1;
        left: 30px;
        border: medium solid white;
        border-width: 10px 10px 10px 0;
        border-color: transparent white transparent transparent;
    }
    
    .timeline-right::after {
        left: -12px;
    }
    
    .timeline-content {
        padding: 20px 30px;
        background-color: white;
        position: relative;
        border-radius: 6px;
    }

    HTML โดยในตัวอย่างได้นำโครงสร้าง html page และ ส่วนแสดงอีโมไอคอนออกเพื่อให้อ่านง่าย เห็นโครงสร้าง html ของ timeline ชัดเจน และต้องมีรูปอยู่ที่โฟลเดอร์ images (ภาพประกอบเนื้อหาของท่านเอง)

    /*Include Bootstrap 5*/
    <link href="css/bootstrap.min.css" rel="stylesheet" />
    
    <div class="timeline">
        <div class="timeline-container timeline-left">
            <div class="timeline-content shadow-lg">
                <div class="d-flex justify-content-between align-items-baseline">
                    <img src="/images/location01.jpg" class="rounded-3 img-shadow" width="150" height="150" />
                    <h3 class="text-brown text-shadow">พ.ศ. 1601-1700</h3>
                </div>
                <p>อุทยานประวัติศาสตร์พระนครศรีอยุธยา สัมผัสความรุ่งเรืองของกรุงศรีอยุธยา อดีตราชธานีที่ปกครองแผ่นดินมายาวนานถึง 417 ปี ภายในมีโบราณสถานมากมายน่าเที่ยว ทั้ง พระราชวังหลวง วัดพระศรีสรรเพชญ์ วัดราชบูรณะ วัดมหาธาตุ วัดไชย...</p>
            </div>
        </div>
        <div class="timeline-container timeline-right">
            <div class="timeline-content shadow-lg">
                <div class="d-flex justify-content-between align-items-baseline">
                    <img src="/images/location02.jpg" class="rounded-3 img-shadow" width="150" height="150" />
                    <h3 class="text-brown text-shadow">พ.ศ. 1601-1700</h3>
                </div>
                <p>อุทยานประวัติศาสตร์พนมรุ้ง ปราสาทหินพนมรุ้งเป็นโบสถ์พราหมณ์ลัทธิไศวะ มีการบูรณะก่อสร้างต่อเนื่องกันมาหลายสมัย ตั้งแต่ประมาณพุทธศตวรรษที่ 15 ถึงพุทธศตวรรษที่ 17 และในพุทธศตวรรษที่ 18 พระเจ้าชัยวรมันที่ 7 แห่ง...</p>
            </div>
        </div>
        <div class="timeline-container timeline-left">
            <div class="timeline-content shadow-lg">
                <div class="d-flex justify-content-between align-items-baseline">
                    <img src="/images/location01.jpg" class="rounded-3 img-shadow" width="150" height="150" />
                    <h3 class="text-brown text-shadow">พ.ศ. 1601-1700</h3>
                </div>
                <p>อุทยานประวัติศาสตร์พระนครศรีอยุธยา สัมผัสความรุ่งเรืองของกรุงศรีอยุธยา อดีตราชธานีที่ปกครองแผ่นดินมายาวนานถึง 417 ปี ภายในมีโบราณสถานมากมายน่าเที่ยว ทั้ง พระราชวังหลวง วัดพระศรีสรรเพชญ์ วัดราชบูรณะ วัดมหาธาตุ วัดไชย...</p>
            </div>
        </div>
        <div class="timeline-container timeline-right">
            <div class="timeline-content shadow-lg">
                <div class="d-flex justify-content-between align-items-baseline">
                    <img src="/images/location02.jpg" class="rounded-3 img-shadow" width="150" height="150" />
                    <h3 class="text-brown text-shadow">พ.ศ. 1792-2006</h3>
                </div>
                <p>อุทยานประวัติศาสตร์ศรีสัชนาลัย ศรีสัชนาลัย มีพัฒนาการมาแต่สมัยก่อนประวัติศาสตร์ตอนปลาย ประมาณ ๒,๓๐๐ – ๑,๕๐๐ ปีมาแล้ว โดยปรากฏชื่อควบคู่ไปกับเมืองสุโขทัยที่เป็นราชธานี สันนิษฐานว่าเมืองศรีสัชนาลัยมีความสำคัญในฐาน...</p>
            </div>
        </div>
    </div>

    ก็จะได้หน้าเว็บใกล้เคียงความต้องการ ดังรูปตัวอย่างนะครับ สามารถนำไปปรับใช้ โดยการปรับแก้ css และเพิ่มเติมส่วนแสดงผลอื่นๆได้ตามต้องการ

  • Oracle: แปลงข้อมูล JSON เป็น Table หรือ View

    ในการส่งข้อมูลระหว่างแอพพลิเคชัน ในปัจจุบันปฏิเสธไม่ได้ว่ารูปแบบ JSON ได้รับความนิยมสูงมาก วันนี้มีโจทย์ในการพัฒนาระบบอย่างนึงที่ผมเจอคือ ได้ข้อมูลจาก log มาจัดเก็บในฐานข้อมูลซึ่งอยู่ในรูปแบบ JSON แต่ต้องการแยกเก็บเป็นฟิลด์เพื่อนำไปประมวลผล และวิเคราะห์ต่อ

    วิธีการแปลงข้อมูลที่ผมเลือกใช้ คือใช้ฟังก์ชัน JSON_TABLE โดยตัวอย่างข้อมูล JSON มีรูปแบบดังนี้

    {
      "latencies": {
        "request": 232,
        "kong": 12,
        "proxy": 220
      },
      "service": {
        "host": "alist-demo.psu.ac.th",
        "created_at": 1641960693,
        "connect_timeout": 60000,
        "id": "c00d9805-c281-4283-ab68-54e99719634a",
        "protocol": "http",
        "name": "test",
        "read_timeout": 60000,
        "port": 80,
        "path": "/services/commonservice.asmx/GetSite",
        "updated_at": 1646117362,
        "ws_id": "4920834d-f36d-41f6-8e98-959b4ec18b1b",
        "tags": {},
        "retries": 5,
        "write_timeout": 60000
      },
      "request": {
        "querystring": {},
        "size": 226,
        "uri": "/alist/getsite",
        "url": "http://api-gateway.psu.ac.th:8000/alist/getsite",
        "headers": {
          "host": "api-gateway.psu.ac.th:8000",
          "postman-token": "c7ee397e-1570-4e5d-9930-eac24fd2ecbc",
          "user-agent": "PostmanRuntime/7.29.0",
          "accept": "*/*",
          "connection": "keep-alive",
          "accept-encoding": "gzip, deflate, br"
        },
        "method": "GET"
      },
      "client_ip": "192.168.3.174",
      "tries": [
        {
          "balancer_latency": 0,
          "port": 80,
          "balancer_start": 1648009515575,
          "ip": "192.168.100.177"
        }
      ],
      "upstream_uri": "/services/commonservice.asmx/GetSite",
      "response": {
        "headers": {
          "content-type": "text/xml; charset=utf-8",
          "x-powered-by": "ASP.NET",
          "x-kong-proxy-latency": "12",
          "cache-control": "private, max-age=0",
          "x-ratelimit-remaining-minute": "9",
          "date": "Wed, 23 Mar 2022 04:25:15 GMT",
          "via": "kong/2.2.2",
          "x-aspnet-version": "4.0.30319",
          "x-frame-options": "SAMEORIGIN",
          "ratelimit-reset": "45",
          "content-length": "2622",
          "x-ratelimit-limit-minute": "10",
          "server": "Microsoft-IIS/10.0",
          "ratelimit-remaining": "9",
          "ratelimit-limit": "10",
          "x-kong-upstream-latency": "220",
          "connection": "close"
        },
        "status": 200,
        "size": 3109
      },
      "route": {
        "id": "db8cc7a7-77bb-4a3a-a059-06b8de3aa549",
        "paths": [
          "/alist/getsite"
        ],
        "protocols": [
          "http",
          "https"
        ],
        "strip_path": true,
        "created_at": 1641960726,
        "ws_id": "4920834d-f36d-41f6-8e98-959b4ec18b1b",
        "request_buffering": true,
        "name": "test-getsite",
        "updated_at": 1647326716,
        "preserve_host": false,
        "regex_priority": 0,
        "response_buffering": true,
        "https_redirect_status_code": 426,
        "path_handling": "v1",
        "service": {
          "id": "c00d9805-c281-4283-ab68-54e99719634a"
        }
      },
      "started_at": 1648009515563
    }

    จะเห็นได้ว่ามีจำนวน Key และ Value จำนวนมากและอยู่ในรูปแบบ Parent Chid ด้วยแต่สามารถสร้าง View หรือ Table จากข้อมูลดังกล่าวด้วยคำสั่งเดียวดังนี้

      create or replace view v_log_traffic as 
      select t.*
      from log_traffic l,
        json_table(l.detail, '$'
        columns
          service_name path '$.service.name',
          service_path path '$.service.path',
          request_url path '$.request.url',
          client_ip path '$.client_ip',
          response_status path '$.response.status',
          response_size path '$.response.size',
          latencies_request path '$.latencies.request',
          latencies_kong path '$.latencies.kong',
          latencies_proxy path '$.latencies.proxy') t;

    สังเกตุว่า จุดสำคัญคือคำสั่ง json_table ที่เราจะต้องระบุฟิลด์ที่เก็บข้อมูล JSON ของเราดังในตัวอย่างคือ l.detail ที่เดิมเก็บอยู่ในตาราง log_traffic โดยใช้ path ย่อเป็น ‘$’ จากนั้นระบุ Keyword columns และตั้งชื่อ columns สำหรับ view ที่เราจะสร้างเช่น service_name ซึ่งใช้เก็บข้อมูลที่อยู่ใน path ดังนี้ ‘$.service.name’ เป็นต้น ซึ่งเมื่อรันคำสั่งดังกล่าวจะได้ view ดังรูป

    ในเรื่องของประสิทธิภาพพบว่าประมวลผลจำนวน 4000+ รายการใช้เวลาประมาณ 0.0008 วินาที ซึ่งถือว่ารวดเร็ว แต่ถ้าจำนวนข้อมูลมากขึ้น และประมวลผลช้าลงอาจปรับเป็น Materialize View หรือวิธีการอื่นๆ ที่ใช้ในการประมวลผลเฉพาะรายการที่เพิ่มใหม่ หรือประมวลผลตามรอบเวลา (ไม่เรียลไทม์) โดยใช้คำสั่งดังกล่าวช่วยก็ได้เช่นกัน หวังว่าจะเป็นประโยชน์สำหรับท่านที่มีโจทย์ประมาณนี้นะครับ

  • Flutter : ดึงข้อมูลจาก RESTFul API

    สำหรับนักพัฒนาแอปพลิเคชันในตอนนี้ คงไม่มีใครไม่รู้จัก Flutter อย่างแน่นอนนะครับ ซึ่งตอนนี้ก็ออกเวอร์ชัน 2.2 มาแล้ว สำหรับผมเองที่เพิ่งเริ่มศึกษาก็ขอเริ่มด้วยการทดสอบดึงข้อมูลจาก API เป็นอย่างแรกนะครับ เพราะแอปพลิเคชันสำหรับใช้งานด้านต่างๆ ในองค์กรมักจะต้องใช้ข้อมูลจากฐานข้อมูล โดยการเชื่อมต่อผ่าน API เป็นหลักครับ

    ขั้นตอนการติดตั้งบน Windows และเตรียมเครื่องมือสามารถอ่านได้ที่ https://flutter.dev/docs/get-started/install/windows

    ในบทความนี้ผมใช้ Visual Studio Code นะครับโดยติดตั้ง Extention เพิ่มเติมดังนี้

    1. เปิด Visual Studio Code
    2. คลิกเมนู View > Command Palette
    3. พิมพ์ “install”, จากนั้นเลือก Extensions: Install Extensions.
    4. พิมพ์ “flutter” เลือก install
    5. พิมพ์ “dart” เลือก install
    6. เมื่อติดตั้งเสร็จเรียบร้อยจะมีรายการ Extention ดังรูปครับ

    จากนั้นทำการสร้างโปรเจค ดังนี้ครับ

    1. คลิกเมนู View > Command Palette
    2. พิมพ์ “flutter”, จากนั้นเลือก Flutter:New Application Project
    3. จะได้โปรเจคที่มีโครงสร้างไฟล์ดังรูปครับ

    สำหรับท่านใดที่อยากเห็นหน้าตาแอปพลิเคชันเริ่มต้น ให้เปิด USB Debuging ที่มือถือให้เรียบร้อย ต่อเข้ากับคอมพิวเตอร์ จากนั้นที่มุมขวามือล่างของ Visual Studio Code ให้เลือกชื่อมือถือของท่าน จากนั้นกด F5 ได้เลยครับ

    ในการเชื่อมต่อกับ API เราจะต้อง import package เพิ่มโดยพิมพ์คำสั่งที่ terminal > cmd ดังรูปครับ

    จากนั้นแก้ไขในไฟล์ main.dart โดยเริ่มจากการ import ดังนี้ครับ

    import 'dart:async';
    import 'dart:convert';
    
    import 'package:flutter/material.dart';
    import 'package:http/http.dart' as http;
    

    ด้านล่างเป็น Code ที่เชื่อมต่อ API โดยใช้ http.get เพื่อดึงข้อมูลแบบ Get จากนั้นก็ return ค่า response โดยใช้ประเภท Future เทียบกับภาษาอื่นๆ ประเภทข้อมูลนี้ก็คือ CallBack นั้นเองครับ เอาไว้อ่านค่าที่ได้จาก api เมื่อได้ข้อมูลมาแล้ว (ในอนาคต ไม่รู้ตอนไหน เมื่อเสร็จจะบอก ประมาณนั้น) ซึ่งข้างในเป็นค่าประเภท dynamic ที่ได้จาก jsonDecode

    Future<dynamic> fetchAlbum() async {
      final response =
          await http.get(Uri.parse('https://jsonplaceholder.typicode.com/albums'));
    
      if (response.statusCode == 200) {
        return jsonDecode(response.body);
      } else {
        throw Exception('Failed to load album');
      }
    }

    ตรงนี้เป็นการสร้าง Widget สำหรับแสดงข้อมูลเป็นแถวๆ

    Widget _buildRow(String dataRow) {
      return ListTile(
        title: Text(
          dataRow,
          style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
        ),
      );
    }

    ต่อไปก็จะเป็น Main Function ที่มีการประกาศ state เพื่อเก็บข้อมูลไว้แสดงผลในรูปแบบ ListView เมื่อได้ข้อมูลจาก API มาแล้วนะครับ

    void main() => runApp(MyApp());
    
    class MyApp extends StatefulWidget {
      MyApp({Key? key}) : super(key: key);
    
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      late Future<dynamic> futureAlbum;
    
      @override
      void initState() {
        super.initState();
        futureAlbum = fetchAlbum();
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Fetch Data Example',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Scaffold(
            appBar: AppBar(
              title: Text('Fetch Data Example'),
            ),
            body: Center(
              child: FutureBuilder<dynamic>(
                future: futureAlbum,
                builder: (context, snapshot) {
                  if (snapshot.hasData) {
                    return ListView.builder(//สร้าง Widget ListView
                        padding: EdgeInsets.all(16.0),
                        itemBuilder: (context, i) {
                           //หากไม่สร้าง Object สามารถเรียกใช้งานแบบนี้ได้เลย
                          return _buildRow(snapshot.data[i]["title"].toString()); 
                        });
                  } else if (snapshot.hasError) {
                    return Text("${snapshot.error}");
                  }
    
                  // รูป Spiner ขณะรอโหลดข้อมูล
                  return CircularProgressIndicator();
                },
              ),
            ),
          ),
        );
      }
    }

    เมื่อ Run ดูก็จะได้หน้าตาประมาณนี้ครับ

    เรียกได้ว่าสัมผัสแรกกับ Flutter รู้สึกประทับใจทั้งในด้าน Extension ที่มีใน Visual Studio Code ให้ความรู้สึกไม่ต่างจากการพัฒนาด้วย .Net Framwork เพราะสามารถ Debug ได้ มี Intellisense ครบถ้วน ในด้าน Syntax เนื่องจากเป็นภาษาใหม่ยังต้องทำความเข้าใจอีกพอสมควร ในด้านการออกแบบ UI สำหรับท่านใดที่เคยใช้ React Native มาน่าจะพอเข้าใจ Concept ของ View Widget (เทียบเท่า Component) ได้ไม่ยากนัก ถ้าหากได้นำมาใช้ในงาน Production จริงๆ แล้วจะนำประเด็นที่น่าสนใจอื่นๆมาแชร์กันเพิ่มเติมครับ

    อ้างอิง

    https://flutter.dev/docs/get-started/codelab

    https://flutter.dev/docs/cookbook/networking/fetch-data

  • ตรวจสอบ Battery ด้วยคำสั่งเดียว (Windows OS)

    เคยมั้ยครับอยากรู้ว่า Battery ของ Notebook ที่เราใช้งานอยู่ Design Capacity จากโรงงานเท่าไหร่ Full Charge Capacity ตอนนี้เหลือเท่าไหร่ ปกติก็ต้องหาโหลดโปรแกรม ติดตั้งและค่าที่ได้ออกมาบางทีก็ไม่ครบถ้วน เสี่ยงต่อมัลแวร์ ต้องเลือกแหล่งที่มาดีๆ สำหรับท่านที่ใช้งานระบบปฏิบัติการ Windows นั้น มีเครื่องมือที่ชื่อว่า powercfg ติดมาอยู่แล้วนะครับ จริงๆความสามารถของมันที่เกี่ยวกับการจัดการพลังงานของเครื่องมีเยอะมากครับ สามารถใช้คำสั่ง powercfg /? เพื่อเรียกดูความสามารถอื่นๆได้ โดยในบทความนี้ผมจะแนะนำวิธีการออก Battery report เพื่อตรวจสอบ Battery ของเราครับ

    • เปิด cmd ขึ้นมา (พิมพ์ค้นหาว่า cmd )
    • พิมพ์คำสั่ง powercfg /batteryreport /output “c:\battery-report.html”

  • เสร็จเรียบร้อยครับเราก็จะได้ไฟล์ battery-report.html อยู่ที่ c:\ สามารถเปลี่ยนชื่อไฟล์ หรือที่จัดเก็บได้
  • ไฟล์ดังกล่าวสามารถเปิดดูด้วย Browser ได้ทุกตัวครับ โดยในไฟล์ดังกล่าวจะประกอบไปด้วย

    ข้อมูลเครื่องคอมพิวเตอร์ของเรา
    ข้อมูล Battery ชื่อรุ่นและสิ่งที่เราอยากทราบนะครับ จากโรงงานความจุเท่าไหร่ (Design Capacity) ตอนนี้ชาร์ทเต็มแล้วได้เท่าไหร่ (Full Charge Capacity)
    ถ้าใครอยากทราบประวัติก็มีให้ดูครับ (ลดเร็วมาก เนื่องจากผมใช้งานแบบเสียบสายชาร์ทไว้ตลอดเวลา)
    สุดท้ายคือค่าประมาณเวลาที่ Battery ใช้งานได้ โดยช่องแรกคือเวลาที่ใช้ปกติ ช่องที่สองคือเวลา Stand by โดยคิดจาก Full Charge Capacity ช่องที่สาม สี่ คิดจาก Design Capacity ซึ่งคิดจากประวัติการใช้งานเครื่องของเราครับ (น่าจะเป็นค่าเฉลี่ย เปิดโปรแกรมที่ใช้พลังงานเยอะๆ ตลอดเวลาก็น่าจะต่ำกว่านี้)
  • อัพโหลดแอปพลิเคชันไปยัง App Store ด้วย Transporter

    สำหรับนักพัฒนาแอปพลิเคชัน เพื่อให้บริการบนระบบปฏิบัติการ iOS, iPadOS ฯลฯ ของ Apple นั้น ทราบกันดีอยู่แล้วว่าต้องอัพโหลดขึ้นไปยัง App Store เพื่อให้ผู้ใช้เข้าไปค้นหาและดาวส์โหลดไปใช้งาน ทั้งนี้ช่องทางการอัพโหลดแบบปกติ (Native App ที่พัฒนาด้วย Xcode อยู่แล้ว หรือ เฟรมเวิร์คที่ Export ออกมาเป็น Xcode โปรเจค) คือ การใช้ขั้นตอน Build Archive เมื่อสำเร็จก็จะสามารถเลือก Distribute App ไฟล์ Build ก็จะอัพโหลดขึ้นไปรอที่ appstoreconnect.apple.com ให้อัตโนมัติ เพื่อเข้ากระบวนการ Review App ต่อไป

    ทั้งนี้ในปัจจุบันเฟรมเวิร์คที่ใช้พัฒนาครั้งเดียวสามารถให้บริการได้หลายระบบปฏิบัติการ (Cross Platform) เป็นที่นิยมอย่างมาก เช่น Flutter, React Native, Xamarin ซึ่งมักจะมีเครื่องมือที่ช่วย Build เป็นไฟล์ .ipa ที่ใช้สำหรับอัพโหลดมาเลย ซึ่งในปัจจุบันไม่สามารถใช้ Xcode อัพโหลดไฟล์นี้ได้โดยตรง (ยังใช้ Terminal รันคำสั่งเพื่ออัพโหลดได้ และจำเป็นต้องติดตั้ง Xcode) จึงมีแอปพลิเคชันที่ทำหน้าที่ตรงนี้ และใช้งานง่ายมาแนะนำครับ นั้นคือ Transpoter

    ก่อนอัพโหลดไฟล์ด้วยแอปพลิเคชันนี้ ต้อง

    1. มี Apple ID ที่ลงทะเบียนเป็นนักพัฒนาเรียบร้อยแล้ว
    2. เข้าไปที่ developer.apple.com เลือก Account
    3. ทำการเพิ่ม Certificates, Identifiers & Profiles ให้เรียบร้อย
    4. ดาวส์โหลด และติดตั้ง Certificate และ Profile บนเครื่องที่จะใช้อัพโหลด (กรณีใช้ Expo จะต้องอัพโหลดตอนสั่ง Build)
    5. สร้าง App Record บน appstoreconnect.apple.com รอไว้ (แค่มีชื่อแอปไว้ก็เพียงพอแล้ว ยังไม่จำเป็นต้องมีรายละเอียดครบถ้วน)

    ขั้นตอนด้านบนจริงๆแล้วอัพโหลดด้วยวิธีไหนก็ต้องทำนะครับ และจะต้องมีการ Config ค่า Bundle ID, Team ID ให้ถูกต้องโดยขึ้นอยู่กับเครื่องมือที่ใช้พัฒนาว่าจะต้อง Config ค่าที่จุดไหน ไว้โอกาศหน้าผมจะมาลงรายละเอียดในส่วนนี้ครับ เมื่อทุกอย่างเรียบร้อยแล้ว ให้ทำการดาวส์โหลดแอปพลิเคชัน Transporter และติดตั้ง เมื่อเปิดแอปพลิเคชันขึ้นมาจะมีหน้าจอให้ Login ดังรูป

    จากนั้น ลากไฟล์ .ipa ของเรามายังหน้าจอของแอป กระบวนการ Validate ไฟล์ และอัพโหลดไปยัง App Store จะทำอัตโนมัติทั้งหมด

    หากเกิดข้อผิดพลาด จะมีหน้าต่างแจ้งเตือน หรือหากต้องการดู Log ก็มีให้เรียกดูได้ ดังภาพครับ

    เป็นอันเรียบร้อยครับ สามารถไปตรวจสอบว่า Build ของเราปรากฏอยู่บน appstoreconnect แล้วหรือยังโดยการกดปุ่ม … เลือก Open Appstoreconnect ได้เลย

    จริงๆแล้วบทความนี้จะเห็นได้ว่า เพียงแค่แนะนำให้รู้จักกับแอปพลิเคชันตัวนี้เท่านั้นครับ วิธีการใช้งานนั้นแค่ลากวางเป็นอันเสร็จ (ถ้าขั้นตอนอื่นๆถูกต้อง) โดยเฉพาะท่านใดที่ใช้ React Native แล้วใช้ Expo Server ในการ build เมื่อได้ไฟล์ .ipa มา ท่านไม่จำเป็นต้องโหลด Xcode ที่ขนาดมโหฬาร (ตัวติดตั้งอย่างเดียว 18GB ต้องมีพื้นที่ว่างในเครื่อง 40GB ถึงจะยอมให้ติดตั้งผ่าน Appstore) มาเพื่ออัพโหลดไฟล์เพียงอย่างเดียว หวังว่าบทความนี้จะเป็นประโยชน์ครับ

  • กำหนด Lexer สำหรับ Full Text Search บน ฐานข้อมูล Oracle เพื่อค้นหาภาษาไทยให้ถูกต้อง

    เนื่องจากระบบสืบค้นที่ดูแลอยู่เจอปัญหาค้นหาเลขไทย “๑ ๒ ๓ …” ไม่เจอ หลังจากตรวจสอบจนแน่ใจแล้วว่าก่อนจะส่งคำสั่ง Query ไปยังฐานข้อมูลไม่ได้เผลอตัดเลขไทยออกที่ขั้นตอนไหน จึงทำการตรวจสอบคำสั่งที่ใช้ในการค้นหา พบว่าใช้ฟังก์ชัน

    SELECT * FROM THAI_LIBRARY WHERE CONTAINS(BOOK_NAME, '๑๐๐ ปีชาติไทย', 1) > 0;

    จากคำสั่ง (ที่สมมุติขึ้น) ด้านบนจะเห็นได้ว่าใช้ CONTAINS ซึ่งเป็นฟังก์ชันที่อยู่ในกลุ่ม Oracle Text ซึ่งฟังก์ชันนี้จะค้นหาคำใกล้เคียงจาก Index แล้วคืนค่า Score มาให้เราเพื่อใช้เป็นเงื่อนไขพิจารณาว่าจะใช้ข้อมูลรายการนั้นหรือไม่

    ที่มาภาพ

    ภาพด้านบนแสดงขั้นตอนการสร้าง Oracle Text Index เนื่องจากระบบจัดเก็บข้อมูลเป็น Text อยู่แล้วจึงไม่มีการกำหนด Fillter, Sectioner ทำให้จุดที่ต้องตรวจสอบว่า เลขไทยเราหายไปจาก Index ได้ยังไงเหลืออยู่คือ Lexer ที่จะเป็นตัวกำหนด Wordlist, Stoplist ในการทำ Index ต่อไป ไปดูว่ามี Lexer อะไรบ้าง

    จากตารางด้านบน เนื่องจากฐานข้อมูลของระบบที่ดูแลอยู่ประกอบไปด้วย ภาษาไทย ภาษาอังกฤษ เป็นหลัก และอาจจะมีภาษาอื่นๆปนอยู่ด้วย Lexer ที่น่าจะใช้ได้คือ AUTO_LEXER, MULTI_LEXER, WORLD_LEXER หลังจากได้ทดสอบกำหนดค่า Lexer ให้กับฐานข้อมูล และทดสอบค้นหาด้วย เลขไทย พบว่าจะต้องใช้ WORLD_LEXER จึงจะสามารถรองรับกรณีนี้ได้ โดยใช้คำสั่งดังนี้

    EXEC CTX_DDL.CREATE_PREFERENCE('WorldLex', 'world_lexer');

    DROP INDEX USER01.IDXFT_THAI_LIBRARY_BOOKNAME;
    CREATE INDEX USER01.IDXFT_THAI_LIBRARY_BOOKNAME ON USER01.THAI_LIBRARY(BOOK_NAME)
    INDEXTYPE IS CTXSYS.CONTEXT
    PARAMETERS('LEXER WorldLex STOPLIST CTXSYS.EMPTY_STOPLIST SYNC(ON COMMIT)')
    NOPARALLEL;

    USER01 คือ User ของฐานข้อมูล Oracle

    THAI_LIBRARY คือ ชื่อตาราง

    BOOK_NAME คือ ชื่อคอลัมภ์ ที่ต้องการทำ Index

    IDXFT_THAI_LIBRARY_BOOKNAME ชื่อ ตาราง index

    ผลพลอยได้ จากการปรับในครั้งนี้พบว่าเดิมต้องทำการตัดคำให้เรียบร้อย (เนื่องจากค่า Default คือ Basic Lexer ที่แบ่งคำด้วยช่องว่างเท่านั้น) เพื่อค้นหา แต่เมื่อปรับ Lexer ให้ถูกต้องสามารถส่งคำค้นเป็นประโยคยาวๆ ไปค้นหาได้เลย หวังว่าบทความนี้จะเป็นประโยชน์กับท่านที่ใช้งาน Full Text Search ของ Oracle และประสบปัญหาคล้ายๆกันนี้ครับ

  • Migrate จากฐานข้อมูล MySql มายัง Oracle ด้วย Sql Developer

    เนื่องจากงานที่รับผิดชอบ จะต้องมีการโอนย้ายข้อมูลจากฐานข้อมูลอื่นๆมายัง Oracle เป็นประจำ พบว่าการย้าย MySql มายัง Oracle นั้นสามารถทำได้ง่ายมาก (อาจเพราะเจ้าของเดียวกัน) โดยมีวิธีดังนี้

    1. ดาวส์โหลดและติดตั้ง Oracle SQL Developer

    2. ทำการเชื่อมต่อไปยังฐานข้อมูล Oracle ด้วย User system

    3. สร้าง Oracle User สำหรับเก็บข้อมูลจาก MySql และกำหนดสิทธิให้เรียบร้อย

    4. ดาวส์โหลดไฟล์ Third Party JDBC Driver สำหรับ My Sql

    5. เปิดการใช้งาน Third Party JDBC Driver โดยไปที่ Tools > Preferences > Database > Third Party JDBC Drivers

    6. ทำการ Restart โปรแกรม Sql Developer เมื่อทำการ New Connection จะมีตัวเลือกเพื่อเชื่อมต่อไปยัง My Sql

    7. ทำการเชื่อมต่อไปยัง My Sql  หากต้องการเพียง Data, Schema สามารถคลิกขวาตารางที่ต้องการเลือก Copy To Oracle ได้เลย

    เพียงเท่านี้ ข้อมูล Table, Field ก็จะถูกโอนย้ายและ Map Data Type ให้อัตโนมัติสามารถ Query จาก Oracle ได้เลย

    แต่สำหรับงานที่ต้องการ Constraint, Trigger, ฯลฯ ด้วย จะมีขั้นตอนเพิ่มเติมดังนี้

    1. ไปที่  Tools > Migration > Migrate กำหนด User ที่จะใช้เป็น Migrate Repository (เก็บข้อมูลต่างๆขณะดำเนินการ Migrate)

    2. กำหนดโฟลเดอร์จัดเก็บ Script, Log file

    3. เลือก Connection My Sql ที่ต้องการ Migrate

    4. เลือก My Sql User ที่ต้องการ Migrate

    5. การ Map Data Type สามารถใช้ค่า Default ได้

    6. เลือก Sql Object ที่ต้องการ

    7. เลือก Connection ของ DB User ที่ใช้เก็บโครงสร้าง และคำสั่งต่างๆ ที่ระบบใช้ในการ Migrate

    8. กำหนด User เป้าหมายที่จะนำข้อมูลเข้า จากนั้นเลือก Finish

    เพียงเท่านี้ก็เรียบร้อยครับ ข้อควรระวังคือการเลือก Repository User ควรสร้างขึ้นมาใหม่ เนื่องจากจะมีการสร้าง Table ขึ้นมาระหว่างการ Migrate และการกำหนดสิทธิให้กับ User ต่างๆให้ครบถ้วน แบบที่สองดูเหมือนหลายขั้นตอน แต่ก็เป็นแบบ Wizard ที่ใช้งานได้ไม่ยาก หวังว่าบทความนี้จะเป็นประโยชน์ครับ

  • การทำ Partial Rendering สำหรับ User Controls

    สำหรับท่านใดที่ยังใช้งาน ASP.NET Web From ในการพัฒนาเว็บไซต์อยู่ คงคุ้นเคยกับการใช้งาน User Controls เป็นอย่างดีเนื่องจากเป็นการสร้าง UI เพื่อใช้งานซ้ำในหลายๆ Page ซึ่งเป็นที่นิยม สำหรับบทความนี้จะแนะนำวิธีการ แยกเรนเดอร์ User Controls ที่มีปัญหาโหลดช้าอันเนื่องมาจากสาเหตุใดก็ตาม และเป็นข้อมูลเพิ่มเติมที่ผู้ใช้ไม่จำเป็นต้องเห็นเป็นอย่างแรก โดยมีขั้นตอนดังนี้

    1.เพิ่ม HTML Container element เช่น Div, Panel ในหน้าจอ ที่ต้องการใช้งาน เช่น

    2.เพิ่ม Ajax Controls toolkit : Dynamic Populate ใช้เพื่อ Render HTML หลังจากที่ Page Load เสร็จเรียบร้อย

    • TargetControlsID คือ ID ของ HTML Container element ที่เราต้องการแสดงผล HTML ของ User Controls ที่เราสร้างมาจาก Web Service
    • ServicePath คือ Path ของ Web Service ที่เรียกใช้งาน
    • ServiceMethod คือ Web Method ที่ใช้ Render User Controls
    • UpdatingCssClass คือ CSS Class ที่ต้องการให้แสดงระหว่างรอ Load HTML

    3.Java Script เรียกใช้งาน  Ajax Controls toolkit : Dynamic Populate และเพิ่มเติมฟังก์ชันที่ต้องการขณะแสดงผล User Controls

    4.สร้าง Web Method สำหรับสร้าง HTML ของ User Controls

    • รูปแบบของ Web Service Method ซึ่งจะต้องมี Return Type เป็น string และรับ Parameter ชื่อ contextKey ซึ่งมี Type เป็น string เช่นกัน
    • เรียก Method ชื่อ RenderUserControl โดยระบุ Path ของ User Control ที่ต้องการ และ List ของ Property ทั้งหมดของ User Control

    5.Method ที่ใช้ในการสร้างเนื้อหา HTML จาก User Controls

    เพียงเท่านี้ เราก็สามารถเรียกใช้งาน User Control นี้ในหน้าจอต่างๆ ได้โดยไม่ต้องกังวลเรื่องเพิ่มเวลาโหลดให้กับหน้าจอแสดงผลนั้นๆแล้วครับ อย่างไรก็ดีบทความนี้ผมได้เขียนไว้นานมากแล้ว แต่ยังไม่ได้นำมาเผยแพร่ ในปัจจุบันอาจมีวิธีการอื่นๆ และ ASP.NET ที่เป็น Web Form อาจจะได้รับความนิยมน้อยลงไปแล้ว แต่สำหรับท่านที่ต้องดูแล และปรับปรุงระบบเก่าๆ ไม่สามารถเขียนขึ้นมาใหม่ทั้งหมดได้ ก็ยังสามารถนำเทคนิคนี้ไปปรับใช้กับระบบของตนเองได้ครับ