C:\UniServerZ\core\php70\
上課範例:活動報系統
系統需求:
<?php echo "Hello World!";
請存到網頁目錄,如:C:\UniServerZ\www\index.php
PHP程式的副檔名一律為.php
PHP程式碼必須放在<?php和?>的起始標記和結束標記符號中。
若是該檔案中只有純粹的PHP語法,並沒有其他的HTML語法,那麼,就不建議加上「?>」符號
echo是PHP用來輸出資料的語言結構(不是函數),常用。
在PHP中,凡是資料類型是字串的都必須用引號包起來,用雙引號或單引號都可以。
每一個PHP語句後要加上「;」結尾。
開啟瀏覽器,在網址列輸入「http://localhost/index.php」或「http://127.0.0.1/index.php」
<?php echo "註解可以直接寫在後面<br>"; // 這是c++風格的單行註解 // 這是c++風格的單行註解 echo "當然註解寫在上面也行<br>"; echo "這是另一種單行註解"; #這是Unix Shell風格的單行註解
<?php /* 這是多行註解只要前後有兩 個多行註解的符號包起來即可 */ /* 這也是多行註解只要前後有兩 個多行註解的符號包起來即可 */
<?php phpinfo();
設定項目 | 建議值 |
---|---|
date.timezone主機預設時區,若主機在台灣,請務必設置為「Asia/Taipei」,否則系統抓到的可能會有誤差。 |
Asia/Taipei |
display_errors是否顯示錯誤訊息?建議開啟!!否則網站變成空白時將很難進行除錯。 |
On |
file_uploads是否允許檔案上傳。需配合 upload_max_filesize, upload_tmp_dir, post_max_size 等設定。一般而言,上傳上限的設定,大小需求如下:memory_limit > post_max_size > upload_max_filesize |
On |
max_execution_time每個程序最大允許執行時間(秒),0 表示沒有限制。這個參數有助於阻止劣質程序無休止的佔用伺服器資源。 |
150 |
max_file_uploads最多只能傳幾個檔案?請視需求設定之。 |
300 |
max_input_time每個程序解析輸入資料的最大允許時間(秒)。 |
120 |
max_input_vars可接收的變數數量,超過此數量,就無法完全接收表單內容。 |
5000 |
memory_limit一個程序所能夠申請到的記憶體空間 (可以使用 K 和 M 作為單位)。 這有助於防止劣質程序消耗完伺服器上的所有記憶體。如果要取消記憶體限制,則必須將其設為 -1 。 |
240M |
post_max_size允許送出的 POST 表單大小。 該值必須大於 upload_max_filesize 的值。 |
220M |
upload_max_filesize允許上傳的檔案的最大尺寸。 |
200M |
如果未來會在網頁目錄下建立許多專案,那麼可以在網頁目錄下建立一個資料夾來放置專案程式的相關檔案,例如建立一個 signup 目錄。
<?php require_once 'smarty/libs/Smarty.class.php'; $smarty = new Smarty; $name = 'Tad'; $smarty->assign('name', $name); $smarty->display('index.tpl');
<h2>Hello {$name}</h2>
傳送內容 | PHP檔(*.php) | Smarty樣板檔(*.tpl) |
---|---|---|
一般變數 |
$name="tad"; $smarty->assign('name', $name);
|
{$name}
|
一維陣列 |
$user['name']="tad"; $user['birthday']="1973-06-16"; $smarty->assign('user', $user);
|
{$user.name} 的生日是 {$user.birthday}
|
二維陣列 |
$users[1]['name']="tad"; $users[1]['birthday']="1973-06-16"; $users[2]['name']="phebe"; $users[2]['birthday']="1973-03-10"; $smarty->assign('users', $users);
|
{foreach $users as $user} {$user.name} 的生日是{$user.birthday} {/foreach} 或 {$user.1.name} 的生日是 {$user.1.birthday} {$user.2.name} 的生日是 {$user.2.birthday}
|
{foreach $來源變數 as $別名} {$別名.索引} {foreachelse} 該變數沒有值時要出現的內容 {/foreach}
{if $變數|in_array:$陣列}
變數後面要加上 |
| 後面加上函數名稱,函數需要的參數用 : 格開
刪除快取:
$smarty->clearAllCache();
<meta name="viewport" content="width=device-width, initial-scale=1"> <!--BootStrap--> <link rel="stylesheet" href="class/bootstrap/css/bootstrap.min.css">
<!--BootStrap js--> <script src="class/bootstrap/js/bootstrap.min.js"></script>
<!--jQuery--> <script src="class/jquery.min.js"></script> <script src="class/jquery-migrate.min.js"></script>
<!--jQuery UI--> <link rel="stylesheet" href="class/jquery-ui/jquery-ui.min.css"> <script src="class/jquery-ui/jquery-ui.min.js"></script>
<!--Font awesome--> <link rel="stylesheet" href="class/font-awesome/css/font-awesome.min.css">
<!DOCTYPE html> <html lang="zh-Hant"> <head> <title>頁面</title> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!--BootStrap--> <link rel="stylesheet" href="class/bootstrap/css/bootstrap.min.css"> <!--jQuery--> <script src="class/jquery.min.js"></script> <script src="class/jquery-migrate.min.js"></script> <!--jQuery UI--> <link rel="stylesheet" href="class/jquery-ui/jquery-ui.min.css"> <script src="class/jquery-ui/jquery-ui.min.js"></script> <!--Font awesome--> <link rel="stylesheet" href="class/font-awesome/css/font-awesome.min.css"> </head> <body> 主內容 <!--BootStrap js--> <script src="class/bootstrap/js/bootstrap.min.js"></script> </body> </html>
<div class="container"> <h1 class="page-header">活動報名系統</h1> <div class="row"> <div class="col-md-9">主內容區</div> <div class="col-md-3">側邊欄</div> </div> </div>
<div class="panel panel-danger"> <div class="panel-heading">登入</div> <div class="panel-body"> 面板內容 </div> </div>
<form class="form-horizontal" action="送至.php" method="post" role="form"> 表單元件 </form>
<div class="form-group"> <label class="col-md-3 control-label">標籤文字</label> <div class="col-md-9"> 表單元件(套用 class="form-control") </div> </div>
<form class="form-horizontal" action="index.php" method="post" role="form"> <div class="form-group"> <label class="col-md-4 control-label" for="name">帳號:</label> <div class="col-md-8"> <input type="text" name="name" id="name" placeholder="請輸入帳號" class="form-control"> </div> </div> <div class="form-group"> <label class="col-md-4 control-label" for="passwd">密碼:</label> <div class="col-md-8"> <input type="password" name="passwd" id="passwd" placeholder="請輸入密碼" class="form-control"> </div> </div> <div class="text-center"> <input type="hidden" name="op" value="login"> <button type="submit" name="button" class="btn btn-primary">登入</button> </div> </form>
"allowed_file_extensions": ["htm", "html", "xhtml", "shtml", "xml", "svg", "tpl"],
<div class="col-md-3"> {include file='side_login.tpl'} </div>
<head> <title>{$page_title}</title> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!--BootStrap--> <link rel="stylesheet" href="class/bootstrap/css/bootstrap.min.css"> <!--jQuery--> <script src="class/jquery.min.js"></script> <script src="class/jquery-migrate.min.js"></script> <!--jQuery UI--> <link rel="stylesheet" href="class/jquery-ui/jquery-ui.min.css"> <script src="class/jquery-ui/jquery-ui.min.js"></script> <!--Font awesome--> <link rel="stylesheet" href="class/font-awesome/css/font-awesome.min.css"> </head>
<!DOCTYPE html> <html lang="zh-Hant"> {include file='head.tpl'} <body> <div class="container"> <h1 class="page-header">{$page_title}</h1> <div class="row"> <div class="col-md-9"> <h2>主內容</h2> </div> <div class="col-md-3"> <h2>Hi {$name}!</h2> {include file='side_login.tpl'} </div> </div> </div> <!--BootStrap js--> <script src="class/bootstrap/js/bootstrap.min.js"></script> </body> </html>
<?php require_once 'smarty/libs/Smarty.class.php'; $smarty = new Smarty; $name = 'Tad'; $smarty->assign('name', $name); $smarty->assign('page_title', '活動報名系統'); $smarty->display('index.tpl');
$admin_id = 'tad@tn.edu.tw'; $admin_pass = '12345';
htmlspecialchars($string,ENT_QUOTES)
更好。htmlspecialchars
很像的 htmlentities 函數並不適用中文,因為會連同中文字一起轉義。htmlentities
和 htmlspecialchars
只能防止XSS攻擊,不能防止SQL隱碼攻擊。
$title = htmlspecialchars($_POST['title'], ENT_QUOTES);
$title = filter_var($_POST['title'], FILTER_SANITIZE_SPECIAL_CHARS);
名稱 | 功用 |
---|---|
FILTER_CALLBACK | option可以讓開發者用自訂的function處理 |
FILTER_SANITIZE_STRING | 去除標籤或特殊字元(html標籤會直接被消除) |
FILTER_SANITIZE_ENCODED | 與urlencode()相同,過濾特殊字串 |
FILTER_SANITIZE_MAGIC_QUOTES | 過濾針對SQL injection做過濾(例如單、雙引號) |
FILTER_SANITIZE_SPECIAL_CHARS | 針對HTML做encoding,例如<會轉成< |
FILTER_SANITIZE_EMAIL | 過濾e-mail,刪除e-mail格式不該出現的字元(除了$-_.+!*'{}|^~[]`#%/?@&=和數字),例如a(b)@gmail.com會被過濾成ab@gmail.com |
FILTER_SANITIZE_URL | 過濾URL,刪除URL格式不該出現的字元 |
FILTER_SANITIZE_NUMBER_INT | 刪除所有字元,只留下數字與+-符號 |
FILTER_SANITIZE_NUMBER_FLOAT | 刪除所有字元,只留下數字和+-.,eE |
FILTER_VALIDATE_INT | 判斷數字是否有在範圍內 |
FILTER_VALIDATE_BOOLEAN | 判斷布林值,1、true、on、yes都會判斷成true,反之為false,若是這些以外的值會回傳NULL |
FILTER_VALIDATE_FLOAT | 判斷是否為浮點數 |
FILTER_VALIDATE_REGEXP | 利用regexp做驗證 |
FILTER_VALIDATE_URL | URL驗證 |
FILTER_VALIDATE_EMAIL | e-mail驗證 |
FILTER_VALIDATE_IP | IP驗證 |
(int) $xxx
或 intval($xxx)
即可,會強制把輸入的內容變成數字。filter_var($num, FILTER_SANITIZE_NUMBER_INT)
來過濾,但和 intval 不太一樣,例如:
$num = "123其他文字456";
echo filter_var($num, FILTER_SANITIZE_NUMBER_INT) . "<br>";
echo (int) $num . "<br>";
echo intval($num) . "<br>";
用用 (int) $xxx
或 intval($xxx)
會得到 123,但用filter_var()
會得到 123456
if(判斷條件){ //條件為真執行 }elseif(判斷條件){ //elseif的條件為真執行 }else{ //條件為假時執行 }
{if 判斷條件} //條件為真執行 {elseif 判斷條件} //elseif 的條件為真執行 {else} //條件為假時執行 {/if}
$op = ''; if (isset($_REQUEST['op'])) { $op = filter_var($_REQUEST['op'], FILTER_SANITIZE_SPECIAL_CHARS); }
$op = isset($_REQUEST['op']) ? filter_var($_REQUEST['op'], FILTER_SANITIZE_SPECIAL_CHARS) : "";
switch ($變數) { case '特定值': # 動作... break; default: # 動作... break; }
<?php /*引入檔案(初始設定)*/ session_start(); require_once 'config.php'; require_once 'smarty/libs/Smarty.class.php'; $smarty = new Smarty; /*過濾變數*/ $op = isset($_REQUEST['op']) ? filter_var($_REQUEST['op'], FILTER_SANITIZE_SPECIAL_CHARS) : ""; $group = isset($_SESSION['group']) ? $_SESSION['group'] : ''; $name = isset($_SESSION['name']) ? $_SESSION['name'] : '訪客'; $content = "主內容"; /*執行流程*/ switch ($op) { case 'login': $name = isset($_POST['email']) ? filter_var($_POST['email'], FILTER_SANITIZE_SPECIAL_CHARS) : "訪客"; $pass = isset($_POST['pass']) ? $_POST['pass'] : ""; if ($name == $admin_id and $admin_pass == $passwd) { $_SESSION['group'] = 'admin'; $_SESSION['name'] = $name; $content = "登入成功"; } else { $content = "登入失敗"; } header("location:{$_SERVER['PHP_SELF']}"); exit; default: # 取得活動列表 break; } /*輸出結果*/ $smarty->assign('name', $name); $smarty->assign('group', $group); $smarty->assign('content', $content); $smarty->assign('page_title', '活動報名系統'); $smarty->display('index.tpl');
<div class="panel panel-success"> <div class="panel-heading">工具列</div> <!-- List group --> <div class="list-group"> <a href="index.php" class="list-group-item">回首頁</a> <a href="admin.php" class="list-group-item">發布活動</a> <a href="index.php?op=logout" class="list-group-item">登出</a> </div> </div>
<!DOCTYPE html> <html lang="zh-Hant"> {include file='head.tpl'} <body> <div class="container"> <h1 class="page-header">{$page_title}</h1> <div class="row"> <div class="col-md-9"> <h2>主內容</h2> {$content} </div> <div class="col-md-3"> <h2>Hi {$name}!</h2> {if $group=='admin'} {include file='side_tools.tpl'} {else} {include file='side_login.tpl'} {/if} </div> </div> </div> <!--BootStrap js--> <script src="class/bootstrap/js/bootstrap.min.js"></script> </body> </html>
(溫馨提示:可以用unset() 移除掉 session 變數,也可以把 session 變數的值改掉。)
function 函數名稱($參數1='預設值' , $參數2='預設值',...){ global $外面的變數; 函數內容,任何有效的 PHP 程式碼,包括其它函數和class定義 ; return 傳回值; }
function login() { require_once "config.php"; $name = isset($_POST['name']) ? filter_var($_POST['name'], FILTER_SANITIZE_SPECIAL_CHARS) : ''; $passwd = isset($_POST['passwd']) ? $_POST['passwd'] : ''; if ($name == $admin_id and $admin_pass == $passwd) { $_SESSION['group'] = 'admin'; $_SESSION['name'] = $name; } }
switch ($op) { case 'login': login(); header("location:{$_SERVER['PHP_SELF']}"); exit; case "logout": header("location:{$_SERVER['PHP_SELF']}"); exit; default: # code... break; }
$stud[1]="tad"; $stud[2]="phebe"; 或 $stud=[1=>"tad", 2=>"phebe"]; 或 $stud=array(1=>"tad", 2=>"phebe"); 使用: echo $stud[1];
$stud[1][1]="tad"; $stud[1][2]="phebe"; $stud[2][1]="kiki"; $stud[2][2]="huhui"; 或 $stud = [ 1 => [1 => "tad", 2 => "phebe"], 2 => [1 => "kiki", 2 => "huhui"] ]; 或 $stud = array( 1 => array(1 => "tad", 2 => "phebe"), 2 => array(1 => "kiki", 2 => "huhui") ); 使用: echo $stud[2][1];
數學運算符 | 範例 | 範例解釋 | 範例結果 |
---|---|---|---|
+(加) | $a + $b | $a加$b | 10+4的結果:14 |
-(減) | $a - $b | $a減$b | 10-4的結果:6 |
*(乘) | $a * $b | $a乘以$b | 10*4的結果:40 |
/(除) | $a / $b | $a除以$b | 10/4的結果:2.5 |
%(求餘數) | $a % $b | $a除以$b的餘數 | 10%4的結果:2 |
$a = $a + 2; 可改寫成 $a += 2;
例子 | 運算符意義 | 解釋 |
---|---|---|
$a == $b | ==相等 | $a和$b的值相等時,才為真 |
$a === $b | ===全等 | $a和$b的值以及資料形態都相等時才為真! |
$a != $b | !=不相等 | $a和$b的值不相等時,才為真 |
$a <> $b | !=不相等 | $a和$b的值不相等時,才為真 |
$a !== $b | !==不全等 | $a和$b的值或資料形態不相等時才為真! |
$a < $b | <小於 | $a小於$b才為真 |
$a > $b | >大於 | $a大於$b才為真 |
$a <= $b | <=小於等於 | $a小於或等於$b才為真 |
$a >= $b | >=大於等於 | $a大於或等於$b才為真 |
$a <=> $b | 比較 | PHP7才新增的 <=> 運算符,只會傳回 -1、0、1三種值! -1,代表左邊小於右邊; 1,代表左邊大於右邊; 0,那就代表左右兩邊相等。 |
運算符 | 意義 | 說明 |
---|---|---|
++$a | 先遞增 | $a值加1之後才傳回$a值 |
$a++ | 後遞增 | 先傳回$a值之後再將$a值加1 |
--$a | 先遞減 | $a值減1之後才傳回$a值 |
$a-- | 後遞減 | 先傳回$a值之後再將$a值減1 |
範例 | 邏輯運算符 | 意義 |
---|---|---|
$a and $b | and(與) | 只有$a 與 $b兩者皆為真,結果才為真 |
$a && $b | and(與) | 只有$a 與 $b兩者皆為真,結果才為真 |
$a or $b | or(或) | 只要$a 或 $b兩者之一為真,結果就為真 |
$a || $b | or(或) | 只要$a 或 $b兩者之一為真,結果就為真 |
$a xor $b | exclusive or(互斥) | 只有$a 與 $b一為真、一為假時,結果才為真 |
!$a | not(否) | 只有$a為假時,結果才為真 |
echo "嗨!" . $user_name . "您好!"; echo "資料庫名稱:" . _DB_NAME; echo "今天是:" . date("Y年m月d日");
實際上,這應該是寫系統最先要做的事,不過,由於大家都是剛入門,所以,擺到這裡才開始說。
因為選用正確的欄位類型可以:
資料型態 | 資料範圍 |
---|---|
tinyint(4) | -128至127 unsigned:0至255 |
smallint(6) | -32768至32767 unsigned:0至65535 |
mediumint(9) | -8388608至8388607 unsigned:0至16777215 |
int(11) | -2147683648至2147683647 unsigned:0至4294967295 |
bigint(20) | -9223372036854775808至9223372036854775807 unsigned:0至18446744073709551615 |
資料型態 | 範圍範例(以MySQL>3.23為例) |
---|---|
decimal[(65[,30])] | decimal(4,1)-999.9到9999.9 decimal(5,1)-9999.9到99999.9 decimal(6,1)-99999.9到999999.9 decimal(6,2)-9999.99到 99999.99 decimal(6,3)-999.999到9999.999 |
float[(255,30)] | float(4,1)-999.9到999.9 float(5,1)-9999.9到9999.9 float(6,1)-99999.9到99999.9 float(6,2)-9999.99到 9999.99 float(6,3)-999.999到999.999 |
double[(255,30)] | double(4,1)-999.9到999.9 double(5,1)-9999.9到9999.9 double(6,1)-99999.9到99999.9 double(6,2)-9999.99到 9999.99 double(6,3)-999.999到999.999 |
資料型態 | 範圍 |
---|---|
date | 1000-01-01至9999-12-31 西元年可用4或2個數字,使用2個數字時,70到99表示1970到1999; 如果是00到69就是2000到2069,有點容易搞錯,所以年份最好還是寫完整4位數比較沒困擾。 |
datetime | 1000-01-01 00:00:00至9999-12-31 23:59:59 |
timestamp | 1970-01-01 00:00:01 UTC到2038-01-19 03:14:07 UTC 其格式與datetime一樣,但儲存空間只需要一半。 |
time | -838:59:59至 838:59:59 存入「150:30:00」而言就是過了150小時又30分鐘之意。 亦可存入「-6:20:00」,意指「6個小時又20分鐘前」 |
year[(4|2)] | 4位數字可以儲存的範圍從1901到2155; 2位數字的範圍從00到99,實際的西元年份是1970到2069,也就是說,當您存入00時,實際代表2000之意; 存入69代表2069;存入70代表1970;存入99代表1999。 |
資料型態 | 最大長度 | 實際儲存的空間 |
---|---|---|
char[(255)] | 255 | 指定的長度 |
varchar(65535) | 65535 | 指定的長度加1或2bytes |
tinytext | 255 | 指定的長度加1byte |
text | 65535 | 指定的長度加2bytes |
mediumtext | 16772215 | 指定的長度加3bytes |
longtext | 4294967295 | 指定的長度加4bytes |
資料型態 | 最大個數 | 儲存空間 |
---|---|---|
enum(字串值[,...]) | 65535 | 1byte(25個以內); 2bytes(256到65535個) |
set(字串值[,...]) | 64 | 1byte(8個以內); 2bytes(16個以內); 3bytes(24個以內); 4bytes(32個以內); 8bytes(64個以內) |
資料型態 | 最大長度 | 實際儲存的空間 |
---|---|---|
bit[(8|64)] | 64 | bit的範圍為0到1 bit(8)的範圍為0到255 bit(64)的範圍為0到18446744073709551615 |
binary[(255)] | 255 | 指定的長度 |
varbinary(65535) | 65535 | 指定的長度加1或2bytes |
tinyblob | 255 | 指定的長度加1byte |
blob | 65535 | 指定的長度加2bytes |
mediumblob | 16772215 | 指定的長度加3bytes |
longblob | 4294967295 | 指定的長度加4bytes |
<div class="panel panel-success"> <div class="panel-heading">註冊</div> <div class="panel-body"> <form class="form-horizontal" action="index.php" method="post" role="form"> <div class="form-group"> <label class="col-md-4 control-label" for="email">Email:</label> <div class="col-md-8"> <input type="text" name="email" id="email" placeholder="請輸入Email" class="form-control"> </div> </div> <div class="form-group"> <label class="col-md-4 control-label" for="pass">密碼:</label> <div class="col-md-8"> <input type="text" name="pass" id="pass" placeholder="請設定密碼" class="form-control"> </div> </div> <div class="form-group"> <label class="col-md-4 control-label" for="name">姓名:</label> <div class="col-md-8"> <input type="text" name="name" id="name" placeholder="請輸入中文姓名" class="form-control"> </div> </div> <div class="text-center"> <button type="submit" name="op" value="save_regist" class="btn btn-primary">確定註冊</button> </div> </form> </div> </div>
其中,密碼改為明碼,避免使用者輸入錯誤。或者也可以改成輸入兩次密碼,然後進行比對亦可(只是比較麻煩)。
另外,記得設定 op,以便告訴程式下一步要做什麼。
當然原本的登入表單 templates\side_login.tpl 也要改一下帳號部份,另外,還得加上註冊的連結:
<div class="panel panel-primary"> <div class="panel-heading">登入</div> <div class="panel-body"> <form class="form-horizontal" action="index.php" method="post" role="form"> <div class="form-group"> <label class="col-md-4 control-label" for="email">Email:</label> <div class="col-md-8"> <input type="text" name="email" id="email" placeholder="請輸入Email" class="form-control"> </div> </div> <div class="form-group"> <label class="col-md-4 control-label" for="pass">密碼:</label> <div class="col-md-8"> <input type="password" name="pass" id="pass" placeholder="請輸入密碼" class="form-control"> </div> </div> <div class="text-center"> <input type="hidden" name="op" value="login"> <a href="index.php?op=regist" class="btn btn-success">註冊</a> <button type="submit" name="button" class="btn btn-primary">登入</button> </div> </form> </div> </div>
case 'regist': break;
{if $op=='regist'} {include file='regist_form.tpl'} {else} {$content} {/if}
問題是,index.tpl如何取得 $op 變數?當然得由index.php傳給他!所以,修改 index.php,多傳一個 $smarty->assign('op', $op); 到樣板。
require_once 'smarty/libs/Smarty.class.php'; $smarty = new Smarty; $smarty->assign('name', $name); $smarty->assign('group', $group); $smarty->assign('content', $content); $smarty->assign('op', $op); $smarty->assign('page_title', '活動報名系統'); $smarty->display('index.tpl');
function link_db() { $mysqli = new mysqli(_DB_LOCATON, _DB_ID, _DB_PASS, _DB_NAME); if ($mysqli->connect_error) { die('無法連上資料庫:' . $mysqli->connect_error); } $mysqli->set_charset("utf8"); return $mysqli; }
<?php $admin_id = 'tad'; $admin_pass = '12345'; //資料庫位址 define('_DB_LOCATON', 'localhost'); //資料庫帳號 define('_DB_ID', 'root'); //資料庫密碼 define('_DB_PASS', '12345'); //資料庫名稱 define('_DB_NAME', 'signup');
case "save_regist": save_regist(); header("location:{$_SERVER['PHP_SELF']}"); exit;
也就是註冊動作交由 save_regist() 函數來處理
註冊完,記得轉向,避免一直保留在「正在註冊的狀態」,若是沒轉向,只要一重新整理,資料庫便會多出一筆一樣的資料。
insert [into] 資料表名稱 [(欄位1,欄位2...)] values (值1,值2...)
<?php session_start(); require_once "config.php"; require_once "function.php"; require_once 'smarty/libs/Smarty.class.php'; $smarty = new Smarty; $db = link_db();
//新增使用者 function save_regist() { global $admin_id, $db; $name = $db->real_escape_string($_POST['name']); $email = $db->real_escape_string($_POST['email']); $pass = $db->real_escape_string($_POST['pass']); $group = $name == $admin_id ? 'admin' : 'user'; $sql = "INSERT INTO `users` ( `name`, `email`, `pass`, `group`) VALUES ('{$name}', '{$email}', '{$pass}', '{$group}')"; $db->query($sql) or die($db->error); $uid = $db->insert_id; return $uid; }
做到這邊很高興終於能新增使用者了,以為以後高枕無憂了?還早的呢...
所以,要注意的東西多著呢!我們一個一個來處理。
$email = $db->real_escape_string($_POST['email']); $email = filter_var($email, FILTER_VALIDATE_EMAIL); if (!$email) { die("不合法的Email"); }
$name = $db->real_escape_string($_POST['name']); if (empty($name)) { die("姓名為必填!"); } $email = $db->real_escape_string($_POST['email']); if (empty($email)) { die("Eamil為必填!"); } $email = filter_var($email, FILTER_VALIDATE_EMAIL); if (!$email) { die("不合法的Email"); } $pass = $db->real_escape_string($_POST['pass']); if (empty($pass)) { die("密碼為必填!"); }
$pass = $db->real_escape_string($_POST['pass']); if (empty($pass)) { die("密碼為必填!"); } $pass = password_hash($pass, PASSWORD_DEFAULT);
if (empty($name)) { throw new Exception("姓名為必填!"); }
可用 Ctrl + H 來全部取代
有些在 or 後面的要改成:
if (!$db->query($sql)) { throw new Exception($db->error); }
try { switch ($op) { case 'login': login(); header("location:{$_SERVER['PHP_SELF']}"); exit; case "logout": logout(); header("location:{$_SERVER['PHP_SELF']}"); exit; case "save_regist": save_regist(); header("location:{$_SERVER['PHP_SELF']}"); exit; default: # code... break; } } catch (exception $e) { $error = $e->getMessage(); }
若想知道更多方法,可參考:http://php.net/manual/en/language.exceptions.extending.php
我們可以把 $error 送到樣板檔(檔案最前面記得給$error一個預設值):
require_once 'smarty/libs/Smarty.class.php'; $smarty = new Smarty; $smarty->assign('name', $name); $smarty->assign('group', $group); $smarty->assign('op', $op); $smarty->assign('error', $error); $smarty->assign('page_title', '活動報名系統'); $smarty->display('index.tpl');
做一個新的樣板檔 \templates\alert_error.tpl,內容為:
<div class="alert alert-danger"> <h2>{$error}</h2> </div>
接著在 index.tpl 主樣板檔中判斷是否有 $error,若有,引入上面的樣板檔,以呈現錯誤訊息。
<div class="col-md-9"> {if $error} {include file='alert_error.tpl'} {/if} </div>
SELECT `查詢的欄位` [FROM `資料表名稱` 附加的篩選條件]
[where 篩選條件] [group by `欄位名稱`][having group的篩選條件] [order by {unsigned_integer | `欄位名稱` | formula} [asc | desc] ,...] [limit [起點,] 筆數]
//登入 function login() { global $db; $email = $db->real_escape_string($_POST['email']); if (empty($email)) { throw new Exception("Eamil為必填!"); } $email = filter_var($email, FILTER_VALIDATE_EMAIL); if (!$email) { throw new Exception("不合法的Email"); } $pass = isset($_POST['pass']) ? $_POST['pass'] : ''; $sql = "SELECT * FROM `users` where `email`='{$email}'"; $result = $db->query($sql); if (!$result) { throw new Exception($db->error); } $data = $result->fetch_assoc(); if (password_verify($pass, $data['pass'])) { $_SESSION['group'] = $data['group']; $_SESSION['name'] = htmlspecialchars($data['name'], ENT_QUOTES); $_SESSION['uid'] = $data['uid']; $_SESSION['email'] = htmlspecialchars($data['email'], ENT_QUOTES); } else { throw new Exception("登入失敗!"); } }
//登出 function logout() { unset($_SESSION['group']); unset($_SESSION['name']); unset($_SESSION['uid']); unset($_SESSION['email']); }
$name = $db->real_escape_string($_POST['name']); if (empty($name)) { throw new Exception("姓名為必填!"); } $email = $db->real_escape_string($_POST['email']); if (empty($email)) { throw new Exception("Eamil為必填!"); } $email = filter_var($email, FILTER_VALIDATE_EMAIL); if (!$email) { throw new Exception("不合法的Email"); } $pass = $db->real_escape_string($_POST['pass']); if (empty($pass)) { throw new Exception("密碼為必填!"); }
可簡化為
$name = clean_var('name', '姓名'); $email = clean_var('email', 'Eamil', FILTER_VALIDATE_EMAIL); $pass = clean_var('pass', '密碼');
參考答案:
//檢查並傳回欲拿到資料使用的變數 function clean_var($var = '', $title = '', $filter = '') { global $db; $clean_var = $db->real_escape_string($_REQUEST[$var]); if (empty($clean_var)) { throw new Exception("{$title}為必填!"); } if ($filter) { $clean_var = filter_var($clean_var, $filter); if (!$clean_var) { throw new Exception("不合法的{$title}"); } } return $clean_var; }
{if $group} {include file='side_tools.tpl'} {else} {include file='side_login.tpl'} {/if}
<div class="panel panel-success"> <div class="panel-heading">工具列</div> <!-- List group --> <div class="list-group"> <a href="index.php" class="list-group-item">回首頁</a> {if $group=="admin"} <a href="admin.php" class="list-group-item">發布活動</a> {elseif $group=="user"} <a href="my.php" class="list-group-item">我參加的活動</a> {/if} <a href="index.php?op=logout" class="list-group-item">登出</a> </div> </div>
<?php session_start(); require_once "config.php"; require_once "function.php"; require_once 'smarty/libs/Smarty.class.php'; $smarty = new Smarty; $db = link_db(); $op = isset($_REQUEST['op']) ? htmlspecialchars($_REQUEST['op'], ENT_QUOTES) : ''; $group = isset($_SESSION['group']) ? $_SESSION['group'] : ''; $name = isset($_SESSION['name']) ? $_SESSION['name'] : '訪客'; $content = $error = ''; if ($group != 'admin') { header("location: index.php"); exit; } try { switch ($op) { default: # code... break; } } catch (exception $e) { $error = $e->getMessage(); } $smarty->assign('name', $name); $smarty->assign('group', $group); $smarty->assign('content', $content); $smarty->assign('op', $op); $smarty->assign('error', $error); $smarty->assign('page_title', '活動管理'); $smarty->display('index.tpl');
樣板一樣用同樣的 index.tpl 即可。
請確定在沒有登入時,是無法看到 http://localhost/signup/admin.php 頁面的。
<?php session_start(); require_once "config.php"; require_once "function.php"; require_once 'smarty/libs/Smarty.class.php'; $smarty = new Smarty; $db = link_db(); $op = isset($_REQUEST['op']) ? htmlspecialchars($_REQUEST['op'], ENT_QUOTES) : ''; $group = isset($_SESSION['group']) ? $_SESSION['group'] : ''; $name = isset($_SESSION['name']) ? $_SESSION['name'] : '訪客'; $content = $error = '';
<?php require_once "header.php"; if ($group != 'admin') { header("location: index.php"); exit; }
<?php $smarty->assign('name', $name); $smarty->assign('group', $group); $smarty->assign('content', $content); $smarty->assign('op', $op); $smarty->assign('error', $error); $smarty->assign('page_title', $page_title); $smarty->display('index.tpl');
try { switch ($op) { default: $content = action_form(); break; } } catch (exception $e) { $error = $e->getMessage(); } $page_title = '活動管理'; require_once "footer.php";
function action_form() { require_once "class/php-bootstrap-form/PFBC/Form.php"; $values = []; ob_start(); echo '<script type="text/javascript" src="class/My97DatePicker/WdatePicker.js"></script>'; Form::open("action", $values); Form::Hidden("op", 'insert_action'); Form::Textbox("活動名稱", "title", ['required' => 1]); Form::Textbox("活動日期", "action_date", ['required' => 1, 'onClick' => "WdatePicker()"]); Form::Textbox("截止日期", "end_date", ['required' => 1, 'onClick' => "WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:00'})"]); Form::YesNo("使否啟用", "enable"); Form::CKEditor("活動內容", "content"); Form::Button('儲存', 'submit'); Form::close(false); $form = ob_get_contents(); ob_end_clean(); return $form; }
$values = [];是個個欄位預設值,未來在修改時會用到
若新增時,想要替某些欄位設定預設值,直接填入 $values 陣列即可(要按照順序),例如:
$values = [ 'title' => '活動', 'action_date' => date("Y-m-d", strtotime("+14 day")), 'end_date' => date("Y-m-d H:i:00", strtotime("+10 day")), 'enable' => 1, 'content' => '活動內容~~~', ];
由於該表單物件會直接輸出,故利用 ob_start(); 將其輸出移至緩衝區,再利用 ob_get_contents(); 把輸出的東西取出來並存入 $form,最後用 ob_end_clean(); 釋放掉。
表單一律從 Form::open() 開始,Form::close() 結束,中間便是表單欄位。
表單預設的 action 位置就是該檔案本身,method 預設為post
引入 WdatePicker.js 目的是為了使用 My97DatePicker 小月曆
詳細表單欄位用法可參考:http://smarttechdo.com/~avb/pfbc/api/
case "insert_action": $action_id = insert_action(); header("location:index.php?action_id=$action_id"); exit;
設定要去執行 insert_action() 函數,也就是用來新增活動到資料庫,並希望他能傳回活動編號,以便儲存後,可以直接連到首頁index.php去觀看該活動的詳細內容。
接著製作 insert_action() 函數,由於寫入的過程都差不多,在此會建議直接複製之前的寫入函數來修改會更快:
//新增活動 function insert_action() { global $db; $title = clean_var('title', '活動名稱'); $action_date = clean_var('action_date', '活動日期'); $end_date = clean_var('end_date', '截止日期'); $enable = clean_var('enable', '使否啟用'); $content = clean_var('content', '活動內容'); $uid = $_SESSION['uid']; $sql = "INSERT INTO `actions` ( `title`, `content`, `action_date`, `end_date`, `uid`, `enable`) VALUES ('{$title}', '{$content}', '{$action_date}', '{$end_date}', '{$uid}', '{$enable}')"; if (!$db->query($sql)) { throw new Exception($db->error); } $action_id = $db->insert_id; return $action_id; }
$action_id = isset($_REQUEST['action_id']) ? intval($_REQUEST['action_id']) : ''; switch ($op) { case 'regist': break; case 'login': login(); header("location:{$_SERVER['PHP_SELF']}"); exit; case "logout": logout(); header("location:{$_SERVER['PHP_SELF']}"); exit; case "save_regist": save_regist(); header("location:{$_SERVER['PHP_SELF']}"); exit; default: if ($action_id) { $op = 'show_action'; show_action($action_id); } else { $op = 'list_action'; list_action(); } break; }
由於流程中會用到 $action_id,我們確定 $action_id 是個編號,所以,利用 intval() 去過濾之,並且接收來自任何方法的 $action_id,故用 $_REQUEST['action_id']。
由於頁尾檔有送 $op 直到樣板,因此,目前在進行的任何一個動作最好都有一個 op 值,故,我們在 default 中,針對那兩個函數各給一個 $op 值,以便樣板可以辨識目前要出現的內容為何。
show_action($action_id) 及 list_action() 為什麼沒有傳回 $content?因為我們打算直接把內容,用樣板變數分別直接傳給樣板,所以,在此就不同一做出結果再傳回,而是直接送給樣板來處理,這樣會版面設計會更彈性。
此外,原本還有一個 op 是 regist,用來註冊用,在此,也要多一組設定。因為若沒有設定,switch會自動跑去 default,由於沒有編號,所以 op 會變成 list_action,如此,使用者將永遠看不到註冊表單。
//列出所有活動 function list_action() { global $db, $smarty; $sql = "SELECT * FROM `actions` order by action_date desc"; $result = $db->query($sql); if (!$result) { throw new Exception($db->error); } $actions = []; while ($values = $result->fetch_assoc()) { $actions[] = $values; } $smarty->assign('actions', $actions); }
由於打算直接在函數中把呈現列表時,樣板需要的變數直接傳到樣板,因此,直接用 global 宣告 $smarty 物件,讓他可以在函數中使用。
撈資料時,我們以活動日期為排序依據,從新排到舊(日期大到小)
此時的 $actions 是一個二維陣列,第一個索引為第N筆資料,第二個索引則為某一筆資料的各個欄位。
從現實考量,這裡暫時不限制enable=1以及報名截止日的問題。
{if $op=="regist"} {include file='regist_form.tpl'} {elseif $op=="list_action"} {include file='list_action.tpl'} {else} {$content} {/if}
也就是當執行列出活動時,就引入 list_action.tpl 作為主內容,而 list_action.tpl 只專心做列出活動的列表即可。
list_action.tpl 的內容為:
<h2>活動列表</h2> <table class="table table-hover table-striped"> <thead> <tr class="info"> <th>活動日期</th> <th>活動名稱</th> <th>報名截止日</th> <th>功能</th> </tr> </thead> <tbody> {foreach $actions as $action} <tr> <td>{$action.action_date}</td> <td><a href="index.php?action_id={$action.action_id}">{$action.title}</a></td> <td>{$action.end_date}</td> <td></td> </tr> {/foreach} </tbody> </table>
//列出指定活動 function show_action($action_id) { global $db, $smarty; $sql = "SELECT * FROM `actions` where `action_id` ='{$action_id}'"; $result = $db->query($sql); if (!$result) { throw new Exception($db->error); } $action = $result->fetch_assoc(); $smarty->assign('action', $action); }
{if $op=="regist"} {include file='regist_form.tpl'} {elseif $op=="list_action"} {include file='list_action.tpl'} {elseif $op=='show_action'} {include file='show_action.tpl'} {else} {$content} {/if}
<h2>{$action.title}</h2> <div class="panel panel-default"> <div class="panel-heading">活動日期:{$action.action_date}</div> <div class="panel-body"> {$action.content} </div> <div class="panel-footer">報名截止日:{$action.end_date}</div> </div>
{foreach $actions as action} <tr> <td>{$action.action_date}</td> <td><a href="index.php?action_id={$action.action_id}">{$action.title}</a></td> <td>{$action.end_date}</td> <td> {if $group=="admin"} <a href="admin.php?action_id={$action.action_id}" class="btn btn-warning btn-xs">修改</a> {/if} </td> </tr> {/foreach}
{if $group=="admin"} <div class="text-center"> <a href="admin.php?action_id={$action.action_id}" class="btn btn-warning">修改</a> </div> {/if}
$action_id = isset($_REQUEST['action_id']) ? intval($_REQUEST['action_id']) : ''; switch ($op) { case "insert_action": $action_id = insert_action(); header("location:index.php?action_id=$action_id"); exit; default: $content = action_form($action_id); break; }
function action_form($action_id = '') { global $db; $values = []; $op = 'insert_action'; if ($action_id) { $sql = "SELECT * FROM `actions` where `action_id`='{$action_id}'"; $result = $db->query($sql); if (!$result) { throw new Exception($db->error); } $values = $result->fetch_assoc(); $op = 'update_action'; } require_once "class/php-bootstrap-form/PFBC/Form.php"; ob_start(); echo '<script type="text/javascript" src="class/My97DatePicker/WdatePicker.js"></script>'; Form::open("action", $values); Form::Hidden("op", $op); Form::Hidden("action_id", $action_id); Form::Textbox("活動名稱", "title", ['required' => 1]); Form::Textbox("活動日期", "action_date", ['required' => 1, 'onClick' => "WdatePicker()"]); Form::Textbox("截止日期", "end_date", ['required' => 1, 'onClick' => "WdatePicker({dateFmt:'yyyy-MM-dd HH:mm:00'})"]); Form::YesNo("使否啟用", "enable"); Form::CKEditor("活動內容", "content"); Form::Button('儲存', 'submit'); Form::close(false); $form = ob_get_contents(); ob_end_clean(); return $form; }
其中 $op 的值也要根據狀況來給不同值,若是有 $action_id 的值,那就表示要修改,故設為 update_action
若是沒有,就表示是要新增,故 $op 的值設為 insert_action
另外,要修改時多一個隱藏欄位,將 action_id 的值送出,如此,修改時,才知道要修改哪一筆活動資料。
case "update_action": update_action($action_id); header("location:index.php?action_id=$action_id"); exit;
update `資料表名稱` set `欄位1`='值1', `欄位2`='值2', ... [where 篩選條件] [limit 筆數]
//更新活動 function update_action($action_id) { global $db; $title = clean_var('title', '活動名稱'); $action_date = clean_var('action_date', '活動日期'); $end_date = clean_var('end_date', '截止日期'); $enable = clean_var('enable', '使否啟用'); $content = clean_var('content', '活動內容'); $uid = $_SESSION['uid']; $sql = "UPDATE `actions` SET `title`='{$title}', `content`='{$content}', `action_date`='{$action_date}', `end_date`='{$end_date}', `enable`='{$enable}', `uid`='{$uid}' WHERE `action_id`='{$action_id}'"; if (!$db->query($sql)) { throw new Exception($db->error); } }
{if $group=="admin"} <a href="admin.php?op=delet_action&action_id={$action.action_id}" class="btn btn-danger btn-xs">刪除</a> <a href="admin.php?action_id={$action.action_id}" class="btn btn-warning btn-xs">修改</a> {/if}
{if $group=="admin"} <div class="text-center"> <a href="admin.php?op=delet_action&action_id={$action.action_id}" class="btn btn-danger ">刪除</a> <a href="admin.php?action_id={$action.action_id}" class="btn btn-warning">修改</a> </div> {/if}
case "delete_action": delete_action($action_id); header("location:index.php"); exit;
delete from `資料表名稱` [where 篩選條件] [limit 筆數]
//刪除活動 function delete_action($action_id) { global $db; $sql = "DELETE FROM `actions` WHERE `action_id`='{$action_id}'"; if (!$db->query($sql)) { throw new Exception($db->error); } }
<a href="javascript:delete_action({$action.action_id})" class="btn btn-danger btn-xs">刪除</a>
<script type="text/javascript" src="class/sweet-alert/sweetalert.min.js"></script> <link rel="stylesheet" type="text/css" href="class/sweet-alert/sweetalert.css" /> <script type="text/javascript"> function delete_action(id){ swal({ title: "確定要刪除嗎?", text: "刪除後資料就消失救不回來囉!", type: "warning", showCancelButton: true, confirmButtonColor: "#DD6B55", confirmButtonText: "是!含淚刪除!", cancelButtonText: "不...別刪", closeOnConfirm: false }, function(){ swal("OK!刪掉惹!", "該資料已經隨風而逝了...", "success"); location.href='admin.php?op=delete_action&action_id=' + id; }); } </script>
然後分別在 show_action.tpl 和 list_action.tpl 中,引入該樣板檔即可:
{include file='sweet-alert.tpl'}
剛剛都是以管理員角度看事情,現在請註冊幾個使用者,要開始以一般使用者的角度看系統了。
{if $group=="admin"} <a href="javascript:delete_action({$action.action_id})" class="btn btn-danger btn-xs">刪除</a> <a href="admin.php?action_id={$action.action_id}" class="btn btn-warning btn-xs">修改</a> {elseif $group=="user"} <a href="index.php?op=signup&action_id={$action.action_id}" class="btn btn-primary btn-xs">我要報名</a> {/if} <a href="index.php?action_id={$action.action_id}" class="btn btn-success btn-xs">詳情</a>
當然,活動頁面 show_action.tpl 的下方也是要加的!
<div class="text-center"> {if $group=="admin"} <a href="javascript:delete_action({$action.action_id})" class="btn btn-danger ">刪除</a> <a href="admin.php?action_id={$action.action_id}" class="btn btn-warning">修改</a> {elseif $group=="user"} <a href="index.php?op=signup&action_id={$action.action_id}" class="btn btn-primary btn-lg">我要報名</a> {/if} </div>
CREATE TABLE `signups` ( `uid` smallint(5) unsigned NOT NULL COMMENT '使用者編號', `action_id` smallint(5) unsigned NOT NULL COMMENT '活動編號', `signup_date` datetime NOT NULL COMMENT '報名日期', PRIMARY KEY (`uid`,`action_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
基本上只有三個欄位!
所謂報名,就是在這個表中多一筆紀錄,看是誰(uid),在什麼時候(signup_date),報了哪個活動(action_id)
基本上,此表沒有所謂審核的功能,有需要請自己加(研習哪來那麼多時間...)
case "signup": signup($action_id); header("location:{$_SERVER['PHP_SELF']}?action_id=$action_id"); exit;
只要傳入編號即可,uid可以從 session抓,日期更是可直接由資料庫產生。
報名後,直接轉向該活動,我們希望可以在活動下方看到已經報名名單
//報名 function signup($action_id) { global $db; $uid = $_SESSION['uid']; $sql = "INSERT INTO `signups` ( `action_id`, `uid`, `signup_date`) VALUES ('{$action_id}', '{$uid}', NOW())"; if (!$db->query($sql)) { throw new Exception($db->error); } }
default: if ($action_id) { $op = 'show_action'; show_action($action_id); list_signup($action_id); } else { $op = 'list_action'; list_action(); } break;
//已報名名單 function list_signup($action_id) { global $db, $smarty; $sql = "SELECT * FROM `signups` where `action_id` = '{$action_id}'"; if (!$result = $db->query($sql)) { throw new Exception($db->error); } $signups = []; while ($signup = $result->fetch_assoc()) { $signups[] = $signup; } $smarty->assign('signups', $signups); }
{if $op=="show_action"} <h2>已報名名單</h2> <table class="table table-hover table-striped"> <thead> <tr class="info"> <th>姓名</th> </tr> </thead> <tbody> {foreach $signups as $signup} <tr> <td>{$signup.uid}</td> </tr> {/foreach} </tbody> </table> {/if}
我們在第一行判斷目前 op 狀態,只有在 show_action 時才顯示。
由於目前我們沒讀出姓名,暫時先顯示uid編號即可。
<h2>Hello {$name}!</h2> {if $group} {include file='side_tools.tpl'} {include file='side_signups.tpl'} {else} {include file='side_login.tpl'} {/if}
SELECT a.* , b.* , c.* FROM `資料表1` as a JOIN `資料表2` as b ON a.`索引欄位`= b.`索引欄位` JOIN `資料表3` as c ON b.`索引欄位`= c.`索引欄位` WHERE a.欄位='值' AND ….
SELECT a.*, b.* FROM `signups` AS a JOIN `users` as b ON a.`uid` = b.`uid` WHERE a.`action_id` = '{$action_id}'
{if $op=="show_action"} <h2>已報名名單</h2> <table class="table table-hover table-striped"> <thead> <tr class="info"> <th>姓名</th> <th>Email</th> </tr> </thead> <tbody> {foreach $signups as $signup} <tr> <td>{$signup.name}</td> <td>{$signup.email}</td> </tr> {/foreach} </tbody> </table> {/if}
//登入 function login() { global $db; $email = clean_var('email', 'Eamil', FILTER_VALIDATE_EMAIL); $pass = clean_var('pass', '密碼'); $sql = "SELECT * FROM `users` where `email`='{$email}'"; $result = $db->query($sql); if (!$result) { throw new Exception($db->error); } $data = $result->fetch_assoc(); if (password_verify($pass, $data['pass'])) { $_SESSION['group'] = $data['group']; $_SESSION['name'] = htmlspecialchars($data['name'], ENT_QUOTES); $_SESSION['uid'] = $data['uid']; $_SESSION['email'] = htmlspecialchars($data['email'], ENT_QUOTES); //抓取該使用者已報名的活動編號 $sql = "SELECT action_id FROM `signups` where `uid`='{$data['uid']}'"; if (!$result2 = $db->query($sql)) { throw new Exception($db->error); } $_SESSION['uid_signup'] = []; while (list($action_id) = $result2->fetch_row()) { $_SESSION['uid_signup'][] = $action_id; } } else { throw new Exception("登入失敗!"); } }
我們將活動編號都放入 $_SESSION['uid_signup'] 陣列中
另外,讀取資料庫的迴圈中若還有讀取資料庫迴圈,其 $result 記得改名,否則第一個迴圈的 $result 會被 第二個迴圈的 $result 給取代。
由於第二個迴圈只抓了一個欄位,故改用 $result2->fetch_row() 來抓,並利用 list() 做指派,把值放到 $action_id 中。
//登出 function logout() { unset($_SESSION['group']); unset($_SESSION['name']); unset($_SESSION['uid']); unset($_SESSION['email']); unset($_SESSION['uid_signup']); }
//報名 function signup($action_id) { global $db; $uid = $_SESSION['uid']; $sql = "INSERT INTO `signups` ( `action_id`, `uid`, `signup_date`) VALUES ('{$action_id}', '{$uid}', NOW())"; if (!$db->query($sql)) { throw new Exception($db->error); } $_SESSION['uid_signup'][] = $action_id; }
{if $group=="admin"} <a href="javascript:delete_action({$action.action_id})" class="btn btn-danger btn-xs">刪除</a> <a href="admin.php?action_id={$action.action_id}" class="btn btn-warning btn-xs">修改</a> {elseif $group=="user"} {if $action.action_id|in_array:$smarty.session.uid_signup} <a href="index.php?action_id={$action.action_id}" class="btn btn-danger btn-xs">取消報名</a> {else} <a href="index.php?op=signup&action_id={$action.action_id}" class="btn btn-primary btn-xs">我要報名</a> {/if} {/if}
我們利用smarty的變數修飾器,直接使用 php 的 in_array() 函數來判斷目前此活動編號有無在 $_SESSION['uid_signup'] 陣列中
<div class="text-center"> {if $group=="admin"} <a href="javascript:delete_action({$action.action_id})" class="btn btn-danger ">刪除</a> <a href="admin.php?action_id={$action.action_id}" class="btn btn-warning">修改</a> {elseif $group=="user"} {if $action.action_id|in_array:$smarty.session.uid_signup} <a href="index.php?action_id={$action.action_id}" class="btn btn-danger btn-lg">取消報名</a> {else} <a href="index.php?op=signup&action_id={$action.action_id}" class="btn btn-primary btn-lg">我要報名</a> {/if} {/if} </div>
<a href="javascript:delete_signup({$action.action_id})" class="btn btn-danger btn-xs">取消報名</a>
接著修改 templates\sweet-alert.tpl(或者要另外做一個新的也行),多加上一個新的函數
function delete_signup(id){ swal({ title: "確定要取消嗎?", text: "取消後就不能參加活動囉!", type: "warning", showCancelButton: true, confirmButtonColor: "#DD6B55", confirmButtonText: "是!含淚取消!", cancelButtonText: "不...還是繼續參加", closeOnConfirm: false }, function(){ swal("OK!取消惹!", "下次有機會再來啦!", "success"); location.href='index.php?op=delete_signup&action_id=' + id; }); }
重點在於14行,也就是實際會連結到 index.php,並且帶個活動編號過去,至於uid不用特別帶過去,因為session中可以抓得到。
case "delete_signup": delete_signup($action_id); header("location:{$_SERVER['PHP_SELF']}?action_id=$action_id"); exit;
接著實際做出該函數:
//取消報名 function delete_signup($action_id) { global $db; $sql = "DELETE FROM `signups` WHERE `action_id`='{$action_id}' and `uid`='{$_SESSION['uid']}'"; if (!$db->query($sql)) { throw new Exception($db->error); } $_SESSION['uid_signup'] = array_diff($_SESSION['uid_signup'], [$action_id]); }
//列出所有活動 function list_action() { global $db, $smarty; $where = (!isset($_SESSION['group']) or $_SESSION['group'] != "admin") ? 'where `end_date` > now() and `enable`=1' : ''; $sql = "SELECT * FROM `actions` $where order by action_date desc"; $result = $db->query($sql); if (!$result) { throw new Exception($db->error); } $actions = []; while ($values = $result->fetch_assoc()) { $actions[] = $values; } $smarty->assign('actions', $actions); }
第五行的條件中,我們設了兩個條件,一個是尚未登入者(只要判斷 $_SESSION['group'] 不存在即可),另外一個是已登入,但身份不是管理員者,只要符合其一,就加入截止時間必須大於現在時間(尚未截止之意),以及活動是開啟狀態的條件。
//列出所有活動 function list_action() { global $db, $smarty; $where = (!isset($_SESSION['group']) or $_SESSION['group'] != "admin") ? 'where `end_date` > now() and `enable`=1' : ''; $sql = "SELECT * FROM `actions` $where order by action_date desc"; include_once "class/PageBar.php"; $PageBar = getPageBar($db, $sql, 5, 10); $bar = $PageBar['bar']; $sql = $PageBar['sql']; $total = $PageBar['total']; $result = $db->query($sql); if (!$result) { throw new Exception($db->error); } $actions = []; while ($values = $result->fetch_assoc()) { $actions[] = $values; } $smarty->assign('actions', $actions); $smarty->assign('bar', $bar); $smarty->assign('total', $total); }
getPageBar($mysqli, $sql, $show_num = 20, $page_list = 10, $to_page = "", $url_other = "")
$mysqli:使用的資料庫物件名稱(必要)
$sql:欲執行的語法(必要)
$show_num = 20:每頁顯示資料數
$page_list = 10:分頁工具列呈現的頁數
$to_page = "":要連結到那一個頁面
$url_other = "":其他額外的連結參數
<h2>活動列表<small>(共 {$total} 個活動)</small></h2>
{$bar}
//已報名名單 function list_signup($action_id) { global $db, $smarty; $sql = "SELECT a.*, b.* FROM `signups` AS a JOIN `users` as b ON a.`uid` = b.`uid` WHERE a.`action_id` = '{$action_id}'"; if (!$result = $db->query($sql)) { throw new Exception($db->error); } $signups = []; while ($signup = $result->fetch_assoc()) { if (!isset($_SESSION['group']) or $_SESSION['group'] != 'admin') { //姓名中字取代 $len = strlen($signup['name']); if ($len > 6) { $target = substr($signup['name'], 3, 3); $signup['name'] = str_replace($target, '○', $signup['name']); } //email取代 preg_match("/(.+?)\@(.+?)\.(.+?)$/", $signup['email'], $em); $signup['email'] = "{$em[1]}@" . str_repeat("x", strlen($em[2])) . ".{$em[3]}"; } $signups[] = $signup; } $smarty->assign('signups', $signups); }
//刪除活動 function delete_action($action_id) { global $db; $sql = "DELETE FROM `actions` WHERE `action_id`='{$action_id}';"; if (!$db->query($sql)) { throw new Exception($db->error); } $sql = "DELETE FROM `signups` WHERE `action_id`='{$action_id}'"; if (!$db->query($sql)) { throw new Exception($db->error); } }
//寄信 function send_mail($to, $subject, $message, $from) { if ($to and $subject and $message) { $headers = "MIME-Version: 1.0\r\n" . "Content-type: text/html; charset=utf-8\r\n" . "From: {$from}\r\n"; mail($to, $subject, $message, $headers); } }
其中 $headers 就是告知收信軟體這封信是有支援網頁語法的檔頭。
最後,只要在報名完,順便寄個通知信即可:
//報名 function signup($action_id) { global $db; $uid = $_SESSION['uid']; $sql = "INSERT INTO `signups` ( `action_id`, `uid`, `signup_date`) VALUES ('{$action_id}', '{$uid}', NOW())"; if (!$db->query($sql)) { throw new Exception($db->error); } $sql = "SELECT * FROM `actions` where `action_id` ='{$action_id}'"; $result = $db->query($sql); if (!$result) { throw new Exception($db->error); } $action = $result->fetch_assoc(); $_SESSION['uid_signup'][] = $action_id; $admin_email = "tad@tn.edu.tw"; $message = "<div>{$_SESSION['name']} 您好:</div><p>您已成功報名「{$action['title']}」活動。</p>"; send_mail($_SESSION['email'], "「{$action['title']}」已報名通知", $message, $admin_email); send_mail($admin_email, "「{$action['title']}」已報名通知", $message, $admin_email); }
其中,我們用了 $_SESSION['email'],但實際上此變數不存在,所以,我們在login()登入時順便加個 $_SESSION['email']:
if (password_verify($pass, $data['pass'])) { $_SESSION['group'] = $data['group']; $_SESSION['name'] = $data['name']; $_SESSION['uid'] = $data['uid']; $_SESSION['email'] = $data['email']; //抓取該使用者已報名的活動編號 $sql = "SELECT action_id FROM `signups` where `uid`='{$data['uid']}'"; if (!$result2 = $db->query($sql)) { throw new Exception($db->error); } $_SESSION['uid_signup'] = []; while (list($action_id) = $result2->fetch_row()) { $_SESSION['uid_signup'][] = $action_id; } } else { throw new Exception("登入失敗!"); }
當然,登出時也記得取消之
//登出 function logout() { unset($_SESSION['group']); unset($_SESSION['name']); unset($_SESSION['uid']); unset($_SESSION['uid_signup']); unset($_SESSION['email']); }