1. 程式人生 > >再學ajax--第二天 | 基於php+mysql+ajax的表單註冊、登錄、註銷

再學ajax--第二天 | 基於php+mysql+ajax的表單註冊、登錄、註銷

常量 insert 寫在前面 break 證明 收獲 localhost 技能 tex

寫在前面

  ajax學習到了第二天,這次是用第一天封裝的ajax函數,後端使用了php+mysql實現基本的註冊,登錄,註銷。

  php是我前幾個月get到的技能,我已經學習到了面向對象,知道各修飾符的含義,繼承,接口,構造函數,實例化對象

  mysql是跟php一塊學的,學習了基本增刪改查。

  ajax原理其實不難理解,最主要的就是XMLHttpRequest(ActiveXObject("Microsoft.XMLHTTP"));在理解該對象之後,最主要理解的是前後端數據的傳遞問題,我也是正在學習其中的樂趣。

HTML、CSS

  因為這次學習,主要學習前後端數據的傳遞,所以就不貼出HTML、CSS的代碼了,下面是簡單示意為主的圖

   技術分享技術分享

 示意為主的註冊與登錄頁面

  技術分享技術分享

註冊不成功頁面

  當輸入帳號輸入欄失去焦點(onblur)時,ajax傳入get參數,執行check方法,檢測mysql是否有有相同username的用戶,有則無刷新提示且提交按鈕的disabled為true,無則可以繼續註冊

技術分享

正常註冊

技術分享

技術分享

登錄成功

  登錄成功會設置一個1min的cookie,值為帳號名,js檢測cookie不存在,也就是undefined,則隱藏註銷欄,當存在,則顯示註銷欄,有退出選項,點擊退出可觸發,註銷函數,去清除cookie,通過把失效日期設置為過去的日期/時間,刪除一個 cookie setcookie(‘uid‘, "", time() - 60, ‘/‘);,其中uid是mysql做表的時候,auto_increment的編號。用這個代表當前用戶。

AJAX

  ajax還是第一天的封裝好的ajax函數

//ajax函數
function ajax(url,method,data,success){
    var xhr = null;
    try{
        xhr = new XMLHttpRequest();
    }catch(e){
        xhr = new ActiveXObject("Microsoft.XMHTTP");
    }
    
        url+="?"+data;
    
    xhr.open(method,url,true);
    xhr.onreadystatechange
=function(){ if(xhr.readyState===4){ if(xhr.status===200){ success && success(xhr.responseText); }else{ alert(xhr.status); } } } xhr.send() }

  ajax(url,method,data,success),一共4個形參,success為一個回調函數,主要作用是將後臺的數據傳到前臺了,這個回調函數很關鍵。

後端 

 後端是單入口文件,單入口文件的好處之一是絕對路徑的設置是參考該入口文件的,避免因為路徑而踩入不必要的坑

  index.php

  1:$config 一個存放數據庫host,port,username,password,database的數組

        //數據層
        $config=array(
            ‘db_host‘        =>    ‘localhost‘,
            ‘db_port‘        =>    ‘3306‘,
            ‘db_user‘        =>    ‘root‘,
            ‘db_password‘    =>    ‘123‘,
            ‘db_name‘        =>    ‘talklist‘,
        );

  2:定義一個獲取get/post請求參數值的常量

        //控制層
        define("module_action",$_REQUEST["a"]);

  3:mysql的控制層,有兩個類,一個是引入數據庫連接庫的類,另一個是給模型層的傳入send方法,send方法是為了把執行狀態(code,message)傳給前臺,send每次執行完,都要echo 模型層給send傳的參數,是為了獲取responseText 最後要exit(),退出當前腳本;

        // mysql庫的控制層
        class DB{
            public static function factory(){
                global $config;
                //mysql庫
                require_once("./libs/Class/DB_Mysql.class.php");
                return DB_Mysql::instance($config);
            }
        }

   上述代碼中DB_Mysql.class.php是mysql庫包含DB_Mysql類,DB_Mysql類包含instance方法,instance方法檢測當前類是否實例,如果沒有實例,就實例當前類並儲存起來,且傳入$config,如果有實例,就直接return 當前實例對象,實際上實例就是調用當前類的構造函數。保存$config,且執行DB_Mysql類的數據庫連接方法connect(),因為是構造函數,所以實例化對象時就會執行構造函數。

class DB_Mysql {

    private static $instanceObj;
    private $config ; //盛放的是數據庫連接的信息,host port username password databases
    
    private function __construct($config) {
        $this->config = $config;  
        $this->connect();
    }

