從前端傳資料給後端(GET, POST)、從 PHP 連線到 MySQL 資料庫


Posted by saffran on 2021-02-25

必看!在開始前一定要做的事:Disable cache

進入 http://localhost:8080 頁面,打開 devtool 的 Network tab,把 Disable cache 勾起來

如果之後碰到問題時,就要打開 devtool 切到 Network tab,按下重新整理,頁面的所有資源就會被重新載入(清空 cache)

注意!一定要在 devtool 打開的情況下才會清空 cache

再次複習 PHP 執行流程

先看靜態檔案的執行流程

  1. 我(client 端)發了一個 request 到 server (Apache),指定要 style.css 這支檔案
  2. server 會去找「我指定的那個檔案 style.css」
  3. 找到 style.css 檔案後,回傳 response 給 client 端(檔案內容是什麼就回傳什麼)

例如:
style.css 的內容是這樣:

body{
  background-color: orange;
}

當我用網頁去存取 style.css 時,檔案內容就會原封不動的傳回來

即使在 css 的檔案內容中有 php 的程式碼,但是對於 server 來說,「.css 結尾的」就是靜態檔案,因此還是會把檔案內容原封不動的傳回來
style.css:

body{
  background-color: <?php echo 'orange'; ?>;
}

靜態網頁、動態網頁的差別就在於:有沒有去執行 php 程式碼

再看動態檔案的執行流程

  1. 我(client 端)發一個 request 到 server (Apache),指定要 index.php 這支檔案
  2. server 會去找「我指定的那個檔案 index.php」 ----- 到目前為止,流程都跟靜態檔案一樣
  3. 因為 server 發現這是一個 php 檔案,所以 server 在回傳 response 前,會先去執行這支 php 檔案
  4. 「執行完 php 檔產生的結果」會回到 server,server 再把這個結果當作 response 傳回 client 端

會去執行 php 程式碼,是因為 server 有特別設定過

Apache server 會回傳 php 執行後的結果,是因為 Apache 這個 server 有做一些設定,它才會去執行 php 檔案

例如:
test.php 的內容是這樣:
這段 php 程式碼執行完後,會輸出的結果是「I am Mickey!」

<?php
  echo 'I am Mickey!';
?>

當我用網頁去存取 test.php 時,回傳的內容會是上面那段 php 程式碼輸出的內容「I am Mickey!」,而不會是一整段 php 的程式碼

注意!即使是在 .php 的檔案內,一定要用 <?php ?> 包住的內容才會被 php 執行,在 <?php ?> 外面的內容也是會原封不動的傳回來(以純文字的方式)

test.php:

<?php
  echo 'I am Mickey!';
?>

body{
  background-color: orange;
}

所以,就可以利用 php 的這個特性,來做出一個動態的網頁,例如:印出當下的時間
test.php:

<?php
  echo 'I am Mickey!';
?>

<h1>Now: <?php echo date('Y-m-d H:i:s'); ?></h1>

根據 server 設定的不同,「.php 結尾的」也有可能變成「靜態檔案」

例如,如果把 test.php 檔案丟到 GitHub 上面,也會看到是「原封不動的回傳檔案內容,是純文字」,而不會是 php 執行後的結果

從前端傳資料給後端:GET 與 POST

PHP 要怎麼跟網頁前端交換資料呢?

可以用 GET 或是 POST 的方式發送 request

  1. 我從瀏覽器發送一個 request,在 request 會有一個欄位是 URL(就是:我想要請求的網址是什麼),例如 http://localhost:8080/huli/be101/data.php
  2. server 看到這個 URL,就會去尋找對應的檔案(data.php)
  3. 找到 data.php 後執行 PHP
  4. 執行完後的結果會回到 server
  5. server 再把這個執行後的結果當作 response 回傳到瀏覽器

方式一:GET

在 URL(前端) 用 query string 傳參數到 PHP(後端)

「用 query string 傳參數」這種形式,所用的 method 是 GET (跟 HTTP 有關)

a=1 這個 query string 會自動存到 data.php 檔案的 $_GET 變數裡面(data.php 會自動幫我準備好 $_GET 這個特殊的變數)

然後,我就可以用 $_GET 這個變數來取得我用 query string 所傳的參數

範例如下
data.php:

<?php
  echo 'Great! <br>';
  echo 'a: ' . $_GET['a'] . '<br>';
  echo 'b: ' . $_GET['b'] . '<br>';
  print_r($_GET);
?>

output:

isset() 這個 function 來判斷是否有設置這個 index(key)

isset($_GET['a']) 判斷:如果有設置 a 這個 index 的話,才執行 echo 'a: ' . $_GET['a'] . '<br>'; 這段

<?php
  echo 'Great! <br>';
  if (isset($_GET['a'])) {
    echo 'a: ' . $_GET['a'] . '<br>';
  }
  if (isset($_GET['b'])) {
    echo 'b: ' . $_GET['b'] . '<br>';
  }

  print_r($_GET);
?>

output:

