# 4 、 PHP 連線到 MySQL
###### tags: `BE101` `PHP` `2020十月第二周` `進度筆記` `Lidemy心得`
## PHP 與 MySQL 如何跟資料庫連線
- 先在 phpMyAdmin 新增一個使用者帳號,主機名稱為 `localhost` 。
- 勾選使用者帳號的資料庫以及全域權限(全選)。

- 執行後在結構的地方選剛創立的資料庫建立資料表。
- 然後設置兩個欄位:
1. 名稱(id) 、 類型(int) 、 AUTO_INCREMENT(PRIMARY) 。
2. 名稱(username) 、 類型(varchar(64)) 、 編碼(utf8mb4_unicode_ci) 。
- 如果編碼與排序沒修正的話,則在操作的地方修正成 `utf8mb4_unicode_ci` ,無修正之後可能遇到編碼問題。
設定完成後來 conn.php 看這麼設定:
```
<?php
$server_name = 'localhost';
$username = 'user';
$password = 'secret';
$db_name = 'user';
$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"');
?>
```
- `new mysqli()`新增一個 MySQL 的 instance , 跟 database 建立連線。
- 建立連線後的東西會回傳給 `$conn` 變數。
- php 物件存取屬性的時候使用 `->` , JS 則是用 `.` 。
- `die()` 表示 `()` 內訊息輸出後,底下程式碼不會被執行,停止在這行。
- 因為資料庫連線錯誤的時候,不希望程式繼續執行。
- 進一步簡化,去除 `!empty` , 如果這個東西是有內容的話就 die 。
修正成:
```
<?php
$server_name = 'localhost';
$username = 'user';
$password = 'secret';
$db_name = 'user';
$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"');
?>
```
- 通常會把連線分開寫,可能會有一個連線的檔案只寫連線相關資訊。
- `require_once('conn.php')` 可能會類似這樣引入進來。
- 所有需要用到資料庫的 php 和改設定的時候只要改這一行就好。
- 連線資料庫 `'SET NAMES UTF8'` 設定編碼,為了使用中文不出錯。
- `'SET time_zone = "+8:00"'` 時區,確保在台灣的時區。
- 如果 PHP 放上 GitHub 時會排除 connection.php 之設定檔案,因為會暴露帳號密碼。
- 東西放到 Git 後很難被完整清掉(版本),會在 `.gitignore` 檔案加上 connection.php 檔案,避免被上傳。
- 只寫連線相關資訊,在 conn.php 檔案。
***
data.php 檔案內:
```
<?php
require_once('conn.php');
if (empty($_GET['name']) || empty($_GET['age'])) {
echo '資料有缺,請再次填寫<br>';
exit();
}
echo "Hello!" . $_GET['name'] . "<br>";
echo "Your age is " . $_GET['age'] . " <br> ";
print_r($_GET);
?>
```
***
# 4-1 、 PHP & MySQL : Read
###### tags: `BE101` `PHP` `2020十月第二周` `進度筆記` `Lidemy心得`
==如何在 PHP 內把 MySQL 資料撈出。==
## 讀取資料(Read)
新增下行指令在 index.php 檔案中:
```
<?php
require_once('conn.php');
$result = $conn->query("SELECT * FROM users");
if (!$result) {
die($conn->error);
}
// 如果 (!$result) 沒有值,則把 error 印出(在 connection 上),並不要往下執行。
while ($row = $result->fetch_assoc()) {
echo "id:" . $row['id'] . '<br>';
echo "username:" . $row['username'] . '<br>';
}
?>
```
- MySQL 指令 `SELECT` 可以換成 `select now()` 來選擇現在時間;這函式會是一個物件,包含 `current_field` 和 `num_rows` 等。
- 如果第一步下的 `querry()` 的值是錯誤的(並不是函式),則 `print_r($result)` 是空值(看有沒有拿到結果);第二步可以用 `print_r($conn->error)` 顯示碰到的錯誤。
- 第三部`($row = $result->fetch_assoc()` 用經過 querry 的相對應結果 `($result)` 來執行 `fetch_assoc()` 函式,然後拿到 `$row` 執行,表示根據 SELECT 的東西(Key)給你一個相對應的值(value),是陣列;如果碰到不確的東西就先印出來。
- `$row` 會是一個 array ; `echo 'now:' . $row['now()'];` 會是從資料庫拿出來的時間(很怪)。
- `"select now() as n;"` 建立別名後,如果要從資料庫拿東西可以用 `$row['n'];` 。
假設資料庫新增了三筆資料分別是: aaa 、 bbb 、 ccc 而如果
```
$row = $result->fetch_assoc();
print_r($row);
$row = $result->fetch_assoc();
print_r($row);
$row = $result->fetch_assoc();
print_r($row);
$row = $result->fetch_assoc();
print_r($row);
```
跑到第四次則會出現錯誤,因為第四個 `$row` 會是空值,無法再拿取。
- 拿取多筆資料可以用 while 迴圈。
- `while ($row = $result->fetch_assoc())` 會像是這樣執行:
```
先做 $row = $result->fetch_assoc();
再做 while($row) {} 並判斷 $row 是不是空值;
如果 $row 是空值則會停住;
```
***
# 4-2 、 PHP & MySQL : Create
###### tags: `BE101` `PHP` `2020十月第三周` `進度筆記` `Lidemy心得`
使用本機的位置 `http://localhost/xampp/User/index.php` 。
## 新增資料(Create)
新增下行指令在 add.php 檔案中:
```
<?php
require_once('conn.php');
if (empty($_POST['username'])) {
die('請輸入 username');
}
$username = $_POST['username'];
$sql = sprintf(
"insert into users(username) values('%s')",
$username
);
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
header("Location: index.php");
?>
```
***
### 如果要插入欄位:
```
<?php
$ result = $conn->query("insert into users(username) values('apple')");
if (!$result) {
die($conn->error);
}
print_r($result);
?>
```
- 上面這樣會自動增加 apple 字串的欄位,可以達成寫入資料功能。
- `insert into users()` 因為設定AUTO INCREMENT, 所以 () 內填 username。
- `values()` () 裡面填要放入的值, 因為是字串, `values('" . . "')` 要用字串拼接。
- 如果 `query("values('apple')");` 內先用雙引號,值是字串用單引號 `('')` , 否則可能產生錯誤。
***
### 先加上 `$sql`
```
<?php
require_once('conn.php');
$username = 'apple';
$sql = "insert into users(username) values('" . $username ."')");
echo $sql;
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
print_r($result);
?>
```
- 先把 query 獨立出來,成 `$sql = "sprintf("insert into users(username) values('"$username ."')", );` 。
- 再印出 query 看是否正確。
***
## `sprintf()` 函式
```
<?php
require_once('conn.php');
$username = 'apple';
$sql = sprintf(
"insert into users(id, username) values('" . $username ."')");
echo $sql;
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
print_r($result);
?>
```
- 可以用這個函式讓字串拼接好理解。
- 第一個參數內是字串,後面加上一個動態塞進去的數字和字符串 `%d` `'%s'` 成為 `values(%d, '%s')` , 會按照順序加入值。
***
### 請求資料(動態新增 username 的表單)
最後 add.php 檔案內如下:
```
<?php
require_once('conn.php');
if (empty($_POST['username'])) {
die('請輸入 username');
}
$username = $_POST['username'];
$sql = sprintf(
"insert into users(username) values('%s')",
$username
);
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
header("Location: index.php");
?>
```
- 可以用 GET ,或是用 POST 請求資源。
- `$username = $_POST['username'];` 這行可以不加,不過推薦先寫好。
- 透過 POST 拿 username 參數,如果沒有(顯示錯誤) 。
- 有 username 的參數則繼續往下代給 `$username` 的變數。
- 並用動態代入的 `$username` 組成 SQL query 。
- sql 印出來後去執行。
- 如果有錯誤就顯示錯誤(像新增同名檔案會顯示錯誤),沒有則顯示新增成功。
- 因為要手動跳回 index.php 有點麻煩所以最後增加一個連結。
- 但比較直觀的是將連結 `<a href="index.php">return to the past</a>` 換成 header , 會自動跳轉回 index.php。
```
header("Location: index.php");
```
而新增的動態表單在 index.php 檔案中為:
```
<?php
require_once('conn.php');
$result = $conn->query("SELECT * FROM users");
// 可以加入 order by id asc 由小排到大;或是反序 desc。
if (!$result) {
die($conn->error);
}
// 如果 (!$result) 沒有值,則把 error 印出(在 connection 上),並不要往下執行。
while ($row = $result->fetch_assoc()) {
echo "id:" . $row['id'] . '<br>';
echo "username:" . $row['username'] . '<br>';
}
?>
<h2>新增 USER</h2>
<form method="POST" action="add.php">
username: <input name="username" />
age: <input name="age" />
<input type="submit" />
</form>
```
如在 index.php 頁面中新增資料成功會顯示新增 USER 以及新增成功。