    public static function instance($config) {
        if (!self::$instanceObj) {
            self::$instanceObj = new DB_Mysql($config);
        }
        return self::$instanceObj;
    }
    //連接數據庫
    public function connect() {
        mysql_connect($this->config[‘db_host‘],$this->config[‘db_user‘],$this->config[‘db_password‘]);
        mysql_select_db($this->config[‘db_name‘]);
        $this->query("set names ‘utf8‘");
    }
}

  所以我是覺得這段代碼是最有趣的。不知道大家是怎樣想的。

  4:Controller類,是為了為模型層的子級繼承父級的Controller類下的send方法,發送數據到前臺。

        class Controller{
             public $db = null;
             private $ajaxData=array(
                 "code"=>0,
                 "message"=>"",
             );
             public function __construct(){
                 $this->db = DB::factory();
             }
             public function send($data=array()){
                 $showdata = array_merge($this->ajaxData,$data);
                 echo json_encode($showdata);//轉成json responseText
                 exit();    //輸出一個消息並且退出當前腳本
             }
        }

  5:加載模型層方法,require_once("./Controller/IndexController.class.php"),get到的參數就是模型層IndexController類的方法

        //MVC中的模型層
        require_once("./Controller/IndexController.class.php");
        //把第一個參數作為回調函數調用,其余參數是回調函數的參數。
        call_user_func(array(new IndexController,module_action));

  6:在介紹模型層之前,先介紹完數據庫文件DB_Mysql.class.php剩下的方法

class DB_Mysql{
    //執行sql語句
    public function query($sql) {
        return mysql_query($sql);
    }

    public function select($sql) {
        $query = $this->query($sql);
        $rs = array();
        //將查詢的結果以數字1的索引方式存在數組裏面
        $queryArr = mysql_fetch_array($query, 1);
         if($queryArr) {
            $rs[] = $queryArr;
         }
        return isset($rs[0])?$rs[0]:false;
    }
}

  query方法就不用多講了,就是執行sql語句 mysql_query(),主要是說了select方法,把select方法單獨挑出來就是為了單獨執行select sql語句 select * from ...等

  單獨執行是為了將select語句曬出來的數據fetch到一個數組裏面,mysql_fetch_array($query, 1),將查詢的結果以數字1的索引方式存在數組裏面,最最最關鍵的是要

  判斷數據庫查到數據了沒,查到返回當前查到的數據,沒查到就返回一個bool值,為得就是在模型層判斷是否查到,return一個狀態(code,message),方便前臺獲取

  介紹完這個,就就能很輕松的理解模型層的方法了。

  7:模型層IndexController類extends Controller類,並且定義了自己的一些方法和屬性

  

class IndexController extends Controller {
    /**
     * @ 用戶名驗證 傳返回值。
     * return 0: 表示在數據庫沒有查到有相同用戶名
     * return 1: 用戶名的長度和類型不合法
     * return 2: 表示在數據庫查到了相同用戶名
     * $rs存在:  表示表示用查到了相同的用戶名,return 2;
     */
    private function _verifyUserName($username) {
        if (strlen($username) < 3 || strlen($username) > 10) {return 1;}
        //查數據庫裏面的數據
        $rs = $this->db->select("SELECT `username` FROM `users` WHERE `username`=‘{$username}‘ LIMIT 1");
        if ($rs) {return 2;}else{return 0;}
        
    }
    /**
     * @ interface 用戶名驗證return
     * 前臺傳來的get參數,選擇執行IndexController下來action
     */
    public function verifyUserName() {
        
        $username = $_REQUEST[‘username‘];
        $code  = $this->_verifyUserName($username);
        switch ($code) {
            case 0:
                $this->send(array(‘code‘=>0,‘message‘=>‘恭喜你,該用戶名可以註冊!‘));
                break;
            case 1:
                $this->send(array(‘code‘=>1,‘message‘=>‘用戶名長度不能小於4個或大於10個字符!‘));
                break;
            case 2:
                $this->send(array(‘code‘=>2,‘message‘=>‘對不起,該用戶名已經被註冊了!‘));
                break;
            default:
                break;
        }
        
    }
}

  在_verifyUserName中先要判斷長度是否合適,再判斷數據庫是否有相同的username,記住要limit 1 ;

  $this->db->select("SELECT `username` FROM `users` WHERE `username`=‘{$username}‘ LIMIT 1");

  這句話也很有意思,表單看來是在當前類的db變量下的select方法,別忘了IndexController extends Controller,在當前類找不到db屬性,那就是它爸爸那找麽,他爸爸身上也是也是沒有的

        class Controller{
             public $db = null;
             public function __construct(){
                 $this->db = DB::factory();
             }
          }         

  所以有趣DB類的factory找,DB::factory()說,我也沒有,我給你 return DB_Mysql::instance($config);,那你去DB_Mysql類中去找把,找啊找,終於在DB_Mysql類中找到了select的方法,由此看來找個這個select方法不容易啊,分析一下,我們從模型層中找到了控制層,控制層又去在數據庫的控制層找,這樣做是為了啥,為的就是模塊化管理,數據庫的方法就方法數據庫類中,互補幹擾,修改起來也很容易,這是MVC的魅力,前端MVC大致也如此吧。

  好的,那就下一個方法verifyUserName,這個方法主要就是為了send方法,send狀態(code,message),讓前臺獲取。用到了流程語句switch case

  好的,那驗證就結束了,下來就是註冊了,註冊就是insert into