前端通常會用表單的方式來跟 PHP 交換資料

範例如下
我在 index.php 寫了一個 form
在 form 裡面有幾個參數要填:

  • method 要填的是 GET
  • action 要填的是:要把這個表單的內容傳到哪裡去

inputname 屬性」就會是「變數 $_GET 所接收到的 index 值」

index.php:

<form method="GET" action="data.php">
  username: <input name="username">
  age: <input name="age">
  <input type="submit">
</form>

填好資料,按下 submit 後,就會跳到 data.php 的頁面

data.php 的內容如下:

<?php
  echo 'Great! <br>';
  print_r($_GET);
?>

瀏覽器就會用 GET 的方式,自動把我剛剛在 form 填寫的值帶到 URL 去

用 Preserve log 看看發出的 request 長什麼樣子

  • Preserve log 的作用就是:讓瀏覽器除了記下最後頁面的 log,也會幫我記下中間頁面跳轉的 log。如果沒有勾選 Preserve log 的話,瀏覽器就只會記住「最新的頁面」的記錄而已,中間如果有頁面跳轉的話就不會被記錄到

在 index.php 打開 devtool 的 Network tab,把 Preserve log 勾起來,一樣填好表單後按下 submit,在下方就可以看到:

form 幫我做的事情就是:用 query string 的方式把我填的值帶到 URL 去,然後送出一個 GET 的 request 到這個 URL

index.php:

<form method="GET" action="data.php">
  username: <input name="username">
  age: <input name="age">
  <input type="submit">
</form>

錯誤寫法:不能用 isset() 來檢查欄位是否有填寫

data.php:

<?php
  if (!isset($_GET['username']) || !isset($_GET['age'])) {
    echo '資料有缺,請再次填寫';
  } else {
    echo 'Hello! ' . $_GET['username']. '<br>';
    echo 'Your age is ' . $_GET['age'];
  }

?>

如果用 isset() 來檢查欄位是否有填寫,會發現:即使欄位沒有填寫,按下 submit 後還是不會印出「資料有缺,請再次填寫」的訊息
原因為:
isset() 是用來檢查「此 key 是否有被設置」的,所以就算我沒有填寫該欄位(value 是空值),但是因為兩個欄位的 key 是有傳入的(有設置 usernameage),所以 isset($_GET['username'])isset($_GET['age']) 的結果都會是 true

必須要「沒有設置這個 key」,isset() 才會是 false

正確寫法:用 empty() 來檢查是否為空值

針對這種欄位的檢查,要使用 empty() 來檢查是否為空值

另一個要補充的點是,比起上面用 else,更推薦使用 exit() 來達到這樣的效果:出現「資料有缺,請再次填寫」後就跳開,讓程式碼不要繼續執行下去印出下面的 Hello! 這些字
data.php:

<?php
  if (empty($_GET['username']) || empty($_GET['age'])) {
    echo '資料有缺,請再次填寫';
    exit();
  }
  echo 'Hello! ' . $_GET['username']. '<br>';
  echo 'Your age is ' . $_GET['age'];

?>

方式二:POST

如果把 method 改成 POST

index.php:

<form method="POST" action="data.php">
  username: <input name="username">
  age: <input name="age">
  <input type="submit">
</form>

按下 submit 後,會發現 URL 不會帶入我填寫的值,原因為:
我沒有用 GET 的方式帶入 query string

打開 devtool 可以看到:
最底下有一個 Form Data 寫著我剛剛填寫的資料,代表:我用 POST 的方式,把表單的資料送到 data.php 了,只是因為 data.php 目前還沒去處理這些資料

$_POST 去存取資料

已經用 POST 的方式,把資料送到 data.php 去了
URL: http://localhost:8080/saffran/data.php
method: POST

PHP 會幫我準備好一個特別的變數 $_POST,去自動存取 Form Data 的資料
因此,我就可以用 $_POST 去存取到我用 POST 送到 data.php 的資料了

index.php:

<form method="POST" action="data.php">
  username: <input name="username">
  age: <input name="age">
  <input type="submit">
</form>

data.php:

<?php
  if (empty($_POST['username']) || empty($_POST['age'])) {
    echo '資料有缺,請再次填寫';
    exit();
  }
  echo 'Hello! ' . $_POST['username']. '<br>';
  echo 'Your age is ' . $_POST['age']. '<br>';

  print_r($_POST);
?>

從 PHP 連線到 MySQL 資料庫

在新增使用者帳號時

  • Host name 代表:可以從哪裡連到資料庫,要從本機端連的話,就填寫 localhost 即可
  • 記得要把 Global privileges 勾選 Check all

在 Operations 底下可以調整資料庫的編碼,選擇 utf8mb4_general_ci 或是 utf8mb4_unicode_ci

在 PHP 要連到 MySQL 資料庫的程式碼如下

data.php:

<?php
  $server_name = 'localhost';
  $username = 'saffran';
  $password = 'rox';
  $db_name = 'saffran_db';

  $conn = new mysqli($server_name, $username, $password, $db_name);