### `AUTO_INCREMENT`
- 每當往上加入一個 id 時,會有個變數以之前存的最高值(id)去記。
- 因此會以最高的 id 值為主再加一,如果把連續的 id 中的值刪除。
- 會產生 1, 2, 3, .... 9 , 直接跳到 9(id) 的情況。

- 也可以手動去調整資料選項。
### dev tool
- 用瀏覽器開發人員偵錯工具下去看的時候,勾選:
- Preserver log 和 Disable cache 。
- 跳轉紀錄不會被記住。
- 會看到 Status Code 302 頁面跳轉,瀏覽器接收到 header 請求。
- 因此頁面跳轉回 index.php (代碼 200) 。
***
參考資料:
[PHP : sprintf - PHP學習誌 - Google Sites](https://sites.google.com/site/phplearnmark/php/php-zhi-ling-qing-dan/zi-chuan-han-shi/php-sprintf)
https://kknews.cc/zh-tw/code/gmjky69.html
***
# 4-3 、 PHP & MySQL : Delete
###### tags: `BE101` `PHP` `2020十月第三周` `進度筆記` `Lidemy心得`
使用本機的位置 `http://localhost/xampp/User/index.php` 。
## 刪除資料(Delete)
在 index.php 新增以下 code :
```
<?php
require_once('conn.php');
if (empty($_GET['id'])) {
die('請輸入 id');
}
$id = $_GET['id'];
$sql = sprintf(
"delete from users where id = %d",
$id
);
echo $sql . '<br>';
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
if ($conn->affected_rows >= 1) {
echo '刪除成功';
} else {
echo '查無資料';
}
// header("Location: index.php");
?>
```
### 用 GET 的方式來代
用 GET 的方式把 id 代過去 delete.php 的檔案。
在 index.php 中 新增:
```
<?php
require_once('conn.php');
$result = $conn->query("SELECT * FROM users");
// 可以加入 order by id asc 由小排到大;或是反序 desc。
if (!$result) {
die($conn->error);
}
// 如果 (!$result) 沒有值,則把 error 印出(在 connection 上),並不要往下執行。
while ($row = $result->fetch_assoc()) {
echo "id:" . $row['id'];
echo " <a href='delete.php?id=". $row['id'] ."
'>刪除</a>";
echo '<br>';
echo "username:" . $row['username'] . '<br>';
}
?>
<h2>新增 USER</h2>
<form method="POST" action="add.php">
username: <input name="username" />
age: <input name="age" />
<input type="submit" />
</form>
```
- 完成後點 id 後面的刪除會跳到相對應的 delete.php 檔案中的 id 位置。
- `echo "<a href='delete.php?id=x'>刪除</a>"` , `id` 這邊可以用 `sprintf()` 來代 id , 或是用字串的形式 `id=" . $row['id'] . "`。
- 如果刪除了不存在的 id 卻 query 執行成功了,而沒有出現錯誤,表示 query 沒有影響到任何東西。
- 一般刪除不太會用 GET 來執行。
- 用 POST 來執行刪除的動作的話,這 `echo " <a href='delete.php?id=". $row['id'] ."'>刪除</a>";` 會改成一個表單的形式。
並新增 delete.php 檔案(用 add.php 檔案修改):
```
<?php
require_once('conn.php');
if (empty($_GET['id'])) {
die('請輸入 id');
}
$id = $_GET['id'];
$sql = sprintf(
"delete from users where id = %d",
$id
// $id 可以置換成 $_GET['id']
);
echo $sql;
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
// 沒有成功的話就印出錯誤。
header("Location: index.php");
?>
```
- 可以在 PHP 抓到被 query 影響的有幾列 `echo $conn->affected_rows;` 。
- 可以根據最後影響的列數來判斷有無刪除成功:
```
if ($conn->affected_rows >=1) {
echo '刪除成功';
} else {
echo '查無資料';
}
```
***
# 4-4 、 PHP & MySQL : Update
###### tags: `BE101` `PHP` `2020十月第三周` `進度筆記` `Lidemy心得`
使用本機的位置 `http://localhost/xampp/User/index.php` 。
## 編輯資料(Update?)
在 index.php 的檔案新增:
```
<?php
require_once('conn.php');
if (empty($_POST['id']) || empty($_POST['username'])) {
die('請輸入 id 與 username');
}
$id = $_POST['id'];
$username = $_POST['username'];
$sql = sprintf(
"update users set username='%s' where id=%d",
$username,
$id
);
echo $sql . '<br>';
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
header("Location: index.php");
?>
<h2>新增 USER</h2>
<form method="POST" action="add.php">
username: <input name="username" />
<input type="submit" />
</form>
<h2>編輯 USER</h2>
<form method="POST" action="update.php">
id: <input name="id" />
username: <input name="username" />
<input type="submit" />
</form>
```
並新增一個 update.php 檔案:
```
<?php
require_once('conn.php');
if (empty($_POST['id'] || empty($_POST['username'])) {
die('請輸入 id 與 username');
}
$id = $_POST['id'];
$username = $_POST['username'];
$sql = sprintf(
"update users set username='%s' where id=%d",
%username,
$id
// 先找到 id=%d 的欄位,再設定 username 。
// 如果沒加 where 會編輯所有欄位。
);
echo $sql;
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
// 沒有成功的話就印出錯誤。
/*
if ($conn->affected_rows >=1) {
echo '編輯成功';
} else {
echo '查無資料';
}
*/
header("Location: index.php");
?>
```
- CRUD 後端基本功能。
***
## 學習心得:
- 基礎的 SQL 指令下法。
- PHP 執行原理。
- PHP 跟 MySQL 透過表單溝通。
- CRUD 。
- 留言版的專案(完整概念)。
- 會員註冊、登入、開 API 、 資安。
- 連線錯誤的時候不能下 Querry String (程式碼的位置)。
***
## PHP 與 MySQL 指令快速查表
```
<?php
// 連線資料庫
$server_name = 'localhost';
$username = 'huli';
$password = 'huli';
$db_name = 'huli';
$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"');
// 新增資料
$username = $_POST['username'];
$sql = sprintf(
"insert into users(username) values('%s')",
$username
);
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
// 讀取資料
$result = $conn->query("SELECT * FROM users ORDER BY id ASC;");
if (!$result) {
die($conn->error);
}
while ($row = $result->fetch_assoc()) {
echo "id:" . $row['id'];
}
// 修改資料
$id = $_POST['id'];
$username = $_POST['username'];
$sql = sprintf(
"update users set username='%s' where id=%d",
$username,
$id
);
echo $sql . '<br>';
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
// 刪除資料
$id = $_GET['id'];
$sql = sprintf(
"delete from users where id = %d",
$id
);
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
if ($conn->affected_rows >= 1) {
echo '刪除成功';
} else {
echo '查無資料';
}
?>
```
***