iT邦幫忙

DAY 20
5

網站系統規劃實務系列 第 20

網站系統規劃 - 發文系統實作(註冊篇)

本篇文章我們接續昨天的內容,繼續討論註冊實作的細節。

--------系列簡介--------

網站系統可說是現在最多學子與新人想要入門的一個領域,
這個原本屬於新興的領域,這幾年來也累積許多年的知識與 pattern ,
在有限的環境(stateless)與有限的伺服器端、瀏覽器資源下,
成為許多人進入程式的一塊入門鐵板(?)。

這個系列希望能夠就網站系統設計幾個門檻著手,
這是設定給初心者作為學習,給同好們作為回顧,

重新認識有關網站系統的每個環節。
昨天的文章裡面,我們聊到如何進行註冊,
並且示範將一筆資料寫進資料庫的部份。

但是其實還有很多細節我們還沒有說完,
我們今天將接著說明細節,並繼續實作剩下的部份。


首先是重新回顧昨天的文章裡面進行註冊的每個細節,
我們註冊的流程是:

* 填寫表單
* 送出至 user/register
* 在 user/register 進行註冊處理,語法如下:

applicaiton/controller/user.php

public function registering(){
	$account = $this->input->post("account");
	$password= $this->input->post("password");
	$passwordrt= $this->input->post("passwordrt");
	
	$this->load->model("UserModel");
	$this->UserModel->insert($account,$password); //完成新增動作
}

這裡我們透過這行語法先載入 UserModel :
$this->load->model("UserModel");

另外接著我們呼叫被載入的 UserModel 進行操作:
$this->UserModel->insert($account,$password);

如果沒有先載入 UserModel 的話,
$this->UserModel 是不會存在的喔,這點要特別注意。

* 接著我們進行 UserModel 裡面的 insert 的實作:

application/models/usermodel.php

    function insert($account,$password){
        $this->db->insert("user", 
            Array(
            "account" =>  $account,
            "password" => $password
        ));     
    }   

這裡我們定義這個 Model 具有 insert 這個函式,
其中有 $account / $password 這兩個參數,
而我們會負責將這兩個資料插入 user 資料表。

然後在這裡我們需要特別解釋,
在 model 裡 $this->db->insert() 這個操作。

$this->db 是 CI 幫 Model 內建的變數,負責專門處理資料庫操作,
在 CI 裡面他稱之為 Active Record 。 CI 參考文件

insert 這個操作的樣板是
$this->db->insert("資料表名稱",欄位與資料)

欄位與資料的部份可以透過指明 key-value 的 Array,
進行資料欄位的新增,以上面的程式碼為例:

$this->db->insert("user", 
    Array(
    "account" =>  $account,
    "password" => $password
));  

指的就是新增一筆資料到 user 資料表,
其中 account 欄位是 $account 變數的資料,
password 欄位是 $password 變數的資料。

換成我們之前熟悉的 sql 語法,大概可以是
"insert into users(account,password) values('".$account."','".
$password."')";

@ 難道我們不能自己下 SQL 嗎?

可以,你可以透過 $this->db->query($sql); ,
來下 SQL 指令,只是我們不建議!

主要的理由是因為,如果你採取上述的拼湊字串的方式,
可能會給使用者有機會使用惡意字元進行 SQL 注入攻擊,
(SQL Injection )

不管什麼語言都好,用外部的變數,
來串接 SQL 都是一個風險很高的作法。

CI 的 Active Record 透過 insert 這樣的指令,
讓使用者能確實指定他希望用哪些欄位輸入哪些資料,
而 Active Record 就會確保當使用者輸入惡意字元時,
能夠正確被進行 escape 處理,而不會造成安全性問題。

另一方面,這樣也比組裝 SQL 更容易被使用者使用。

@ 那我為什麼還要學 SQL ?

因為這些資料最後還是回歸 SQL ,你必須理解工具幫你做了什麼事情,
這樣在批次更新或者自己查詢資料時,才能夠好好掌握方法與工具。

還有就是查詢時,大多還是需要自己下查詢語法。


