再学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
再学ajax--第二天 | 基于php+mysql+ajax的表单注册、登录、注销的更多相关文章
- 第二十二章 Django会话与表单验证
第二十二章 Django会话与表单验证 第一课 模板回顾 1.基本操作 def func(req): return render(req,'index.html',{'val':[1,2,3...]} ...
- ajax+FormData+javascript 实现无刷新表单注册
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- ajax方式提交带文件上传的表单,上传后不跳转
ajax方式提交带文件上传的表单 一般的表单都是通过ajax方式提交,所以碰到带文件上传的表单就比较麻烦.基本原理就是在页面增加一个隐藏iframe,然后通过ajax提交除文件之外的表单数据,在表单数 ...
- 基于Bootstrap+jQuery.validate Form表单验证实践
基于Bootstrap jQuery.validate Form表单验证实践 项目结构 : github 上源码地址:https://github.com/starzou/front-end- ...
- 基于jQuery商品分类选择提交表单代码
分享一款基于jQuery商品分类选择提交表单代码.这是一款基于jQuery实现的商品信息选择列表表单提交代码. 在线预览 源码下载 实现的代码: <div class="yList ...
- Node.js基于Express框架搭建一个简单的注册登录Web功能
这个小应用使用到了node.js bootstrap express 以及数据库的操作 :使用mongoose对象模型来操作 mongodb 如果没了解过的可以先去基本了解一下相关概念~ 首先注 ...
- 基于JQuery的前端form表单操作
Jquery的前端表单操作: jquery提供了良好的方法封装,在一些基本的操作的时候,能节省很多的麻烦,其中,在具体使用时,form表单的数据提交是最频繁也最常见的前后数据交换方式,所以在前 ...
- 文档驱动 —— 表单组件(五):基于Ant Design Vue 的表单控件的demo,再也不需要写代码了。
源码 https://github.com/naturefwvue/nf-vue3-ant 特点 只需要更改meta,既可以切换表单 可以统一修改样式,统一升级,以最小的代价,应对UI的升级.切换,应 ...
- AJAX练习(一):制作可以自动校验的表单(从原理上分析ajax的作用)
继上文(AJAX(一)AJAX的简介和基础)作为联系. 传统网页在注册时检测用户名是否被占用,传统的校验显然缓慢笨拙. 当ajax出现后,这种体验有了很大的改观,因为在用户填写表单时,签名的表单项已经 ...
随机推荐
- 操作系统-I/O(4)I/O控制方式
I/O控制的方式分为: 程序直接控制方式(最简单的I/O方式) • 无条件传送:对简单外设定时(同步)进行数据传送 • ...
- 个人项目wc(C语言)
github地址:https://github.com/nilonger/mycangku 一.项目要求 1.wc.exe 是一个常见的工具,它能统计文本文件的字符数.单词数和行数.这个项目要求写一个 ...
- 微信小程序发送订阅消息(之前是模板消息)
之前的模板消息已经废弃,现在改为订阅消息,订阅消息发布前,需要用户确认后才能接收订阅消息. 小程序端 index.wxml <button bindtap="send"> ...
- 【翻译】.NET 5 Preview8发布
[翻译].NET 5 Preview8发布 今天,.NET 5预览8发布了,对于.NET5.0的功能开发已经完成了,这必须要排除待处理的bug,预览8是最后一次预览版本.预计11月正式的.NET5.0 ...
- Windows server 2008 搭建DNS服务
现在用Windows搭建DNS的已经很少了,感觉也只有一些公司的某块部分能用上,最近在捣鼓这个,索性直接写下来,以后可以看了直接用. 开始: ★★★配置静态IP地址 老样子,有关服务器功能的建立,都是 ...
- 程序员小哥教你秋招拿大厂offer
快要到秋招了,对于应届生来说,秋招是一个特别重要的机会.对于社招同学来说,金九银十也是一个很好的跳槽窗口. 而我呢,因为是从上海到广州工作,就没有提前先把工作定下来.刚好也趁这个机会出去旅游了两个月. ...
- 力扣Leetcode 1248. 统计「优美子数组」
统计「优美子数组」 给你一个整数数组 nums 和一个整数 k. 如果某个 连续 子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」. 请返回这个数组中「优美子数组」的数目. 示例 ...
- PCIe例程理解(一)用户逻辑模块(接收)仿真分析
前言 本文从例子程序细节上(语法层面)去理解PCIe对于事物层数据的接收及解析. 参考数据手册:PG054: 例子程序有Vivado生成: 为什么将这个内容写出来? 通过写博客,可以检验自己理解了这个 ...
- 万字长文,Python数据分析实战,使用Pandas进行数据分析
文章目录 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类人,我给大家 ...
- CRMEB小程序商城v4.0二次开发对接集成阿里云短信
作者:廖飞 - CRMEB小程序商城研发项目组长 前言 cremb小程序商城v4.0版本支持短信平台为云信,但有部分用户有需求对接阿里云短信,这篇文章将对阿里云短信平台如何对接方以及对接流程详细说明. ...