PHP Ionic Angularjs Phonegap AJAX Javascript CSS MySQL jQuery Forum

อธิบายโค้ด เพิ่มหน้าตั้งค่าระบบใน app เปิดปิด Push Notification

02 December 2016 By


เนื้อหาต่อไปนี้เป็นแนวทางสำหรับการสร้างหน้า app เพื่อใช้ในการตั้งค่าต่างๆ ในระบบ
app ของเรา ในที่นี้จะแสดงเฉพาะตัวอย่างการตั้งค่าการรับข้อความ push เป็นเนื้อหาที่เกี่ยว
เนื่องกับตอนที่แล้ว
 
ประยุกต์ใช้งาน Push Notification plugin ใน ionic metarial 
http://www.ninenik.com/content.php?arti_id=749 via @ninenik
 
จากบทความที่ผ่านมา เราได้รู้โครงสร้างของ database บนฝั่ง server ว่ามีการเก็บฟิลด์ 
notify_status เก็บเป็นค่า 0 กับ 1 
    โดย 0 ให้หมายถึงผู้ใช้ app ไม่ต้องการรับข้อความ push และ 1 คือผู้ใช้ต้องการรับข้อความ push 
เงื่อนไขนี้ ไว้ใช้สำหรับตอนที่ทำการดึงข้อมูลผู้ใช้ที่ต้องการรับข้อความ push โดยเพิ่มเงื่อนไข 
WHERE notify_status = 1 เข้าไปในคำสั่ง SQL ตอนดึงข้อมูลฝั่ง server 
    ดังนั้นหน้าตั้งค่าการเปิดปิด push notification เราก็จะทำการส่งค่า 0 และ 1 มายัง server เพื่ออัพเดท
ฟิลด์ notify_status นอกจากนั้นเรายังจำเป็นจะต้องเก็บค่าสถานะการเปิดปิด push notification ไว้
ที่เครื่องด้วย เพื่อเป็นค่าเริ่มต้นในการแสดง และไม่ต้องเช็คไปยัง server ตอนเปิด app ขึ้นมา โดยการ
บันทึกไว้ที่เครื่องเราจะจัดเก็บใน database ผ่าน SQLite เก็บไว้ในตาราง setting
 
    เนื้อหานี้จะเป็นการอธิบายโค้ด โดยโค้ดที่ทำรองรับไว้แล้ว สามารถดาวน์โหลดไฟล์ได้ที่บทความ
 
แจกตัวอย่างโค้ด ionic material รองรับการตั้งค่า Push Notifcation 
http://www.ninenik.com/content.php?arti_id=750 via @ninenik
 

1. สร้าง Router กำหนด state ให้กับหน้า setting 

    เราได้รู้มาแล้วจากบทความก่อนๆ ว่าเมื่อมีการสร้างหน้า app ขึ้นมาใหม่จะมีการกำหนด state
ของหน้านั้นๆ ศึกษาเพิ่มเติมได้ที่ 
 
แนวทางการเพิ่มหน้า app และการจัด layout ใน ionic material 
http://www.ninenik.com/content.php?arti_id=733 via @ninenik
 
    ในส่วนนี้ให้เราเปิดไฟล์ app.js ในโฟลเดอร์ js ดูในส่วนของ method config()
เราได้มีการเพิ่ม state ของหน้า setting ดังนี้
 
.state('app.settings', {// กำหนด state
	url: '/settings',  // กำหนดตำแหน่งหน้า app คล้ายๆ url ในเว็บ
	views: {
		'menuContent': { // ส่วนของการแสดงใน view ชื่อ menuContent
			templateUrl: 'templates/settings.html', // ใช้ template ชื่อ settings.html
			controller: 'SettingCtrl'// กำหนดชื่อ controller ที่จะใช้งาน
		},
		'fabContent': { // ส่วนของการแสดงใน view ชื่อ fabContent ไม่ได้กำหนดอะไร
			template: ''
		}
	}
})
 
จากรูปแบบการกำหนด state ให้กับหน้า setting สิ่งที่เราจะมีเพิ่มเข้ามาคือไฟล์ settings.html
และส่วนของการควบคุมหน้า setting ด้วยการกำหนด controller ที่ชื่อ SettingCtrl
โดยจะอธิบายสองส่วนนี้ใน ข้อที่  3  และ 4 ตามลำดับ

2. เพิ่มฟิลด์ notify_status ใน database ของ SQLite 

    เนื่องจากเราจำเป็นต้องทำการเก็บค่า สถานะการรับข้อความ push ไว้ที่เครื่อง เราจะทำการ
เพิ่มฟิลด์ notify_status เข้าไปในตาราง (ปล. กรณีแก้ไขตาราง เวลาทดสอบ app ให้ทำการลบ
การติดตั้ง app เดิมก่อนเพื่อให้ระบบ reset โครงสร้างตารางใหม่ตอนติดตั้ง) 
ให้ดูในส่วนของ AppCtrl ส่วนของ controller หลัก
โดยเปิดไฟล์ controllers.js ในโฟลเดอร์ js
 