好的,基本的介紹應該大致上已經說完了。


接下來我們要繼續進行昨天沒處理好的部份,分成是以下幾個部份:

1.如果密碼或帳號是空白,應該告知使用者密碼或帳號不得為空白。
如果 retype password 與 password 欄位資料不一致,
應該告知使用者確認密碼並不正確。

告知時應顯示表單,請使用者繼續填寫。

2.新增資料時如果這個帳號已經存在,應該視為錯誤,
告知使用者此帳號已存在,並請使用者重新選擇帳號。

3.如果新增正常時,應該告訴使用者他已註冊成功,並提示到登入頁面。


接下來我們一個一個實作

1.如果 retype password 與 password 欄位資料不一致,
應該繼續顯示表單,並且告知 password 不一致,請使用者繼續填寫。

	public function registering(){
		$account = $this->input->post("account");
		$password= $this->input->post("password");
		$passwordrt= $this->input->post("passwordrt");
		
		if( trim($password) =="" || trim($account) =="" ){
			$this->load->view('register',Array(
				"errorMessage" => "Account or Password shouldn't be empty,please check!" ,
				"account" => $account
			));
			return false;
		}

		if( $password != $passwordrt ){
			//如果不一致,我們讀取 register view,
			//但將 $account 跟錯誤訊息帶入作為處理
			$this->load->view('register',Array(
				"errorMessage" => "Password doesn't match re-type password,please check yout input!" ,
				"account" => $account
			));
			return false;
		}

		$this->load->model("UserModel");
		$this->UserModel->insert(trim($account),trim($password)); //完成新增動作
	}

在這裡我們進行一些判斷,如果有問題,就重新顯示 view ,
trim 的意思是去掉首尾空白,避免這些空白造成的誤判。

一般而言對帳號或密碼資料,首尾的空白是不會列入作為內容的。

這裡因為我們要重新產生 view 並且要有錯誤訊息,
所以需要透過變數告訴 view 要產生什麼訊息,
所以我們一定是透過一個指明 key-value 的 Array 進行變數傳遞。

如果我們不自己寫 html 的話,畫面是不會變的,
所以接著我們要修改 view 作為對應顯示:

application/views/register.php
完整修改後的內容請參考此一連結 https://gist.github.com/3884988

其中重要的部份在於

<?php  if (isset($errorMessage)){?>
	<div class="alert alert-error">
		<?=$errorMessage?>
	</div>
<?php }?>

如果有錯誤訊息的話,就顯示錯誤訊息,
alert-error 是 bootstrap 用來顯示錯誤訊息的標籤。

另外就是將使用者剛剛輸入的 account 保存下來,避免使用者重新輸入欄位。

<?php if(isset($account)){ ?>
	<input type="text" name="account" 
	    value="<?=htmlspecialchars($account)?>" />
<?php }else{ ?>
	<input type="text" name="account" />
<?php } ?>

在做表單時,這些細節是需要考慮到的。

@ htmlspecialchars 這是什麼?

因為 $account 是使用者輸入的資訊,
而且他是被放在 html tag 裡面的 value tag 裡,
所以需要特別為 $account 的內容進行 html 字元過濾。

