規劃產品路由與功能
檔案 | 功能 |
---|---|
index.php | 觀看所有留言 |
handle_add_comment.php | 處理新增留言 |
conn.php | 連接資料庫 |
handle_login.php | 處理登入邏輯 |
handle_register.php | 處理註冊邏輯 |
login.php | 登入頁面 |
logout.php | 登出 |
register.php | 註冊頁面 |
utils.php | 重複用的 function 放這 |
規劃資料結構以及建置資料庫
三個資料表 :
資料表 | 功用 | 欄位 |
---|---|---|
users | 會員資料 | 1.id 2.nickname 3.username 4.password 5.created_at |
comments | 留言資料 | 1.id 2.nickname 3.content 4.created_at |
tokens | token 對照 username | 1.id 2.token 3.username |
id 記得都要設 A_I
AUTO_INCREMENT 自動累加
如果使用 php 內建的 session 可以不用 tokens 那張資料表
切板完串接資料庫顯示留言
conn.php
$conn = new mysqli($server_name, $username, $password, $db_name);
// conn 為 connection 的簡寫,第一個參數是 server 名稱,第二個是帳號,第三個是密碼,第四個是 database
if ($conn->connect_error) { // 物件存取屬性是用 -> 來表示
die('資料庫連線錯誤:' . $conn->connect_error);
}
$conn->query('SET NAMES UTF8'); // 設定編碼
$conn->query('SET time_zone = "+8:00"'); // 設定台灣時間
index.php
顯示留言板面
$result = $conn->query("SELECT * FROM comments ORDER BY id DESC");
if (!$result) {
die('Error:' . $conn->error);
}
在 HTML 想要安插的地方放暱稱、建立時間、內容
while ($row = $result->fetch_assoc()) {
<?php echo $row['nickname']; ?>
<?php echo $row['created_at'] ?>
<?php echo $row['content'] ?>
加入新增留言功能
$_POST
取資料放入資料庫
handle_add_comment.php
require_once 'conn.php';
if (empty($_POST['content'])) {
die('資料不齊全');
}
$content = $_POST['content'];
$sql = sprintf("INSERT INTO comments(content) VALUE('%s')", $content);
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
header("Location: index.php");
實作註冊功能
在 index.php 放個連結
<a href="register.php">註冊</a>
登入、登出以此類推
register.php 頁面長得跟 index.php 差不多,所以拿 index.php 來改一改就好
要有暱稱、帳號、密碼框供使用者填入
handle_register.php : 將註冊資料寫入資料庫,長得跟 handle_add_comment.php 很像,所以複製改一改就好,username 在資料表設唯一,並且做檢查
MYSQL ERROR CODE 錯誤編號的意義
1062:欄位值重複,入庫失敗
if (!$result) {
$code = $conn->errno;
if ($code === 1062) {
header('Location: register.php?errCode=2');
}
die($conn->error);
}
handle_register.php
require_once 'conn.php';
if (empty($_POST['nickname']) || empty($_POST['username']) || empty($_POST['password'])) {
die('資料不齊全');
}
$nickname = $_POST['nickname'];
$username = $_POST['username'];
$password = $_POST['password'];
$sql = sprintf("INSERT INTO users(nickname, username, password) VALUE('%s', '%s', '%s')", $nickname, $username, $password);
$result = $conn->query($sql);
if (!$result) {
$code = $conn->errno;
if ($code === 1062) {
header('Location: register.php?errCode=2');
}
die($conn->error);
}
header("Location: index.php");
實作登入功能
login.php 和 register.php 頁面長差不多,複製拿來改一改
handle_login.php 和 handle_register.php 處理邏輯也差不多,複製拿來改一改
num_rows
代表結果有多少筆資料
如果輸入錯誤 帶參數回去 header("Location: login.php?errCode=2");
在 login.php 找個地方做判斷,並顯示帳號密碼有誤出來
<?php
if (!empty($_GET['errCode'])) {
$code = $_GET['errCode'];
$msg = 'Error';
if ($code === '2') {
$msg = '帳號密碼有誤';
}
echo '<h3 class="error">錯誤 : ' . $msg . '</h3>';
}
?>
handle_login.php
<?php
require_once 'conn.php';
if (empty($_POST['username']) || empty($_POST['password'])) {
die('資料不齊全');
}
$username = $_POST['username'];
$password = $_POST['password'];
$sql = sprintf("SELECT * FROM users WHERE username='%s' and password='%s'", $username, $password);
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
if ($result->num_rows) {
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
header("Location: index.php");
} else {
header("Location: login.php?errCode=2");
}
該怎麼記住登入狀態?Cookie 簡介與實作
HTTP 狀態健忘特性
如何看 Cookie : 打開 DevTools --> application --> 左邊版面 storage 有 cookies 可以看,其實就是 name 跟 value
瀏覽器會自動把符合條件的 Cookie 帶上來,符合條件指的是沒有過期,domain 和路徑要符合
所以在處理登入頁面的 php ,handle_login.php
$token = generateToken();
$expire = time() + 3600 * 24 * 30;
setcookie("token", $token, $expire);
header("Location: index.php");
utils.php
function generateToken()
{
$s = '';
for ($i = 1; $i <= 16; $i++) {
$s .= chr(rand(65, 90));
}
return $s;
}
在 handle_login.php 裡面 setcookie
後,在 index.php 要拿到用 $_COOKIE
$username = null;
if (!empty($_COOKIE['token'])) {
$user = getUserFromToken($_COOKIE['token']);
$username = $user['username'];
}
登出就把 cookie 清空
require_once "conn.php";
$token = $_COOKIE['token'];
$sql = sprintf("DELETE FROM tokens WHERE token = '%s'", $token);
$conn->query($sql);
setcookie("token", "", 0);
header("Location: index.php");
handle_login.php
require_once 'conn.php';
require_once 'utils.php';
if (empty($_POST['username']) || empty($_POST['password'])) {
die('資料不齊全');
}
$username = $_POST['username'];
$password = $_POST['password'];
$sql = sprintf("SELECT * FROM users WHERE username='%s' and password='%s'", $username, $password);
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
if ($result->num_rows) {
// 建立 token 並儲存
$token = generateToken();
$sql = sprintf(
"INSERT INTO tokens(token,username) VALUES('%s', '%s')",$token, $username
);
$result = $conn->query($sql);
if (!$result) {
die($conn->error);
}
// 登入成功
$expire = time() + 3600 * 24 * 30;
setcookie("token", $token, $expire);
header("Location: index.php");
} else {
header("Location: login.php?errCode=2");
}
發布留言的顯示暱稱
所以在 handle_add_comment.php
if (!empty($_COOKIE['token'])) {
$user = getUserFromToken($_COOKIE['token']);
$username = $user['username'];
}
utils.php
function getUserFromToken($token)
{
global $conn;
$sql = sprintf("SELECT username FROM tokens WHERE token = '%s'", $token);
$result = $conn->query($sql);
$row = $result->fetch_assoc();
$username = $row['username'];
$sql = sprintf("SELECT * FROM users WHERE username = '%s'", $username);
$result = $conn->query($sql);
$row = $result->fetch_assoc();
return $row; // username, id, nickname
}