Tag Archive update

Byphunsanit

Linux: upgrade PHP version ล่าสุด

php ก็เหมือนโปรแกรมอื่น ๆ จะมีการอัพเดต อัพเกรดเวอร์ชั่นเป็นระยะเพื่อเพิ่มความสามารถและความปลอดภัย การอัพเดตใน linux และ wsl สามารถทำได้ง่าย ๆ

  1. Update ระบบโดยรวมก่อน

    sudo apt update && sudo apt -y upgrade
  2. เพิ่ม Surý PHP APT repository

    sudo apt update

    sudo apt install -y lsb-release gnupg2 ca-certificates apt-transport-https software-properties-common

    sudo add-apt-repository ppa:ondrej/php
  3. ติดตั้ง PHP ตัวล่าสุด

    sudo apt install php8.2
  4. เช็ค php version โดย

    php -v
  5. ติดตั้ง extension เพิ่มเติม เช่น

    sudo apt-get install -y libapache2-mod-php8.2 libphp8.2-embed mcrypt openssl php-bcmath php-cli php-common php-curl php-fpm php-gd php-gmp php-intl php-mbstring php-mysql php-tokenizer php-xml php-xmlrpc php-zip php8.2 php8.2-bcmath php8.2-cgi php8.2-cli php8.2-common php8.2-curl php8.2-dev php8.2-fpm php8.2-gd php8.2-imagick php8.2-imap php8.2-mbstring php8.2-mysql php8.2-phpdbg php8.2-soap php8.2-xml php8.2-xmlrpc php8.2-zip
  6. ผูก Apache และ PHP

    sudo apt install apache2 php-fpm

ดูเพิ่มเติม How To Install PHP 8.2 on Ubuntu 22.04|20.04|18.04

Byphunsanit

SQL Server: update limit

เทสงานที่มีการบันทึกข้อมูลไว้ ถ้าจะทำอีกครั้งก็ต้อง update กลับไปเป็นเหมือนเดิม แต่เพราะว่าเงื่อนไขมันกว้างมาก ๆ เลยไปอัพเดตทั้ง table เลยมันช้าไป เปลืองทรัพยากร เลยหาวิธีอื่นดู

  • ROWCOUNT อย่างต้องการให้ update แค่ 100 รายการแรก
    • SET ROWCOUNT 100;
      UPDATE table_name
      SET colunm_name = …
      WHERE column_name = …
      SET ROWCOUNT 0;
  • TOP จะง่ายกว่าหน่อย
    • UPDATE TOP(100) table_name
      SET colunm_name = …
      WHERE column_name = …
  • CTE
    • ;WITH CTE AS
      (
      SELECT TOP 100 *
      FROM table_name
      ORDER BY colunm_name
      )
      UPDATE CTE SET colunm_name = …
  • Sub Query
    • UPDATE table_name
      SET column_name =…
      WHERE column_id IN (
      SELECT TOP 100 column_id
      FROM table_name
      WHERE column_name = 0
      )

เลือกเอาแบบที่ชอบได้เลยครับ ตัวที่ผมชอบที่สุดคือ ROWCOUNT เพราะว่าใช้ query จริง ๆ มาวางได้เลย

Byphunsanit

Windows: install error 0x800f0954

ถ้าลงโปรแกรมเก่า ๆ บางครั้งจะเจอ error 0x800f0954 ซึ่งสาเหตุคือ เราไปลงโปรแกรมที่เก่ากว่า version ปัจจุบัน อย่าง Poor Man’s T-SQL Formatter จะใช้ .net2 ที่เก่ามากหลายปีแล้ว ทำให้เจอ error code 0x800f0954

ก่อนอื่นโหลดตัวติดตั้ง .net 2 มาก่อน แต่เพราะว่ามันเก่ามากจนไม่สามารถหาตัวติดตั้งจาก microsoft ได้ ดังนั้นจะโหลดตัวติดตั้ง .Net 3.5 จาก Microsoft .NET Framework 3.5 ที่จะมี .NET Framework 2.0 และ .NET Framework 3.0 service pack ในตัวแทน

การเตรียมการติดตั้ง

  1. เปิดโปรแกรม regedit โดยไปที่เมนูของ windows พิมพ์ regedit.exe ในช่อง run หรือ search แล้ว enter
  2. เปิดคีย์ตามลำดับ HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU
  3. ดูที่ด้านขวาจะมี value ชื่อ UseWUServer เซ็ตค่าเป็น 0
  4. ปิด regedit
  5. restart windows หรือ restart service Windows Update