.controller('AppCtrl', function($scope, $ionicModal, $ionicPopover, $timeout,
$ionicPopup,$ionicPlatform, $cordovaDialogs, $cordovaToast, $state ) {

    $scope.loginData = {};
    $scope.isExpanded = false;
    $scope.hasHeaderFabLeft = false;
    $scope.hasHeaderFabRight = false;

    var navIcons = document.getElementsByClassName('ion-navicon');
    for (var i = 0; i < navIcons.length; i++) {
        navIcons.addEventListener('click', function() {
            this.classList.toggle('active');
        });
    }

    ////////////////////////////////////////
    // Layout Methods
    ////////////////////////////////////////

    $scope.hideNavBar = function() {
        document.getElementsByTagName('ion-nav-bar')[0].style.display = 'none';
    };

    $scope.showNavBar = function() {
        document.getElementsByTagName('ion-nav-bar')[0].style.display = 'block';
    };

    $scope.noHeader = function() {
        var content = document.getElementsByTagName('ion-content');
        for (var i = 0; i < content.length; i++) {
            if (content[i].classList.contains('has-header')) {
                content[i].classList.toggle('has-header');
            }
        }
    };

    $scope.setExpanded = function(bool) {
        $scope.isExpanded = bool;
    };

    $scope.setHeaderFab = function(location) {
        var hasHeaderFabLeft = false;
        var hasHeaderFabRight = false;

        switch (location) {
            case 'left':
                hasHeaderFabLeft = true;
                break;
            case 'right':
                hasHeaderFabRight = true;
                break;
        }

        $scope.hasHeaderFabLeft = hasHeaderFabLeft;
        $scope.hasHeaderFabRight = hasHeaderFabRight;
    };

    $scope.hasHeader = function() {
        var content = document.getElementsByTagName('ion-content');
        for (var i = 0; i < content.length; i++) {
            if (!content[i].classList.contains('has-header')) {
                content[i].classList.toggle('has-header');
            }
        }

    };

    $scope.hideHeader = function() {
        $scope.hideNavBar();
        $scope.noHeader();
    };

    $scope.showHeader = function() {
        $scope.showNavBar();
        $scope.hasHeader();
    };

    $scope.clearFabs = function() {
        var fabs = document.getElementsByClassName('button-fab');
        if (fabs.length && fabs.length > 1) {
            fabs[0].remove();
        }
    };

    $scope.noShadow = function() {
        var headerBar = document.getElementsByTagName('ion-header-bar');
        for (var i = 0; i < headerBar.length; i++) {
            if (!headerBar[i].classList.contains('no-shadow')) {
                headerBar[i].classList.add('no-shadow');
            }
        }
    };

    $scope.hasShadow = function() {
        var headerBar = document.getElementsByTagName('ion-header-bar');
        for (var i = 0; i < headerBar.length; i++) {
            if (headerBar[i].classList.contains('no-shadow')) {
                headerBar[i].classList.remove('no-shadow');
            }
        }

    };

    // ตัวแปรสำหรับกำหนด การซ่อนหรือแสดงเมนูสมาชิก false คือซ่อนเมนูสมาชิก
    $scope.showMemberMenu = false;
    // สร้างฟังก์ชั่น สำหรับเรียกใช้ เพื่อกำหนดการ กำหนดค่าตัวแปรเพื่อซ่อนหรือแสดงเมนู
    $scope.setMemberMenu = function(status){
        $scope.showMemberMenu = status;
    };

    // กำหนดตัวแปรไว้สำหรับเก็บ id ของสมาชิกที่สมัครในเครื่องนั้นๆ
    $scope.sesMemberID = null;
    // สร้างฟังก์ชั่นสำหรับกำหนดค่า id ให้สามารถเรียกใช้งานจาก $scope หลักได้
    $scope.setMemberID = function(memID){
        $scope.sesMemberID = memID;
    };
    // สร้างฟังก์ชั่นสำหรับเรียกดูค่า id ของสมาชิกจาก $scope หลักได้
    $scope.getMemberID = function(){
        return $scope.sesMemberID;
    };



    // สร้างฟังก์ชั่นสำหรับเรียกใช้ Toast plugin
    $scope.showToast = function(str, duration, position){
        $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin
            return $cordovaToast
                .show(str, duration, position)
                .then(function(success) {
                    // success
                }, function (error) {
                    // error
                });
        });
    };

    var db = null;
    $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin
        // ตรวจสอบและทำการเชื่อมต่อกับ db
        db = window.sqlitePlugin.openDatabase({
            name: 'my.db', location: 'default'
        }, function (db) {
            // เชื่อมต่อ db สำเร็จ
            $scope.showToast('Open DB Success','long','bottom');

            // เริ่มทำงานของคำสั่ง db
            db.transaction(function (tx) {
                // เวลามีการทดสอบและเพิ่มฟิลด์หรือแก้ไขตารางควรเปิดคอมเม้นการลบตารางก่อน
                // นั้นหมายถึงตารางจะถูกสร้างและจัดรูปแบบใหม่ทุกครั้ง ข้อมูลตารางจะรีเซ็ต ล้างค่า
                // พอว่างโครงสร้างตารางเรียบร้อยแล้ว ให้ปิดคอมเม้นไว้เหมือนเดิม เพื่อให้ข้อมูลยังคงอยู่
               // tx.executeSql('DROP TABLE IF EXISTS setting');
                // ทำคำสั่งสร้างตาราง setting ถ้ายังไม่มี ในที่นี้เราเก็บแค่ setting_id และ member_id
                tx.executeSql('' +
                    'CREATE TABLE IF NOT EXISTS setting ' +
                    '(setting_id integer primary key,' +
                    'member_id integer,notify_status interger)' +
                    '');
                // เช็คตาราง setting ว่ามีข้อมูลไหม
                tx.executeSql(''+'SELECT setting_id,member_id,' +
                    'notify_status FROM setting', [], function(tx, res) {
                    if(res.rows.length<=0){ // ถ้าไม่มีข้อมูล ให้เพิ่มข้อมูลไป
                        var query = "INSERT INTO setting" +
                            " (member_id,notify_status) " +
                            " VALUES (?,1)";

                        var member_id = 0;
                        // ทำงานคำสั่ง sql
                        tx.executeSql(query, [member_id], function(tx, res) {
                                // เมื่อทำการบันทึกข้อมูลสำเร็จ
                                // ค่าเริ่มต้น เราจะให้ member_id เป็น 0 ไว้ก่อน แล้วค่อยมาอัพเเดทที่หลัง
                                $scope.showToast('insertId: ' + res.insertId + ' -- probably 1','long','bottom');
                                $scope.showToast('rowsAffected: ' + res.rowsAffected + ' -- should be 1','long','bottom');
                            },
                            function(tx, error) {
                                $scope.showToast('INSERT error: ' + error.message,'long','bottom');
                            });
                    }else{ // ถ้ามีข้อมูลอยู่แล้ว และ member_id ที่เก็บไว้ไม่เท่ากับ 0
                        $scope.setNotify(res.rows.item(0).notify_status);
                        if(res.rows.item(0).member_id!=0){ // เช็ค member_id ที่เก็ยไว้ไม่เท่ากับ 0
                            $scope.setMemberID(res.rows.item(0).member_id); // กำหนดค่าตัวแปร
                            $state.go('app.profile'); // ไปที่หน้าโพรไฟล์
                        }
                    }
                });
            }, function (error) {
                $scope.showToast('transaction error: ' + error.message,'long','bottom');
            }, function () {
                $scope.showToast('transaction ok','long','bottom');
            });
        }, function (error) {
            $scope.showToast('Open database ERROR: ' + JSON.stringify(error),'long','bottom');
        });
    });

    // สร้างฟังก์ชั่นสำหรับ logout
    $scope.logoutMember = function(){
        $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin

            // ขึ้นแจ้งเตือนว่าต้องการจะออกจากระบบหรือไม่
            $cordovaDialogs.confirm('ยืนยันออกจากระบบ?', 'ออกจากระบบ?', ['ตกลง','ยกเลิก'])
                .then(function(buttonIndex) {
                    // no button = 0, 'OK' = 1, 'Cancel' = 2
                    var btnIndex = buttonIndex;
                    if(btnIndex==2){ // ถ้าตอบ ยกเลิก
                        return false; // หยุดการทำงาน ไม่ต้องออกจากระบบ
                    }
                    if(btnIndex==1){ // ถ้าตอบ ตกลง ยืนบันออกจากรับบ
                        // เริ่มทำงานของคำสั่ง db
                        db.transaction(function (tx) {
                            // ล้างค่า member_id ที่บันทึกไว้ในตาราง setting ให้เป็น 0 คือไม่จำ member_id
                            var query = "UPDATE setting SET member_id = 0 WHERE setting_id = 1";
                            tx.executeSql(query);
                            $scope.sesMemberID = null;
                            $state.go('app.login'); // ไปที่หน้าล็อกอิน
                        }, function(error) {
                            $scope.showToast('transaction error: ' + error.message,'long','bottom');
                        }, function() {
                            $scope.showToast('transaction ok','long','bottom');
                        });
                    }
                });
        });
    };


    // กำหนดตัวแปรไว้สำหรับเก็บ notify status ในเครื่องนั้นๆ
    $scope.sesNotify = null;
    // สร้างฟังก์ชั่นสำหรับกำหนดค่า notify status ให้สามารถเรียกใช้งานจาก $scope หลักได้
    $scope.setNotify = function(status){
        $scope.sesNotify = status;
    };
    // สร้างฟังก์ชั่นสำหรับเรียกดูค่า notify status ของสมาชิกจาก $scope หลักได้
    $scope.getNotify = function(){
        return $scope.sesNotify;
    };

})
 