?>

$conn = new mysqli($server_name, $username, $password, $db_name); 意思就是:我要 new 一個 mysqli 的 instance 出來(用來跟資料庫建立連線),跟資料庫建立連線之後,會回傳一些資訊到 $conn 變數

conn 是 connection 的簡寫

  • 第一個參數是「server 名稱」,這裡因為我是連到本機端的資料庫,所以 server 名稱是 localhost
  • 第二個參數是「帳號」
  • 第三個參數是「密碼」
  • 第四個參數是「資料庫名稱」

當資料庫連線錯誤時

在 PHP,要存取物件屬性時,要使用 -> 這個符號

$conn 變數會是一個物件,此物件裡面有一個屬性叫做 connect_error

<?php
  $server_name = 'localhost';
  $username = 'saffran';
  $password = 'rox';
  $db_name = 'saffran_db33';

  $conn = new mysqli($server_name, $username, $password, $db_name);
  echo $conn->connect_error;
?>

我故意輸入一個錯誤的 db 名稱,然後用 echo $conn->connect_error; 印出來看看

echo $conn->connect_error; 印出的是下方紅色箭頭那句

之所以上面還會出現那段 Warning,是因為我的 PHP 有打開這個「顯示 Warning」的設定,這樣比較方便 debug

因此,可以這樣寫:
如果 $conn->connect_error 不是空值,那就顯示「資料庫連線錯誤:...」的提示訊息

<?php
  $server_name = 'localhost';
  $username = 'saffran';
  $password = 'rox';
  $db_name = 'saffran_db33';

  $conn = new mysqli($server_name, $username, $password, $db_name);

  if (!empty($conn->connect_error)) {
    echo '資料庫連線錯誤:' . $conn->connect_error . '<br>';
  }
?>

或是也可以這樣寫:

die() 當資料庫連線錯誤時,就沒必要繼續往下執行程式碼了

<?php
  $server_name = 'localhost';
  $username = 'saffran';
  $password = 'rox';
  $db_name = 'saffran_db33';

  $conn = new mysqli($server_name, $username, $password, $db_name);

  if (!empty($conn->connect_error)) {
    die('資料庫連線錯誤:' . $conn->connect_error);
  }
  echo 'great!';
?>

die() 的作用是:會把 die() 小括號裡面的內容輸出之後,就讓整個 PHP 死掉,底下的程式碼都不會繼續執行
因此,echo 'great!'; 不會被執行到

設定編碼、時區

query() 這個 function 來改變資料庫的一些設定:編碼與時區

  • $conn->query('SET NAMES UTF8'); 是「設定編碼」,如果沒有設定編碼的話,使用中文會出現亂碼
  • $conn->query('SET time_zone = "+8:00"'); 是「把資料庫的時區設為台灣的時區」

注意!上面這兩行 query() 一定要放在 if (!empty($conn->connect_error)... 的後面,原因為:
連線完之後,要先檢查連線是否有錯誤。如果連線沒有錯誤,才繼續往下執行,這時候下 query() 去設定編碼與時區才不會出錯(如果連線錯誤,下 query() 就會出錯)

data.php:

<?php
  $server_name = 'localhost';
  $username = 'saffran';
  $password = 'rox';
  $db_name = 'saffran_db';

  $conn = new mysqli($server_name, $username, $password, $db_name);

  if (!empty($conn->connect_error) {
    die('資料庫連線錯誤:' . $conn->connect_error);
  }

  $conn->query('SET NAMES UTF8');
  $conn->query('SET time_zone = "+8:00"');
?>

連線資料庫的程式碼,完整版如下:

也可以進一步簡化,把 empty() 拿掉:

<?php
  $server_name = 'localhost';
  $username = 'saffran';
  $password = 'rox';
  $db_name = 'saffran_db';

  $conn = new mysqli($server_name, $username, $password, $db_name);

  if ($conn->connect_error) {
    die('資料庫連線錯誤:' . $conn->connect_error);
  }

  $conn->query('SET NAMES UTF8');
  $conn->query('SET time_zone = "+8:00"');
?>

把連線資料庫的程式碼獨立成一個檔案

通常在實務上會這樣做:
建立一個檔案叫做 conn.php,裡面只放「連線資料庫的程式碼」

當我在 data.php 裡面要連線到資料庫時,我只需要用 require_once()引入 conn.php 即可:

<?php
  require_once('conn.php');
  echo 'great!';
?>

注意!conn.php 不會放到 Git 上面做版本控制

當我要把我的程式碼放到 Git 上面時,會把 conn.php 排除掉(加在 .gitignore 裡面),因為 conn.php 裡面有我資料庫的位置、帳號密碼,不可以暴露在網路上


#PHP







Related Posts

[FE102] Part 2

[FE102] Part 2

1338. Reduce Array Size to The Half

1338. Reduce Array Size to The Half

 JavaScript 進階觀念

JavaScript 進階觀念


Comments