---
# System prepended metadata

title: 4 、 PHP 連線到 MySQL
tags: [2020十月第二周, Lidemy心得, 進度筆記, 2020十月第三周, BE101, PHP]

---

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

![](https://i.imgur.com/AwCY62U.png)  

- 執行後在結構的地方選剛創立的資料庫建立資料表。  
- 然後設置兩個欄位:  
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 以及新增成功。  

![](https://i.imgur.com/sskXs8Y.png)  
   
![](https://i.imgur.com/QFQELXl.png)   

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

- 也可以手動去調整資料選項。 

### 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 '查無資料';
  }
?>
```

***