สังเกตให้ส่วนของคำสั่งสร้างตาราง
 
tx.executeSql('' +
	'CREATE TABLE IF NOT EXISTS setting ' +
	'(setting_id integer primary key,' +
	'member_id integer,notify_status interger)' +
	'');
 
เราได้เพิ่มฟิลด์ notify_status เก็บเป็น interger ไว้เก็บค่า 0 กับ 1
 
เมื่อสร้างตารางแล้ว เราจะให้ทำคำสั่ง ดึงข้อมูลในตาราง ในขั้นตอนนี้เราก็ต้อง
ดึงฟิลด์ notify_status ด้วย ตามคำสั่ง 
 
tx.executeSql(''+'SELECT setting_id,member_id,' +
	'notify_status FROM setting', [], function(tx, res) {
 
ปล. สังเกตคำสั่ง sql จะมีเป็นลักษณะการใช้การ + string  ที่ขึ้นบรรทัดใหม่ ถ้าเขียนติดกันยาวๆ 
จะได้เป็น 
 
tx.executeSql('SELECT setting_id,member_id,notify_status FROM setting', [], function(tx, res) {
 
คำสั่งข้างต้น จะดึงค่ากรณีมีข้อมูล ไปใช้ในการแสดงหน้า setting ซึ่งถ้าเป็นการติดตั้ง app ครั้งแรก
ก็จะไมีมีข้อมูล ก็จะไปทำงานคำสั่งเพิ่มข้อมูล 
 
var query = "INSERT INTO setting" +
	" (member_id,notify_status) " +
	" VALUES (?,1)";

var member_id = 0;
// ทำงานคำสั่ง sql
tx.executeSql(query, [member_id], function(tx, res) {
 
จะเห็นว่าในส่วนของการบันทึกข้อมูล เราจะส่งค่าแค่ค่าเดียวตัวตัวกำหนด ( ?
 
tx.executeSql(query, [member_id], function(tx, res) {
 
คำสังนี้เราส่งแค่ member_id ส่วนค่า notify_status เรากำหนดค่าตายตัว ไปในคำสั่งแล้วเป็น 1
 
ข้างต้นเป็นกรณีไม่มีข้อมูล ส่วนกรณีมีข้อมูลในตารางแล้ว เราก็จะเข้าเงื่อนไข นำข้อมูล
ไปกำหนดค่า ผ่านฟังก์ชั่น 
 
}else{ // ถ้ามีข้อมูลอยู่แล้ว และ member_id ที่เก็บไว้ไม่เท่ากับ 0
	$scope.setNotify(res.rows.item(0).notify_status);
	if(res.rows.item(0).member_id!=0){ // เช็ค member_id ที่เก็ยไว้ไม่เท่ากับ 0
		$scope.setMemberID(res.rows.item(0).member_id); // กำหนดค่าตัวแปร
		$state.go('app.profile'); // ไปที่หน้าโพรไฟล์
	}
}
 
เงื่อนไขคือมีข้อมูลการตั้งค่า คำอธิบายด้านบนอธิบายผิด member_id เป็นค่าว่างก็ได้
บรรทัดต่อมาเราจะเห็นการใช้ฟังก์ชั่น เพื่อส่งค่าสถานะ notify_status ไปกำหนดในตัวแปร
 
// นำค่าจาก database ส่งค่าไปในฟังก์ชั่น กำหนดค่า sesNotify
$scope.setNotify(res.rows.item(0).notify_status);
 
ค่าตัวแปร ฟังก์ชั่นกำหนดค่า และฟังก์ชั่นเรียกใช้ค่า เรากำหนดในส่วนของ AppCtrl ตามนี้
 
// กำหนดตัวแปรไว้สำหรับเก็บ notify status ในเครื่องนั้นๆ
$scope.sesNotify = null;
// สร้างฟังก์ชั่นสำหรับกำหนดค่า notify status ให้สามารถเรียกใช้งานจาก $scope หลักได้
$scope.setNotify = function(status){
	$scope.sesNotify = status;
};
// สร้างฟังก์ชั่นสำหรับเรียกดูค่า notify status ของสมาชิกจาก $scope หลักได้
$scope.getNotify = function(){
	return $scope.sesNotify;
};
 
 

3. สร้างหน้า setting สำหรับให้ผู้ใช้เปลี่ยนค่าสะานะการรับข้อความ push

    ดูไฟล์ settings.html ในโฟลเดอร์ templates 
 
<ion-view view-title="Settings">
    <ion-nav-buttons side="right">
        <div class="buttons buttons-right header-item">
    <span class="right-buttons">
    <button class="button button-icon button-clear ion-android-more-vertical"> </button>
    </span>
        </div>
    </ion-nav-buttons>
    <ion-content ng-class="{expanded:isExpanded}">


        <div class="list">

            <ion-toggle class="border-item"
                        ng-model="notify_status_checked"
                        ng-checked="notify_status_checked"
                        ng-change="changeNotiy()"
                        toggle-class="toggle-calm-900">
                Push Notification
            </ion-toggle>
        </div>
        <div class="padding">
            {{notify_status_checked}}
        </div>

    </ion-content>
</ion-view>
 
สังเกตว่าเราใช้ directive ion-toggle ของ ionic (directive ก็คล้ายกับรูปแบบแท็กเฉพาะที่สร้างขึ้นมา แล้ว
จะถูกแปลงในรูปแบบแท็กพื้นฐาน html อีกที) ลักษณะ toggle ก็จะคล้าย checkbox หรือคล้ายกับ สวิชเปิดปิด

ng-model="notify_status_checked" คือ
ค่าของ toggle ให้เท่ากับตัวแปร notify_status_checked
ng-checked="notify_status_checked" คือ 
ค่าการเปิดปิด ให้เป็นไปตามค่าของตัวแปร notify_status_checked
ng-change="changeNotiy()" คือ
ถ้ามีการเปลี่ยนแปลงค่า ให้ไปทำงานฟังก์ชั่น changeNotify()
toggle-class="toggle-calm-900" คือ
ส่วนของการกำหนด css class 
 
โดยค่าตัวแปร และฟังก์ชั่น เราจะเขียนไว้ในส่วนของการจัดการหรือ controller 
จากโค้ดเรามีการแทรก คำสั่งการแสดงค่าตัวแปร เพื่อให้เห็นภาพ
 
<div class="padding">
{{notify_status_checked}}
</div>
 

4. จัดการการทำงานหน้า settng ใน SettingCtrl

    ในไฟล์ controllers.js ในโฟลเดอร์ js ให้เราดูในส่วนของ SettingCtrl
หรือส่วนควบคุมการทำงาหน้า setting 
 
.controller('SettingCtrl', function(
    $scope, $stateParams, $timeout, ionicMaterialInk, ionicMaterialMotion
    ,$ionicPlatform, $cordovaToast, $cordovaDevice, notifyService) {
    $scope.$parent.showHeader();
    $scope.$parent.clearFabs();
    $scope.$parent.setHeaderFab(false);
    $scope.$parent.hasShadow();

    $timeout(function() {
        $scope.isExpanded = false;
        $scope.$parent.setExpanded(false);
    }, 300);

    // สร้างฟังก์ชั่นสำหรับเรียกใช้ Toast plugin
    $scope.showToast = function(str, duration, position){
        $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin
            return $cordovaToast
                .show(str, duration, position)
                .then(function(success) {
                    // success
                }, function (error) {
                    // error
                });
        });
    };


    var db = null;

    $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin
        // ตรวจสอบและทำการเชื่อมต่อกับ db
        db = window.sqlitePlugin.openDatabase({
            name: 'my.db', location: 'default'
        }, function (db) {
            $scope.showToast('Open DB Success','long','bottom');
        }, function (error) {
            $scope.showToast('Open database ERROR: ' + JSON.stringify(error),'long','bottom');
        });
    });

    $scope.notify_status = $scope.$parent.getNotify();
    $scope.notify_status_checked = ($scope.notify_status==1)?true:false;
    
    $scope.changeNotiy = function(){
        $ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin
          //  alert($scope.notify_status_checked);
            var statusNotify = null;
            var statusNotify_val = 0;
            if ($scope.notify_status_checked == true) {
                $scope.notify_status_checked = false;
             //   alert(0);
                statusNotify_val = 0;
                statusNotify = 'false';

            } else {
                $scope.notify_status_checked = true;
             //   alert(1);
                statusNotify_val = 1;
                statusNotify = 'true';
            }
            // เริ่มทำงานของคำสั่ง db
            db.transaction(function (tx) {
                // คำอธิบายในโค้ดตัวอย่างในไฟล์ที่แจก บรรทัดนี้ผิด ดูคำอธิบายด้านล่าง
                var query = "UPDATE setting SET notify_status = ? WHERE setting_id = 1";
                tx.executeSql(query, [statusNotify_val], function(tx, res) {
                        // เมื่อทำการบันทึกข้อมูลสำเร็จ
                        // เตรียมข้อมูลสำหรับส่งไปบันทึกบน server
                        $scope.$parent.setNotify(statusNotify_val);
                        var dataSend = {
                            uid: $cordovaDevice.getUUID(),
                            status: statusNotify
                        };
                        // service เรียกใช้ฟังก์ชั่น notifyService ส่งค่าไปยัง server
                        notifyService.updateNotify(dataSend)
                            .then(function (response) {
                                // ทดสอบแสดงค่าว่าบันทึกสำเร็จหรือไม่
                             //   alert(JSON.stringify(response));
                            });
                    },
                    function(tx, error) {
                        $scope.showToast('Update error: ' + error.message,'long','bottom');
                    });
            }, function (error) {
                $scope.showToast('transaction error: ' + error.message, 'long', 'bottom');
            }, function () {
                $scope.showToast('transaction ok', 'long', 'bottom');
            });


        });
    };

    ionicMaterialMotion.fadeSlideInRight();

    ionicMaterialInk.displayEffect();

})
 
จะเห็นว่าเรามีการ inject หรือเรียกใช้ $cordovaDevice และ notifyService
โดย $cordovaDevice เราจะใช้ดึงค่า UUID รหัสเฉพาะของเครื่องมือถือนั้น ส่งไปเช็ค
ค่าในตาราง tbl_notify บน server (เนื้อหาอ้างอิงบทความที่ผ่านมา เนื่องจากเรามีการ
เก็บค่า UUID และ registrandid สำหรับมือถือที่รับข้อความ push แต่เริ่มต้นแล้ว)
ส่วน nofityService เราจะเพิ่มฟังก์ชั่นการอัพเดทข้อมูลเข้าไป จากเดิม มีเพียงคำสั่งเพิ่มข้อมูล
 
ทำความเข้าใจในส่วน SettingCtrl ขออธิบายเฉพาะเนื้อหาใหม่
ส่วนแรก เราจะไปดึงค่าสถานะ จาก controller หลัก ผ่านฟังก์ชัน getNotify()
มาไว้ในตัวแปร notify_status ซึ่งกำหนดในรูปแบบ $scope.notify_status
สังเกตว่าการเรียกฟังชั่นจาก controller หลัก เราจะใช้ $scope.$parent. เพื่ออ้างอิง controller หลัก
แล้วตามด้วยฟังก์ชั่น ที่ต้องการ
 
$scope.notify_status = $scope.$parent.getNotify();
$scope.notify_status_checked = ($scope.notify_status==1)?true:false;
 
ส่วนต่อมาคือตัวแปรสถานะการเปิดปิด toggle จะใช้เป็น true หรือ false
โดยถ้าค่า $scope.notify_status==1 ให้ notify_status_checked เป็น true ถ้าเป็นอื่นให้มีค่าเป็น false
นั่นคือ ถ้าใน database มีการบันทึกเป้น 1 แล้ว ตัว toggle ในหน้า setting ก็จะแสดงเป็นสะานะเปิด ตาม
ค่าของตัวแปร notify_status_checked
 
ต่อไปมาดูส่วนของฟังก์ชั่น ขออธิบายในโค้ด
 
// เมื่อมีการเปลี่ยนค่า toggle
$scope.changeNotiy = function(){
	$ionicPlatform.ready(function() { // เตรียมก่อนเรียกใช้ plugin
		// ค่าที่ส่งเข้ามาของตัวแปร notify_status_checked จะเป็นค่าของสถานะก่อนเปลี่ยนค่า
	  //  alert($scope.notify_status_checked);
		var statusNotify = null; // สร้างตัวแปรเก็บสถานะแบบข้อความ
		var statusNotify_val = 0; // สร้างตัวแปรเก็บถานะแบบตัวเลข
		if ($scope.notify_status_checked == true) { // ค่าเดิมเป็น true เมื่อเปลี่ยน
			$scope.notify_status_checked = false; // ให้กำหนดค่าหใม่เป็น false
		 //   alert(0);
			statusNotify_val = 0;
			statusNotify = 'false';

		} else { //เมื่อค่าเดิมเป็น false
			$scope.notify_status_checked = true; // กำหนดค่าใหม่เป็น true
		 //   alert(1);
			statusNotify_val = 1;
			statusNotify = 'true';
		}
		// เริ่มทำงานของคำสั่ง db
		db.transaction(function (tx) {
			// ทำคำสั่ง อัพเดทสถานะ notify_status ค่าที่อัพเดทเราจะใช้เป็นตัวแปรที่เก็บตัวเลข 0 หรือ 1
			var query = "UPDATE setting SET notify_status = ? WHERE setting_id = 1";
			tx.executeSql(query, [statusNotify_val], function(tx, res) {
					// เมื่อทำการอัพเดทสำเร็จ
					$scope.$parent.setNotify(statusNotify_val); // อัพเดทค่าในตัวแปรใน controller หลักด้วย
					// เตรียมข้อมูลสำหรับส่งไปบันทึกบน server สัเกตสถาน เราจะส่งเป็นข้อความ ไปแทนโดยใช้
					// เป็นข้อความ 'true' หรือ 'false'
					var dataSend = {
						uid: $cordovaDevice.getUUID(),
						status: statusNotify
					};
					// service เรียกใช้ฟังก์ชั่น updateNotify จาก  notifyService ส่งค่าไปยัง server
					notifyService.updateNotify(dataSend)
						.then(function (response) {
							// ทดสอบแสดงค่าว่าบันทึกสำเร็จหรือไม่
						 //   alert(JSON.stringify(response));
						});
				},
				function(tx, error) {
					$scope.showToast('Update error: ' + error.message,'long','bottom');
				});
		}, function (error) {
			$scope.showToast('transaction error: ' + error.message, 'long', 'bottom');
		}, function () {
			$scope.showToast('transaction ok', 'long', 'bottom');
		});


	});
};
 
 

5. ส่วนของ service ที่เพิ่มฟังก์ชั่น อัพเดทข้อมูล

    จากในข้อที่ 4 เรามีการใช้งาน notifyService ให้ทำงานต่อจากบันทึกข้อมูลลง database บนเครื่องแล้ว
ให้ทำงานอัพเดทสถานะบน server ด้วย ดูไฟล์ services.js ในโฟลเดอร์ js
 
.service('notifyService',function($http, $q){ // สร้าง service
    // กำหนด url ของ ไฟล์ api ของเรา
    var url = "http://192.168.8.100/app/api/notify_api.php";
    return { // ในที่นี้เราจะใช้การส่งค่าแบบ post
        setNotify: function(dataSend) {
            var deferred = $q.defer();
            $http.post(url,dataSend)
                .success(deferred.resolve)
                .error(deferred.reject);
            return deferred.promise;
        },
        updateNotify: function(dataSend) {
            var deferred = $q.defer();
            $http.post(url,dataSend)
                .success(deferred.resolve)
                .error(deferred.reject);
            return deferred.promise;
        }        
    };

})
 
มีส่วนของฟังก์ชั่น updateNotify() เพิ่มเข้ามา ส่งค่าไปทำงานที่ไฟล์ notify_api.php ที่ฝั่ง server
โดยโค้ด notify_api.php เราก็เพิ่มในส่วนของเงื่อนไขการอัพเดทเข้าไปด้วย จะได้เป็น 
 

ไฟล์ notify_api.php

 
<?php
header("Content-type:application/json; charset=UTF-8");          
header("Cache-Control: no-store, no-cache, must-revalidate");         
header("Cache-Control: post-check=0, pre-check=0", false);    
if(isset($_SERVER['HTTP_ORIGIN'])) {
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
 //       header('Access-Control-Max-Age: 86400');    // cache for 1 day
}
// Access-Control headers are received during OPTIONS requests
if($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if(isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");         
if(isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
exit(0);
}
// ส่วนการกำหนด header ให้กำหนดตามด้านบน ไม่เช่นนั้น app จะไม่สามารถส่งค่ามาได้
require_once("dbconnect.php");
$postdata = file_get_contents("php://input"); // ค่าที่ส่งมาจะถูกเก็บที่ไฟล์ 
if(isset($postdata)) { // ถ้ามีคาที่ส่งมา
$_POST = json_decode($postdata,true); // แปลงค่าที่ส่งมาเป็นตัวแปร array $_POST
}
$_error_msg = null;
$_success_msg = null;
if(
	isset($_POST['regid']) &&  $_POST['regid']!="" && 
	isset($_POST['uid']) &&  $_POST['uid']!=""
){ //  ส่วนของเงื่อนไขเพิ่มข้อมูล
	
	
      
    $_res_regID = $mysqli->real_escape_string(trim($_POST['regid']));  
    $_res_UID = $mysqli->real_escape_string(trim($_POST['uid']));  
  
    if(isset($_error_msg) && $_error_msg!=""){  
        $json_data[]=array(    
            "success" => $_success_msg,  
            "error" => $_error_msg  
        );            
    }else{  
  
        // เช็คในตารางก่อนว่า เครื่องมือถือนั้นๆ ได้เคยบันทึกข้อมูลมาก่อนแล้วหรือไม่ อิงจาก UUID  
        $sql="  
        SELECT notify_regid,notify_uid,notify_status FROM tbl_notify WHERE notify_uid = '".$_res_UID."'   
        ";    
        $result = $mysqli->query($sql);  
        if($result && $result->num_rows>0){ // ถ้าเคยบันทึกแล้ว  
            $row = $result->fetch_assoc(); // ดึงค่ามาตรวจสอบ  
            $_success_msg=1;  
            if($row['notify_regid'] != $_res_regID){ // ถ้าค่าเดิม กับค่าหใม่ไม่เหมือนกัน ให้อัพเดทค่าใหม่  
                $sql2 = "  
                UPDATE tbl_notify SET   
                notify_regid = '".$_res_regID."' ,  
                notify_status = '1' ,  
                notify_updateadd = '".date('Y-m-d H:i:s')."'          
                WHERE notify_uid = '".$_res_UID."'  
                ";  
                $result2 = $mysqli->query($sql2);  
                if($result2 && $mysqli->affected_rows>0){ // ถ้าอัพเดทสำเร็จ  
                    $_success_msg=1;  
                    $json_data[]=array(    
                        "success" => $_success_msg,  
                        "error" => $_error_msg,  
                        "regid" => $_res_regID,  
                        "uid"=> $_res_UID,  
                        "status"=> 1   
                    );                
                }else{ // ถ้าอัพเดทไม่สำเร็จ  
                    $json_data[]=array(    
                        "success" => $_success_msg,  
                        "error" => $_error_msg  
                    );                    
                }  
            }else{  // ถ้าค่าใหม่ เท่ากับค่าเดิม ใช้ค่าเดิม  
                $json_data[]=array(    
                    "success" => $_success_msg,  
                    "error" => $_error_msg,  
                    "regid" => $row['notify_regid'],  
                    "uid" => $row['notify_uid'],  
                    "status" => $row['notify_status']  
                );        
            }  
        }else{ // ถ้ายังไม่มีข้อมูล ให้บันทีกข้อมูลใหม่  
            $sql2 = "  
            INSERT INTO tbl_notify SET   
            notify_uid = '".$_res_UID."' ,  
            notify_regid = '".$_res_regID."' ,  
            notify_status = '1' ,  
            notify_dateadd = '".date('Y-m-d H:i:s')."'        
            ";  
            $result2 = $mysqli->query($sql2);  
            if($result2 && $mysqli->affected_rows>0){ // ถ้าบันทึกขอ้มูลสำเร็จ  
                $_success_msg=1;  
                $json_data[]=array(    
                    "success" => $_success_msg,  
                    "error" => $_error_msg,  
                    "regid" => $_res_regID,  
                    "uid"=> $_res_UID,  
                    "status"=> 1   
                );                
            }else{ // ถ้าบันทึกข้อมูลล้มเหลว  
                $json_data[]=array(    
                    "success" => $_success_msg,  
                    "error" => $_error_msg  
                );                    
            }  
        }  
          
    }  
    //  
}elseif(
	isset($_POST['status']) &&  $_POST['status']!="" && 
	isset($_POST['uid']) &&  $_POST['uid']!=""
){ //  ส่วนของเงื่อนไขอัพเดทข้อมูล 

	$_res_STATUS = $mysqli->real_escape_string(trim($_POST['status']));
	$_res_UID = $mysqli->real_escape_string(trim($_POST['uid']));
	
	$_res_STATUS = ($_res_STATUS=='true')?1:0;

	if(isset($_error_msg) && $_error_msg!=""){
		$json_data[]=array(  
			"success" => $_success_msg,
			"error" => $_error_msg
		);    		
	}else{

		$sql="
		SELECT notify_id,notify_regid FROM tbl_notify WHERE notify_uid = '".$_res_UID."' 
		";	
		$result = $mysqli->query($sql);
		if($result && $result->num_rows>0){
			$row = $result->fetch_assoc();
			$_success_msg=1;
			$sql2 = "
			UPDATE tbl_notify SET 
			notify_status = '".$_res_STATUS."' ,
			notify_updateadd = '".date('Y-m-d H:i:s')."' 		
			WHERE notify_uid = '".$_res_UID."'
			";
			$result2 = $mysqli->query($sql2);
			if($result2 && $mysqli->affected_rows>0){
				$_success_msg=1;
				$json_data[]=array(  
					"success" => $_success_msg,
					"error" => $_error_msg,
					"regid" => $row['notify_regid'],
					"uid"=> $_res_UID,
					"status"=> $_res_STATUS 
				);    			
			}else{
				$json_data[]=array(  
					"success" => $_success_msg,
					"error" => $_error_msg
				);    				
			}
		}else{
			$sql2 = "
			INSERT INTO tbl_notify SET 
			notify_uid = '".$_res_UID."' ,
			notify_status = '".$_res_STATUS."' ,
			notify_dateadd = '".date('Y-m-d H:i:s')."' 		
			";
			$result2 = $mysqli->query($sql2);
			if($result2 && $mysqli->affected_rows>0){
				$_success_msg=1;
				$json_data[]=array(  
					"success" => $_success_msg,
					"error" => $_error_msg,
					"regid" => '',
					"uid"=> $_res_UID,
					"status"=> $_res_STATUS 
				);    			
			}else{
				$json_data[]=array(  
					"success" => $_success_msg,
					"error" => $_error_msg
				);    				
			}
		}
		
	}
	
	
}else{ // ถ้าไม่ได้กรอกข้อมลใดๆ เขามา
	$_error_msg = "โปรดกรอกข้อมูลให้ครบถ้วน";
	$json_data[]=array(  
		"success" => $_success_msg,
		"error" => $_error_msg
	);    			
}
// แปลง array เป็นรูปแบบ json string
if(isset($json_data)){
	$json= json_encode($json_data);  
	if(isset($_GET['callback']) && $_GET['callback']!=""){  
	echo $_GET['callback']."(".$json.");";      
	}else{  
	echo $json;  
	}  
}
?>
 
โดยในไฟล์ php เมื่อมีสถานะเป็นข้อความ เป็นคำว่า 'true' ส่งค่ามาอัพเดท ก็เปลี่ยนค่าเป้น 1
ถ้าเป็นอื่นเปลี่ยนค่าเป็น 0 แล้วบันทึกสถานะการรับข้อความ push ของผู้ใช้นั้นๆ 
 
และในส่วนของการส่งข้อความ push เราก็เพิ่มเงื่อนไขตอนดึงข้อมูล ให้ดึงเฉพาะผู้ใช้ที่เปิดรับข้อความ
push ตัวอย่าง

ไฟล์ test_push.php ที่มีเงื่อนไข 

 
<?php
header("Content-type:text/html; charset=UTF-8");            
header("Cache-Control: no-store, no-cache, must-revalidate");         
header("Cache-Control: post-check=0, pre-check=0", false);      
// เชื่อมต่อกับฐานข้อมูล      
require_once("dbconnect.php");
// ส่วนตั้งค่าการใช้งาน push
require_once("pusher.php");
$fcmToken = "your_fcm_token";  // ค่าา FCM token
$pusher = new Pusher($fcmToken);


$regId=array();
$sql="
SELECT * FROM tbl_notify WHERE notify_status = 1 
";
$result = $mysqli->query($sql);
if($result){
	while($row = $result->fetch_assoc()){
		$regId[]=$row['notify_regid'];
	}
}

$msg = array
(
	'message' 	=> 'ทดสอบข้อความภาษาไทย',
	'title'		=> 'ทดสอบหัวข้อหลัก',
	'subtitle'	=> 'ทดสอบหัวข้อย่อย',
	'tickerText'	=> 'Ticker text here...Ticker text here...Ticker text here',
	'vibrate'	=> 1,
	'sound'		=> 1,
	'count'		=> 2,	
	'image' => 'http://www.ninenik.com/images/banner_ninenik.gif',
	'largeIcon'	=> 'large_icon',
	'smallIcon'	=> 'small_icon'
);
$pusher->notify($regId, $msg);
echo "<pre>";
print_r($pusher->getOutputAsArray());
echo "</pre>";
?>
 
ทั้งหมดเป็นการอธิบายรูปแบบการทำงาน การกำหนดการตั้งค่าในหน้า setting สำหรับกำหนดสถานะ
push notification ให้ศึกษาเป็นแนวทางต่อไป 

 



เนื้อหาที่เกี่ยวข้อง



Tags:: ionicframework ionicmaterial

เนื้อหาพิเศษ เฉพาะสำหรับสมาชิก

กรุณาล็อกอิน และลงชื่อติดตาม


สมัครสมาชิกได้ที่        ล็อกอินได้ที่   





URL สำหรับอ้างอิง