class IndexController extends Controller{
public function reg() {
        $username = $_REQUEST[‘username‘];
        $password = $_REQUEST[‘password‘];

      $code = $this->_verifyUserName($username);
      //if($code ==0){$this->sendByAjax(array(‘code‘=>1,‘message‘=>""))}
      if ($code !== 0 || strlen($password)<3 || strlen($password) > 15) {
        $this->send(array(‘code‘=>1,‘message‘=>‘註冊失敗!‘));
      }

       //密碼加密,插入數據庫裏面
        $password = md5($password);
        if (false === $this->db->query("insert into users (username, password) values (‘{$username}‘, ‘{$password}‘)")) {
            $this->send(array(‘code‘=>1,‘message‘=>‘註冊失敗!‘));
        }else {
            $this->send(array(‘message‘=>‘註冊成功!‘));
        }
    }
}
    

  插入帳號,插入密碼,執行的是query方法,insert錯誤,就註冊是吧,否則註冊成功,記著要講密碼md5加密呢。也很好理解

  再之後就是登錄方法,註冊不僅要check帳號密碼是否匹配,更重要是設置cookie,就是為以後的註銷做打算

class IndexController extend Controller{

/**
     * @ 用戶登陸
     * $username 是帳號
     * $password 是密碼
     * $rs 在數據庫中選出所有用戶名等於$username的所有信息,mysql_fetch_array($sql,1);放在$rs數組裏面
     * setcookie(cookiename,cookie的值,cookie的有效期,cookie的服務器路徑)
     */
    public function login() {
        $username = $_REQUEST[‘username‘];
        $password = $_REQUEST[‘password‘];
        //檢測cookie中有沒有uid,有則證明已經登錄過了。
        if (isset($_COOKIE[‘uid‘])) {
            $this->send(array(‘code‘=>1,‘message‘=>‘你已經登陸過了!‘));
        }
        $rs= $this->db->select("select * from users where username=‘{$username}‘ limit 1");
        if ($rs) {
            if ($rs[‘password‘] != md5($password)) {
                $this->send(array(‘code‘=>1,‘message‘=>‘密碼與帳號不匹配‘));
            } else {
                //1分鐘過期
                setcookie(‘uid‘, $rs[‘uid‘], time() + 60, ‘/‘);
                setcookie(‘username‘, $rs[‘username‘], time() + 60, ‘/‘);
                $this->send(array(‘code‘=>0,‘message‘=>‘登陸成功!cookie有效時間為1min‘));
            }
        } else {
            $this->send(array(‘code‘=>1,‘message‘=>‘數據庫未檢測到您的信息‘));
        }
    }
}
    

  首先在登錄的時候,有用戶已經登錄,就不能繼續登錄,這句話得先判斷,isset($_COOKIE[‘uid‘]這句話很重要,如何檢測是否有用戶登錄呢,你select * from users

  把select到的內容都放入一個數組裏面,之前也說了mysql_fetch_array()這個方法了,這是$rs放的就不只有username了,還有password和auto_increment的uid,這就方便了check,首先在數據庫的username是否和輸入的username一致的情況下再判斷password是否一致,如果password一致,那就setcookie了

                setcookie(‘uid‘, $rs[‘uid‘], time() + 60, ‘/‘);
                setcookie(‘username‘, $rs[‘username‘], time() + 60, ‘/‘);

  前臺檢測cookie是否存在,存在就顯示註銷欄,不存在就不現實註銷欄,最後那就是註銷了,之前註銷也說了,就是清除cookie

class Controller extends Controller{
  /**
     * @ 用戶退出
     * 通過把失效日期設置為過去的日期/時間,刪除一個 cookie
     * uid不存在的話,則證明就沒有登錄
     */
    public function logout() {
        if (!isset($_COOKIE[‘uid‘])) {
            $this->send(array(‘code‘=>1,‘message‘=>‘你還沒有登陸!‘));
        } else {
            //通過把失效日期設置為過去的日期/時間,刪除一個 cookie:
            setcookie(‘uid‘, "", time() - 60, ‘/‘);
            $this->send(array(‘code‘=>0,‘message‘=>‘退出成功!‘));
        }
    }    
}
    

  setcookie(‘uid‘, "", time() - 60, ‘/‘);這句話狠抓那個要,通過把失效日期設置為過去的日期/時間,刪除一個 cookie:

        if (!isset($_COOKIE[‘uid‘])) {
            $this->send(array(‘code‘=>1,‘message‘=>‘你還沒有登陸!‘));
        } 
  這句話可有可無,因為你沒有uid的時候,註銷欄都隱藏了,所以何談點擊,何談get請求呢,聰明的你肯定想到了。

   說了這麽多,還沒有說JS大法呢。

JS

  理解了後臺,再去做前臺就會很容易了。getelements我就不寫了,就要寫函數

  檢測帳號

    //校驗帳號
    username1.onblur=function(){
        ajax("guestbook/index.php","get","m=index&a=verifyUserName&username="+this.value,function(data){
            var jsondata = JSON.parse(data)
            verifyUserNameMsg.innerHTML=jsondata.message;
            console.log(JSON.parse(data));
            if(jsondata.code==1 || jsondata.code==2){
                verifyUserNameMsg.style.color="red";
                btnReg.disabled=true;
            }else{
                verifyUserNameMsg.style.color="green";
                btnReg.disabled=false;
            }
        })
    }

  m=index&a=verifyUserName&username="+this.value,這是你get的參數

  回調函數有參數data,data就是responseText,就是狀態(code,message),就是send的的echo值

  code=1 代表格式不對 code=2 代表重名了 code=0代表ok

  註冊與登錄

    //註冊帳號
     btnReg.onclick=function(){
          ajax("guestbook/index.php","get","m=index&a=reg&username="+username1.value+"&password="+password1.value,function(data){
              alert("註冊成功!跳轉頁面中...");
              location.reload();
          })
     }
     //登錄帳號
     btnLogin.onclick=function(){
         ajax("guestbook/index.php","get","m=index&a=login&username="+username2.value+"&password="+password2.value,function(data){
             console.log(data);
             var jsondata = JSON.parse(data);
             if(jsondata.code===1){
                 alert(jsondata.message);
                 
             }else{
                 alert(jsondata.message);
                 user.style.display="block";
                 location.reload();
                 //userinfo.innerHTML=cookiename;
             }
         })
     }

   m=index&a=reg&username="+username1.value+"&password="+password1.value 註冊get參數

    m=index&a=login&username="+username2.value+"&password="+password2.value 登錄get參數

   code等於1代表未檢測到您的信息

   退出

      //退出      
     logout.onclick=function(){
         console.log(123);
         ajax("guestbook/index.php","get","m=index&a=logout",function(data){
             var jsondata = JSON.parse(data)
             console.log(data);
             if(jsondata.code === 0){
                 alert("退出成功!");
                 location.reload();
             }else{

             }
         })
     }

    退出的get參數 m=index&a=logout

    code=0退出成功

   接下來就是如何前端獲取cookie了

//前端獲取cookie
function getCookie(cookiename){
             var strCookie = document.cookie;
             var arrCookie = strCookie.split(";");
              for(var i = 1;i<arrCookie.length;i++){
                  
                  var arr = arrCookie[i].split("=");
                  
                     if(arr[0]===cookiename){
                             return arr[1];
                     }
                     
              }
}
         var cookiename = getCookie(" username");
         console.log(cookiename);

    前端如何判斷cookie是否存在了

    //登錄成功後顯示 用戶名退出欄
     if(cookiename===undefined){
         // userinfo.innerHTML="";
         user.style.display="none";
     }else{
         userinfo.innerHTML=cookiename;
     }

    cookiename就是username

    但是聰明的你又發現了,過多的get請求會導致緩存嚴重,尤其在chrome下,緩存嚴重必須要Ctrl+F5了,

    而且dom操作過多,導致了頁面性能的降低

sql

create database talklist
 create table `users` (
  `uid` int(11) unsigned primary key auto_increment,
  `username` char(16) 
  `password` char(32) 
  key `username` (`username`)
) engine=myisam default charset=utf8;  

收獲

  通過這兩次ajax的復習,對ajax的原理和使用有了深刻的認識,前後端交數據交互,ajax在其中發揮了巨大作用,PHP面向對象與JS面向對象的區別我也有了新的理解,上午還看了一個帖子,講JS面向對象,一對比果然印象深刻,這次前後端的鍛煉,讓我收獲頗豐,自己繼續會擼起袖子加油幹。

  好了,晚安,期待下一次發貼。

  

再學ajax--第二天 | 基於php+mysql+ajax的表單註冊、登錄、註銷