Cr. Error Code 0x800F0954 on Windows 10

Byphunsanit

slick: carousel / slideshow จาก ajax

ตัวอย่างการแก้ไข carousel หรือ slideshow โดย update slide จาก ajax

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <title>kenwheeler.slick: ajax update</title>
    <link href="../vendor/components/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
    <link href="../vendor/kenwheeler/slick/slick/slick.css" rel="stylesheet" type="text/css" />
    <link href="../vendor/kenwheeler/slick/slick/slick-theme.css" rel="stylesheet" type="text/css" />
    <link href="theme.css" rel="stylesheet" type="text/css" />
</head>

<body>
    <div id="slideshowA"></div><br>
    <select id="order_by">
    <option value="asc">น้อยไปมาก</option>
    <option value="desc">มากไปน้อย</option>
    <option value="random">สุ่ม</option>
    </select>
    <script src="../vendor/components/jquery/jquery.min.js"></script>
    <script src="../vendor/kenwheeler/slick/slick/slick.min.js"></script>
    <script>
        $(function() {

            var order_by = $('#order_by');
            var slideshowA = $('#slideshowA');

            function getSliderSettings() {
                return {
                    "autoplay": false,
                    "autoplaySpeed": 3000,
                    "centerMode": true,
                    "infinite": false,
                    "slidesToScroll": 4,
                    "slidesToShow": 4,
                    "speed": 300,
                    "variableWidth": true,
                    "zIndex": 2
                }
            }

            function getSlideShow() {

                $.ajax({
                    "data": {
                        "order_by": order_by.val(),
                    },
                    "success": function(data, textStatus, jqXHR) {

                        /* add items */
                        $.each(data.datas, function(index, value) {
                            slideshowA.append('<div><b>' + index + '</b><img alt="' + index + '" src="' + value + '"><p>แสดง ' + index + ' จาก http://www.avatarsdb.com</p></div>');
                        });

                        slideshowA.slick('unslick'); /* ONLY remove the classes and handlers added on initialize */
                        $('.my-slide').remove(); /* Remove current slides elements, in case that you want to show new slides. */
                        slideshowA.slick(getSliderSettings()); /* Initialize the slick again */

                        alert('ทดลองดูครับ');

                    },
                    "url": "ajax.php",
                });

            }

            order_by.change(function() {
                getSlideShow();
            });

            getSlideShow();
            slideshowA.slick(getSliderSettings());
        });
    </script>
</body>

</html>

ไฟล์ที่ส่งข้อมูลกลับมาให้

<?php
$datas = [];

if (isset($_GET['order_by'])) {
    $order_by = $_GET['order_by'];
} else {
    $order_by = 'asc';
}

$datas['Acrobat'] = 'http://www.avatarsdb.com/avatars/acrobat.gif';
$datas['Cat Rain'] = 'http://www.avatarsdb.com/avatars/cat_rain.gif';
$datas['Dota Windranger'] = 'http://www.avatarsdb.com/avatars/dota_windranger.jpg';
$datas['Fighting Funny'] = 'http://www.avatarsdb.com/avatars/fighting_funny.gif';
$datas['Fire 01'] = 'http://www.avatarsdb.com/avatars/fire_01.gif';
$datas['German Shepherd Puppy'] = 'http://www.avatarsdb.com/avatars/german_shepherd_puppy.jpg';
$datas['Girl With Cigarette'] = 'http://www.avatarsdb.com/avatars/girl_with_cigarette.jpg';
$datas['Hidden Cat'] = 'http://www.avatarsdb.com/avatars/hidden_cat.jpg';
$datas['Im Fabulous'] = 'http://www.avatarsdb.com/avatars/im_fabulous.jpg';
$datas['One Direction'] = 'http://www.avatarsdb.com/avatars/One_Direction.jpg';
$datas['Panda Kiss'] = 'http://www.avatarsdb.com/avatars/panda_kiss.gif';
$datas['PC User'] = 'http://www.avatarsdb.com/avatars/pc_user.gif';
$datas['Riri Queen'] = 'http://www.avatarsdb.com/avatars/riri_queen.gif';
$datas['Tennessee'] = 'http://www.avatarsdb.com/avatars/tennessee.jpg';
$datas['Tuxedo M'] = 'http://www.avatarsdb.com/avatars/Tuxedo_m.jpg';
$datas['Tuxedo Mask'] = 'http://www.avatarsdb.com/avatars/tuxedo_mask.jpg';
$datas['Ugly Face'] = 'http://www.avatarsdb.com/avatars/ugly_face.gif';
$datas['Waifu'] = 'http://www.avatarsdb.com/avatars/waifu.jpg';
$datas['Wolf In The Snow'] = 'http://www.avatarsdb.com/avatars/wolf_in_the_snow.jpg';
$datas['Xerxes Break Kevin'] = 'http://www.avatarsdb.com/avatars/xerxes_break_kevin.jpg';

