(舊資料,參考) 將資料從前端寫入後端資料庫 – 加上PHP與MySQL
我們終於來到最後階段,要將前端變動的資料送至後端進行處理。這個月曆App程式裏,有二個資料:
- 主題色彩名稱
- 記事資料
截至目前為止
- 變化顏色之後,並沒有將最新的色彩名稱永久的記下來,只要我們重新整理網頁,就會回到最初的色彩-藍色(blue)。
- 重新整理頁面之後,記事資料全部消失不見。
HTTP讓二個網頁間傳遞資料有二種方法:GET和POST,GET就像下面的URL,可以看到資料是一組”key=value”方式送到另一個網頁,若是有二組資料,資料間則是用”&”進行串接。 http://www.example.com/action.php?name=john&age=24
要從前端送或收資料,我們必須透過XMLHttpRequest 這個物件,此物件有一個很炫的名字叫ajax,第1個a點出這個物件最具代表性的特色:Asynchronous/非同步,非同步的意思是:前後端在交換資料時不需要彼此互相”等待”,一方需要資料時,告訴另一方,然後,轉頭做自己的事,另一方資料送到時,會被通知資料到了…。
這個月曆程式的資料更新邏輯,在個別資料(色彩與記事資料)變動時,資料由前端透過XMLHttpRequest送到後端,由後端的php程式處理(不一定要使用php,使用.Net C#也行,或其他…, 如Python)。
新增js/ajax.js,這支程式是前端與後端的資料橋樑:
function ajax(object){ var request = new XMLHttpRequest(); request.open("POST", "index.php"); request.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); request.send(objectToString(object)); } function objectToString(object){ let str = ""; Object.keys(object).forEach(function(key){ str += key; str += `=${object[key]}&`; }); return str; }
此方法由我們前端呼叫ajax(object)這個方法,我們將更新資料需求包裝在object裏,透過ajax送到後端處理。
不過,要開始進行資料寫入資料庫,我們要先準備月曆App的資料表格:notes與theme。
notes表格結構:
theme表格結構:
可以依照上面的資訊建出notes與theme這二個表格,或是透過下面的SQL字串執行,或是使用myCalendar.sql.zip 使用匯入的方式,將月曆所需使用的資料表格與初始資料建立出來。
(註:若MySQL不支援uft8mb4,請改為utf8。)
-- phpMyAdmin SQL Dump -- version 4.8.3 -- https://www.phpmyadmin.net/ -- -- 主機: localhost:3306 -- 產生時間: 2019 年 03 月 14 日 12:41 -- 伺服器版本: 5.6.43 -- PHP 版本: 7.2.7 SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; SET AUTOCOMMIT = 0; START TRANSACTION; SET time_zone = "+00:00"; /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8mb4 */; -- -- 資料庫: `a02_calendar_app_build` -- -- -------------------------------------------------------- -- -- 資料表結構 `notes` -- CREATE TABLE `notes` ( `id` int(11) NOT NULL, `note_id` varchar(114) COLLATE utf8mb4_unicode_ci NOT NULL, `note_color` int(11) NOT NULL, `note_text` text COLLATE utf8mb4_unicode_ci NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- -------------------------------------------------------- -- -- 資料表結構 `theme` -- CREATE TABLE `theme` ( `id` int(11) NOT NULL, `cur_theme` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- -- 資料表的匯出資料 `theme` -- INSERT INTO `theme` (`id`, `cur_theme`) VALUES (1, 'blue'); -- -- 已匯出資料表的索引 -- -- -- 資料表索引 `notes` -- ALTER TABLE `notes` ADD PRIMARY KEY (`id`); -- -- 資料表索引 `theme` -- ALTER TABLE `theme` ADD PRIMARY KEY (`id`); -- -- 在匯出的資料表使用 AUTO_INCREMENT -- -- -- 使用資料表 AUTO_INCREMENT `notes` -- ALTER TABLE `notes` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=116; -- -- 使用資料表 AUTO_INCREMENT `theme` -- ALTER TABLE `theme` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2; COMMIT; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
更新Theme色彩資料
要讓含有PHP碼的網頁index.html由後端PHP引擎執行,我們先要將index.html更名為index.php(Web伺服器看到附屬檔名為php、php3、php4、php5、php7會喚用php模組來解譯裏面的php碼)。
ajax方法裏 request.open(“POST”, “index.php”); 這個敘述係利用POST方法來叫用index.php,因此,在index.php裏加上取得資料的PHP程式碼,當透過ajax方法使用POST方法傳送color資料時,呼叫db_updateTheme方法更新theme表格中的color欄位 (id固定為1,因為整個月曆應用程式只有一筆色彩資料。)
此段程式流程:
- mysqli_connect方法連接資料庫,四個參數:localhost、使用者名稱(授權的資料庫使用者)、密碼、資料庫名稱
- if(!$connection),如果連結資料庫失敗(帳密錯誤、網路斷線…),使用die函式結束。
- if(isset($_POST[‘color’])),判斷是否透過http的POST方法傳進來color資料,是的話呼叫db_updateTheme($_POST[‘color’]);
- db_updateTheme函式將色彩代碼資料更新至資料表格theme中(指定id值為1的資料列),若執行sql查詢失敗的話,則使用die結束。
註:SQL在資料表格上的查詢,大概就是insert、update、delete這三種操作(插入/新增、更新、刪除資料)
<?php $connection = mysqli_connect("localhost", "user", "password", "databaseName"); if(!$connection){ die("There was an error connecting to the database."); } function db_updateTheme($newTheme){ global $connection; $query = "UPDATE theme SET cur_theme = '$newTheme' WHERE id = 1"; $result = mysqli_query($connection, $query); if(!$result){ die("Query failed: " . mysqli_error($connection)); } } if(isset($_POST['color'])){ db_updateTheme($_POST['color']); } ?>
首先我們先簡單的使用 ajax({color: ‘pink’}); 來測試從前端發動一個色彩變更處理:
<script src="js/updateData.js"></script> <script src="js/changeTheme.js"></script> <script src="js/postIts.js"></script> <script language="JavaScript"> updateData(); //更新日期相關資料,預計改個名字,預叫buildCalendar,比較貼切! ajax({color: 'pink'}); </script>
將{color: ‘pink’}這個物件經過objectToString方法轉換成字串形式後丟到後端處理。此時,請檢視資料庫裏的theme表格資料是否被變更為pink,同學可試著給不同的顏色字串,測試色彩資料是否被成地更新。objectToString方法就是將我們的資料組合以”parameter=value”方式呈現,以 ajax({color: ‘pink’});這個敘述為例,objectToString會回傳”color=pink&”,這組資料以POST方式送到了另一個網頁php碼,$_POST[‘color’]即儲存所傳進來的資料,其中鍵值為color,其值為pink。
此時請觀察資料表格theme中的資料是否成功地被變更為pink:
資料變更成功之後,我們要在適當的地方來加入 ajax({color: currentColor.name);,敘述, currentColor.name是我們寫在色彩對話方塊裏,每次點選色塊後呼叫addCheckMark函式,此函式會將選取的色彩代碼指定給currentColor.name變數。
由於先前的fillInMonth方法呼叫changeColor來處理色彩的變化,而fillInMonth在整個程式中被叫用多次,我們若在在changeColor方法裏加上 ajax({color: currentColor.name);,只要一使用fillInMonth方法,theme資料表格就會被無意義更新一次(色彩不變,只是變內容),因此,我新增一個方法reDrawColor,fillInMonth中的changeColor呼叫改為對reDrawColor的呼叫,reDrawColor裏面不對theme表格進行資料更新,程式碼如下:
function changeColor(){ ajax({color: currentColor.name}); reDrawColor(); } function reDrawColor(){ //第1步:先找出勾選色彩所設定的色碼 // console.log(currentColor.name) color_data.forEach(function(arr_data){ //陣列的走訪,每走訪一個陣列元素,以arr_data變數表示。 if(currentColor.name == arr_data.name){ //找到color_data陣列中符合的色彩, currentColor.color = arr_data.color_code; currentColor.off_color = arr_data.off_color_code; } }); var elements; //先清除掉所有的style設置(td) elements = document.getElementsByTagName("td"); for(let i=0; i < elements.length; i++) { elements[i].style = null; } //改變目前的色彩設置 elements = document.getElementsByClassName("color"); for(let i=0; i < elements.length; i++) { elements[i].style.backgroundColor = currentColor.color; } elements = document.getElementsByClassName("border-color"); for(let i=0; i < elements.length; i++) { elements[i].style.borderColor = currentColor.color; } elements = document.getElementsByClassName("off-color"); for(let i=0; i < elements.length; i++) { elements[i].style.color = currentColor.off_color; } // ajax({color: currentColor.color}); //index-9-1.php //關閉對話方塊 var modal = document.getElementById("modal"); modal.open = false; modal.classList.remove("fade-out"); //淡出效果 var template = document.getElementById("fav-color"); template.setAttribute("hidden", "hidden"); }
目前的程式:index-9-1.php
新增、更新與刪除notes表格中的記事資料
緊接者,月曆記事資料也都將用上面的方法來進行傳遞指令與值:
- $_POST[‘new_note_uid’],月曆記事新增
ajax({new_note_uid: post_it.id, new_note_color: post_it.note_num, new_note_text: post_it.note});
- $_POST[‘update_note_uid’],月曆記事更新
ajax({update_note_uid: post_its[data.post_its.current_post_it_index].id, update_note_text: post_it.note});
- $_POST[‘delete_note_uid’],月曆記事刪除
ajax({delete_note_uid: post_its[indexToDel].id});
在這個階段,我們在index.php中加入處理記事資料的PHP碼:
<?php $connection = mysqli_connect("localhost", "user name", "password", "Database name"); if(!$connection){ die("There was an error connecting to the database."); } function db_updateTheme($newTheme){ //更新色彩代碼 global $connection; $query = "UPDATE theme SET cur_theme = '$newTheme' WHERE id = 1"; $result = mysqli_query($connection, $query); if(!$result){ die("Query failed: " . mysqli_error($connection)); } } function db_insertNote($uid, $color, $text){ //新增記事資料函式 global $connection; $text = mysqli_real_escape_string($connection, $text); $query = "INSERT INTO notes(note_id, note_color, note_text) VALUES('$uid', '$color', '$text')"; $result = mysqli_query($connection, $query); if(!$result){ die("Something went wrong on line 24"); } } function db_updateNote($uid, $text){//更新記事資料函式 global $connection; $text = mysqli_real_escape_string($connection, $text); $query = "UPDATE notes SET note_text = '$text' WHERE note_id = '$uid' LIMIT 1"; $result = mysqli_query($connection, $query); if(!$result){ die("Something went wrong on line 44"); } } function db_deleteNote($uid){ //刪除記事資料函式 global $connection; $query = "DELETE FROM notes WHERE note_id = '$uid'"; $result = mysqli_query($connection, $query); if(!$result){ die("Something went wrong on line 43"); } } if(isset($_POST['color'])){ //色彩主題變更 db_updateTheme($_POST['color']); } if(isset($_POST['new_note_uid'])){ //新增記事資料 db_insertNote($_POST['new_note_uid'], $_POST['new_note_color'], $_POST['new_note_text']); } if(isset($_POST['update_note_uid'])){ //更新記事資料 db_updateNote($_POST['update_note_uid'], $_POST['update_note_text']); } if(isset($_POST['delete_note_uid'])){ //刪除記事資料 db_deleteNote($_POST['delete_note_uid']); } ?>
另外,在postIts.js中加上記事資料的新增、變更與刪除ajax呼叫:(submitPostIt與deleteNote這二個方法)
function submitPostIt(){ //按了PostIt按鍵後執行的方法 const value = document.getElementById("edit-post-it").value; document.getElementById("edit-post-it").value = ""; let num = getRandom(1, 6); //取得1~6的亂數,用來標示便利貼顏色的檔案代號 let postIt = { id: currentPostItID, note_num: num, note: value } if(newCurrentPostIt){ //如果是新記事的話 postIts.push(postIt); //將新記事postIT物件推入postIts陣列 ajax({new_note_uid: postIt.id, new_note_color: postIt.note_num, new_note_text: postIt.note}); //<<--加入新增記事資料的ajax呼叫 } else { postIts[currentPostItIndex].note = postIt.note; //更新現有記事物件的記事資料 ajax({update_note_uid: postIts[currentPostItIndex].id, update_note_text: postIt.note}); //<<--加入更新記事資料的ajax呼叫 } // console.log(postIts) fillInMonth(); //這個方法可以改成fillInCalendar比較貼切,之後,我們再來統一大改 (refactoring) closeMakeNote(); } function deleteNote(){//按了Delete按鍵後執行的方法 document.getElementById("edit-post-it").value = ""; let indexToDel; if(!newCurrentPostIt){ indexToDel =currentPostItIndex; } if(indexToDel != undefined){ ajax({delete_note_uid: postIts[indexToDel].id}); //<<--加入刪除記事資料的ajax呼叫 postIts.splice(indexToDel, 1); } fillInMonth(); //這個方法可以改成fillInCalendar比較貼切,之後,我們再來統一大改 (refactoring) closeMakeNote(); }
目前的程式:index-9-2.php
整個月曆快完了,加油…
每次載入/重新載入index.php時,我們有二個工作:
- 要將資料庫中的色彩資料取出,變更月曆顏色。
- 將資料庫中的記事資料取出,更新月曆。
這部份的資料流是由後端(PHP)到前端(JavaScript),此部份的學習我們在下一個教學來進行。