否則如果使用者輸入的資料裡含有雙引號(")或之類的字,
就可能輕則會造成畫面顯示的錯誤,
重則導致一般使用者資料被惡意使用者竊走。

當然這裡大家可能會認為帳號這種資料不太會出現這種問題,
因為使用者不太可能對帳號輸入惡意資訊,
但做服務並不能假設使用者都是善良使用者。

使用者輸入惡意 html 字元這是常見的安全性問題,稱之為 Html Injection ,
屬於常見的 XSS (Cross Site Scripting) 攻擊的一種分支 。

對於使用者輸入的資訊如果不隨時提高警覺的話,後患無窮。

當我們實作完這兩個 view 之後,將可以看到類似以下的結果:


2.新增資料時如果這個帳號已經存在,應該視為錯誤,
告知使用者此帳號已存在,並請使用者重新選擇帳號。

要檢查帳號是否存在需要透過資料庫查詢,
所以我們將在 model 新增方法,作為檢查帳號是否存在。

檢查的方式很簡單,將使用者輸入的帳號丟入資料庫查詢,
看看是否有同樣名字的資料即可:

如以下操作 (完整程式碼請參考:https://gist.github.com/3885055

application/models/usermodel.php

function checkUserExist($account){
    $this->db->select("COUNT(*) AS users");
    $this->db->from("user");
    $this->db->where("account", $account);
    $query = $this->db->get(); 

    return $query->row()->users > 0 ;
}

這段程式碼對應到的 SQL 其實就近似是
"select count(*) from user where account = '".$account."';"

有沒有覺得很好懂呢?

$this->db->select 決定選擇欄位
$this->db->from 決定存取資料表
$this->db->where 決定存取條件
$this->db->get() 負責執行查詢,並將查詢結果回傳。

$query->row() 則是將查詢資料的第一筆取出,
在這裡我們透過第一筆資料的 users 資料判斷他是不是存在。

@ count(*) ?

這是一個我們還沒有介紹過的複合函式,
主要用來算出滿足這個條件的使用者到底有多少人。

假設 user table 有五個人,
"select count(*) as users from user "

得到的結果會是一行的:

users,5

接下我們將這個函式整合回 user controller 裡面:

修改 application/controller/user.php,

$this->load->model("UserModel");
if($this->UserModel->checkUserExist(trim($account))){ //檢查帳號是否重複
	$this->load->view('register',Array(
		"errorMessage" => "This account is already in used." ,
		"account" => $account
	));
	return false;
}

$this->UserModel->insert(trim($account),trim($password));  //完成新增動作

(完整修改後結果:https://gist.github.com/3885073

當我們試著建立重複資料時,就會看到以下錯誤訊息:

接下來我們已經處理完幾個常見的錯誤判斷,
也讓大家對透過 CI 對資料庫的操作又多瞭解一次,

接著我們就要進行資料完成的 View 製作,
首先在 user/registering 最後加上完成的 view:

$this->load->view('register_success',Array(
		"account" => $account
));	

接著建立出完成的 view file,到 application/views/ ,
建立 register_success.php:

<html >

	<meta charset="utf-8">
	<title>發文系統 - 會員註冊成功</title>
    <link rel="stylesheet" href="<?=base_url("/css/bootstrap.min.css")?>">
    <link rel="stylesheet" href="<?=base_url("/css/bootstrap-responsive.min.css")?>">



<div class="container">
	<div class="alert alert-success">
		恭喜你 (<?=$account?>),你已經完成註冊,接下來馬上到登入頁面去試試看吧!
		<a href="<?=site_url("user/login")?>">登入</a>
	</div>
</div>

結果如下圖:

今天介紹的其實比較細,但是為了讓大家一一瞭解網站的細節,
並且考慮到這是我們首次進入程式與資料庫合作的部份,

我們刻意放慢腳步告訴大家這其中的細節,
之後在實作發文系統時,就不會有這麼多著墨。

那麼,明天見囉。;)


上一篇
網站系統規劃 - 畫面、功能、資料表
下一篇
網站系統規劃 - 很多重複的程式碼?談網頁內容重用、模組化。
系列文
網站系統規劃實務27
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
鐵殼心
iT邦高手 1 級 ‧ 2012-10-14 00:01:23


坐好慢慢看

0
tony1223
iT邦新手 2 級 ‧ 2012-10-14 00:03:04

另外有關 retype password 跟一些資料欄位檢查,
其實一般開發時比較常見是先用 JavaScript 進行,

但是這樣會增加系統的複雜度,且回到伺服器端一樣是必須要檢查的,

所以這裡我們先以伺服器端檢查為主,等我們核心系統實作完,
接著我們會回頭聊前端畫面的優化。:)

這裡大家可能會覺得網站的畫面很陽春,看起來不像網站,
但是就網站的開發過程中,這是很正常的事情。

一般筆者的開發經驗,我們通常是會先參考 wireframe 完成功能,
再回頭進行頁面細節的調整。

我要留言

立即登入留言