switch ($order_by) {
    case 'desc':{krsort($datas);}
        break;

    case 'random':{
            $keys = array_keys($datas);
            shuffle($keys);
            $random = [];
            foreach ($keys as $key) {
                $random[$key] = $datas[$key];
            }
            $datas = $random;
        }break;

    case 'asc':
    default:
        {ksort($datas);}
        break;
}

header('Content-type: application/json; charset=utf-8');
echo json_encode([
    'datas' => $datas,
    'order_by' => $order_by,
]);

code บางส่วนจะอยู่ในบทความ slick: สร้าง carousel / slideshow

Credit: Reinitialize Slick js after successful ajax call

Byphunsanit

update table อัตโนมัติ

สามารถให้ mysql หรือ MariaDB update หรือแก้ไขข้อมูลได้โดยอัตโนมัติทุกครั้งที่ insert, update และ delete ได้โดยใช้ trigger ช่วย จะยกตัวอย่างโดยสมมุติตาราง users โดยถ้า มีการเพิ่มข้อมูลมาใหม่ให้สุ่ม salt และ hash รหัสผ่านให้โดยอัตโนมัติ และเพื่อความปลอดภัยให้เปลี่ยน salt และ hash ใหม่ทุกครั้งที่ password เปลี่ยนไป ถ้าไม่เข้าใจว่า salt คืออะไร ขอเชิญอ่านจากเรื่อง login แบบปลอดภัย

สร้างตาราง users ก่อนโดยใช้

