zoukankan      html  css  js  c++  java
  • 再学ajax--第二天 | 基于php+mysql+ajax的表单注册、登录、注销

    写在前面

      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面向对象,一对比果然印象深刻,这次前后端的锻炼,让我收获颇丰,自己继续会撸起袖子加油干。

      好了,晚安,期待下一次发贴。

      已经把源码放在我的github里面了,有需要可以去下载,如果喜欢帮忙点一个star

           https://github.com/dirkhe1051931999/writeBlog/tree/master/php-mysql-ajax-js-login-reg

  • 相关阅读:
    JavaScript函数节流与函数去抖
    AngularJS 中文资料+工具+库+Demo 大搜集
    一个意想不到的Javascript内存泄漏
    mac添加Chromedriver
    selenium3+python自动化1——input标签上传文件
    python笔记1——xml文件的创建,读写,与增删改查
    C# 单例模式
    java的byte数组转换成在[0,255]范围内
    【转载】Stack Overflow: The Architecture
    C# 号码归属地查询算法(根据Android来电归属地二进制文件查询修改)
  • 原文地址:https://www.cnblogs.com/dirkhe/p/6974243.html
Copyright © 2011-2022 走看看