CREATE TABLE `users` (
  `user_id` int(11) NOT NULL,
  `username` varchar(30) NOT NULL,
  `password` char(32) NOT NULL,
  `salt` char(5) NOT NULL,
  `password_hash` char(32) NOT NULL,
  `email` varchar(254) NOT NULL,
  `date_create` datetime DEFAULT NULL,
  `date_update` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `users`
  ADD PRIMARY KEY (`user_id`);

ALTER TABLE `users`
  MODIFY `user_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1;

สร้าง trigger โดยใช้โครงสร้าง

DELIMITER $$
CREATE TRIGGER `ชื่อ trigger ` AFERT หรือ BEFORE INSERT หรือ UPDATE หรือ DELETE ON `ตาราง ` FOR EACH ROW BEGIN
...
SET NEW.`ชื่อฟิลย์ ` = ค่าใหม่;
...
END
$$
DELIMITER ;
...
.OLD
ค่าใน record เดิมก่อน update
.NEW
ค่าใหม่ที่จะเข้ามา update เปลี่ยนค่าเดิม

trigger ของตาราง users ในแบบที่เราต้องการจะเขียนเป็น

--
-- Triggers `users`
--
DELIMITER $$
CREATE TRIGGER `users_passwordhash_insert` BEFORE INSERT ON `users` FOR EACH ROW BEGIN
 SET @string := 'abcdefghijklmnopqrstuvwxyz0123456789';
    SET @i := 1;
    SET @random := '';

    WHILE (@i <= 5) DO
        SET @random := CONCAT(@random, SUBSTRING(@string, FLOOR(RAND() * 36 + 1), 1));
        SET @i := @i + 1;
    END WHILE;

SET NEW.`date_create` = NOW();
SET NEW.`salt` = @random;
SET NEW.`password_hash` = MD5(CONCAT(NEW.`password`, @random));
END
$$
DELIMITER ;
--
DELIMITER $$
CREATE TRIGGER `users_passwordhash_update` BEFORE UPDATE ON `users` FOR EACH ROW BEGIN
 SET @string := 'abcdefghijklmnopqrstuvwxyz0123456789';
    SET @i := 1;
    SET @random := '';

    WHILE (@i <= 5) DO
        SET @random := CONCAT(@random, SUBSTRING(@string, FLOOR(RAND() * 36 + 1), 1));
        SET @i := @i + 1;
    END WHILE;

SET NEW.`date_update` = NOW();
SET NEW.`salt` = @random;
SET NEW.`password_hash` = MD5(CONCAT(NEW.`password`, @random));
END
$$
DELIMITER ;

ทดลองเพิ่ม และเปลี่ยนข้อมูลดูครับตัว salt และ password_hash จะต้องเปลี่ยนทุกครั้ง ในการใช้งานจริง ให้ลบฟิลย์ password ออกและแก้ trigger ใหม่ เพราะไม่ควรเก็บ password เป็นข้อความธรรมดา (plain text)

Byphunsanit

CRUD ใน laravel 5

บอกเลย CRUD (Create, read, update and delete generator) ใน laravel 5 หายากมากจน GII ของ YII ดูเทพขึ้นมาทันที ตัวที่พอจะเทียบได้ก็มี Laravel Generator

ติดตั้งโดย

  1. เพิ่ม package InfyOmLabs/laravel-generator ในไฟล์ composer.json
      "require": {
      ...
      "infyomlabs/adminlte-templates": "dev-master",
      "infyomlabs/generator-builder": "dev-master",
      "infyomlabs/laravel-generator": "dev-master",
      "infyomlabs/swagger-generator": "dev-master",
      "jlapp/swaggervel": "dev-master"
      "laravelcollective/html": "5.2.*"
      ...
      },
      
  2. run command
    composer update
  3. เปิดไฟล์ \config\app.php เพิ่ม
      ...
      'providers' => [
      ...
      \InfyOm\Generator\InfyOmGeneratorServiceProvider::class,
      \InfyOm\GeneratorBuilder\GeneratorBuilderServiceProvider::class,
      Collective\Html\HtmlServiceProvider::class,
      Laracasts\Flash\FlashServiceProvider::class,
      Prettus\Repository\Providers\RepositoryServiceProvider::class,
      ...
      ],
    
      'aliases' => [
    
      'Flash'     => Laracasts\Flash\Flash::class,
      'Form'      => Collective\Html\FormFacade::class,
      'Html'      => Collective\Html\HtmlFacade::class,
    
      ],
  4. Publish ไฟล์ไปโฟลเดอร์ public โดย run command
    php artisan vendor:publish
    php artisan infyom:publish
  5. Run routes Publish Command
    php artisan infyom.publish:generator-builder

    จะเพิ่ม rute

    Route::get('generator_builder', '\InfyOm\GeneratorBuilder\Controllers\GeneratorBuilderController@builder');
    Route::get('field_template', '\InfyOm\GeneratorBuilder\Controllers\GeneratorBuilderController@fieldTemplate');
    Route::post('generator_builder/generate', '\InfyOm\GeneratorBuilder\Controllers\GeneratorBuilderController@generate');
  6. Publish view โดย run command
    php artisan infyom.publish:generator-builder --views
  7. เปิดไฟล์ \config\infyom\generator_builder.php แก้เป็น
    <?php
    
    return [
    
        'views' => [
    
            'builder' => 'infyom.generator-builder.builder',
    
            'field-template' => 'infyom.generator-builder.field-template'
        ]
    ];
  8. เปิดไฟล์ \config\infyom\laravel_generator.php แก้ ‘templates’ => ‘core-templates’, เป็น ‘templates’ => ‘adminlte-templates’,

ทดลองใช้

  1. ทดลองเรียกดู http://localhost/…/generator_builder
  2. แต่กรอกอะไรก็เจอ error Fail!result ลองจับ traffic ดูก็เห็นว่ามี error MethodNotAllowedHttpException in RouteCollection.php line 218: เปิดไฟล์ \resources\views\infyom\generator-builde\builder.blade.php บรรทัด 321 แก้ type: “POST” เป็น method: “POST”,
Byphunsanit

update ข้อมูลใน MySQL แบบหลายตาราง

กำลังทำ presashop ตัวใหม่อยู่ จำเป็นต้องใส่ต้อง copy feature จากภาษาหนึ่งไปอีกภาษาหนึ่งสำหรับ product 1,600 รายการ ถ้าเลือกใส่ใน backend ปกติถึงทำกัน 3 คนมือก็หงิกอยู่ดี (ยังไม่ต้องคิดว่าถ้าทำพลาดละ) และเพราะ prestashop เก็บข้อมูลโดยแยกแต่ละภาษา ออกไปอีกตารางจึ้งต้องเขียน sub query ตามแบบ
SQL Update column values using subquery
query ของผมเป็น

UPDATE `feature_value_lang` AS target INNER JOIN (
    SELECT `id_feature_value` ,`value`FROM `feature_value_lang`
    WHERE `id_lang` = 1
) AS source ON target.`id_feature_value` = source.`id_feature_value`
AND  target.`id_lang` = 3
SET target.`value` = source.`value`