-
项目预览演示
一个自媒体信息发布平台
管理员(编辑)通过网站后台管理界面管理(发布、维护)自媒体内容
用户可以通过网站前台查看内容
准备工作
- 数据库设计
- 基础结构搭建
数据库设计
根据我们的业务需要设计数据库的结构,这个过程是每个项目开始时所必须的,一般由专门的 DBA 角色完成(很多没有划分的非常具体的公司由后端开发人员兼任)。
搭建项目架构
项目最基本的分为两个大块,前台(对大众开放)和后台(仅对管理员开放)。
第一步:搭建基本结构目录
└── baixiu ······································ 项目文件夹(网站根目录)
├── admin ··································· 后台文件夹
│ └── index.php ··························· 后台脚本文件
├── static ·································· 静态文件夹
│ ├── assets ······························ 资源文件夹
│ └── uploads ····························· 上传文件夹
└── index.php ······························· 前台脚本文件
基本原则:
- 先明确一共有多少个页面
- 一个页面就对应一个php文件去处理
第二步:整合静态资源文件
静态文件VS动态文件
- 静态文件指的就是服务器不会经过任何处理就返回客户端的文件,比如:图片、样式表、字体文件等
- 动态文件指的就是服务器会对请求的文件进行处理,并将处理后的结果返回给客户端的文件,比如:PHP文件、ASP文件等
具体操作
└── baixiu ······································ 项目文件夹(网站根目录) ├── ...... ├── static ·································· 静态文件夹 │ ├── assets ······························ 资源文件夹 + │ │ ├── css ····························· 样式文件夹 + │ │ ├── img ····························· 图片文件夹 + │ │ ├── js ······························ 脚本文件夹 + │ │ └── venders ························· 第三方资源 │ └── uploads ····························· 上传文件夹 + │ └── 2017 ···························· 2017 年上传文件目录 ├── ......
注意:
- static 目录中只允许出现静态文件
- assets目录中放置网页中所需的的资源文件
- uploads目录中放置网站运营过程中上传的文件,如果担心文件过多,可以按年归档(一年一个文件夹)
第三步:配置项目文件
由于在接下来的开发过程中,肯定有一部分公共的成员,例如数据库主机、数据库用户名、数据库密码等,这些数据应该放到公共的地方,抽象成一个配置文件config.php放到项目根目录下。
这个配置文件采用定义常量的方式定义配置成员:
<?php
/**
* 我们项目中用到的配置信息
*/
/**
* 数据库用主机
*/
define('DB_HOST', 'localhost');
/**
* 数据库用户名
*/
define('DB_USER', 'root');
/**
* 数据库密码
*/
define('DB_PASS', '123');
/**
* 数据库名字
*/
define('DB_NAME', 'baixiu');
注意:这种只有服务端的PHP文件应该去除结尾处的?>,防止输出内容
在需要配置文件的时间可以通过require载入:
<?php
//载入配置文件
require_once '../config.php';
项目架构总结
架构的目的就是搭建一个基本的架子或者说是制定一个基础的约束,让所有的开发人员基于这一个约束基础之上展开开发工作
有利于后期维护(不至于写一段时间过后大家都不认识这个项目,找一个文件,找一个功能找半天)
开始编码
对于网站功能开发人员来说,他们在展开工作之前就已经完成了静态页面的制作,接下来就是具体开发每一个业务功能。
下面有一种方式:
先把每一个静态页直接转成一个对应的PHP文件,调整页面中的资源路径,然后在此静态页面可以访问的基础之上实现各个功能
第四步:整合全部静态页面
1、将静态页面全部拷贝到admin目录中
2、将文件扩展名由.html改为.php,页面中的a链接也需要调整
3、调整页面中的静态资源路径,将原本的相对路径调整为绝对路径
绝对路径vs相对路径
- 不会跟随当前页面的访问地址的变化而变化
- 更简洁明了,不容易出错,不用一层一层的找
第五步:抽离公共部分
由于每一个页面中都有一部分代码(侧边栏)是相同的,分散到各个文件中,不容易维护,所有应该抽象到一个公共的文件中。
于是我们在admin目录中创建一个inc子目录,在这个目录中创建一个sidebar.php文件,用于抽象公共的侧边栏
<div class="aside"> ... </div>
,然后在每一个需要的模块的页面中通过include载入:
...
<?php include 'inc/sidebar.php' ;?>
...
第六步:侧边栏的焦点状态
由于侧边栏在不同页面时,active class name出现的位置不尽相同,所以我们需要区别当前sidebar.php文件是在哪个页面中载入的,从而明确焦点状态。
所以目前的关键问题就出现在了如何在 sidebar.php
中知道当前被哪个文件载入了。
通过查看 include
函数的文档发现:如果 a.php
通过 include
载入了 b.php
文件,那么在 b.php
文件中可以访问到 a.php
中定义的变量。
http://php.net/manual/zh/function.include.php
借助这个特性,我们可以在各个页面中定义一个标识变量,然后在 sidebar.php
中通过这个标识变量区别不同页面的载入:
每一个菜单项 <li>
元素:
...
<li <?php echo $current_page == 'comments' ? 'class="active"':''; ?>>
<a href="/admin/comments.php"><i class="fa fa-comments"></i>评论</a>
</li>
...
对于有子菜单的菜单项,有一点例外:
...
<?php $menu_posts = array('posts','post-add','categories'); ?>
<li <?php echo in_array($current_page, $menu_posts) ? 'class="active"':''; ?>>
<a href="#menu-posts" <?php echo in_array($current_page, $menu_posts) ? '':'class="collapsed"'; ?> data-toggle="collapse">
<i class="fa fa-thumb-tack"></i>文章<i class="fa fa-angle-right"></i>
</a>
<ul id="menu-posts" class="collapse<?php echo in_array($current_page, $menu_posts) ? ' in':''; ?>">
<li <?php echo $current_page == 'posts' ? 'class="active"':''; ?>><a href="/admin/posts.php">所有文章</a></li>
<li <?php echo $current_page == 'post-add' ? 'class="active"':''; ?>><a href="/admin/post-add.php">写文章</a></li>
<li <?php echo $current_page == 'categories' ? 'class="active"':''; ?>><a href="/admin/categories.php">分类目录</a></li>
</ul>
</li>
...
管理后台登录
第七步:处理 HTML 中需要调整的地方
在写静态页面时,我们一般不会关心功能实现过程中对 HTML 的要求,特别是表单一类的 HTML,在实际开发功能时我们一般都会使用到表单的 action
和 method
属性,还有表单元素的 name
属性等等。
这里我们需要调整的有:
- 给form表单添加action和method属性
- 给邮箱和密码添加name属性
- 将登陆按钮由a链接改为button提交按钮
登陆业务逻辑
用户第一次访问登录页面(GET),服务端会返回(响应)一个包含登录表单的 HTML 给浏览器。
当用户填写完表单点击登录按钮,浏览器会再发送一个 POST
请求到 login.php
文件
第八步:判断是否以POST方法请求
在 PHP 脚本开始执行时,判断当前请求是否是以 POST
方式提交:
<?php
// 判断当前是否是 POST 请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// TODO: 是 POST 提交
}
...
参数处理:如果是 POST 提交,则通过 $_POST
关联数组获取用户在表单中填写的邮箱和密码。
合法化校验:如果邮箱或者密码没有填写的话,应该提示用户完整填写邮箱和密码,并结束之后的逻辑(没必要执行了)。
//1.接收并校验
if (empty($_POST['email'])) {
$GLOBALS['message'] = '请输入邮箱';
return;
}
if (empty($_POST['password'])) {
$GLOBALS['message'] = '请输入密码';
return;
}
错误信息展示:如果执行逻辑过程中出现错误,界面上必须要有一个错误信息的展示,我们的方案就是在出错过后,定义一个错误信息的变量,最后在渲染 HTML 时输出到 HTML 中:
<!-- 有错误信息时展示 -->
<?php if (isset($message)): ?>
<div class="alert alert-danger">
<strong>错误!</strong> <?php echo $message ?>
</div>
<?php endif ?>
接收参数:
$email = $_POST['email'];
$password = $_POST['password'];
第九步:表单状态保持
当用户填写错误的邮箱或密码提交过后,再次得到 HTML 页面显示时,除了可以看见错误信息,应该也可以看到之前填写邮箱(非敏感数据)
解决办法也很简单:在输出 <input>
时为其添加一个 value
属性
<input id="email" name="email" type="email" class="form-control" placeholder="邮箱" autofocus value="<?php echo empty($_POST['email'])? '':$_POST['email'] ?>">
第十步:数据库连接查询
只有数据库才“知道”邮箱与密码是否正确,所以必须通过查询数据库验证邮箱和密码。
根据邮箱查询用户:根据邮箱到数据库中查询对应的用户,如果查询不到则代表用户不存在
$conn = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if (!$conn) {
exit('<h1>连接数据库失败</h1>');
}
$query = mysqli_query($conn,"select * from users where email = '{$email}' limit 1 ;");
if (!$query) {
$GLOBALS['message'] = '登录失败,请重试';
return;
}
//获取登录用户
$users = mysqli_fetch_assoc($query);
if (!$users) {
//用户名不存在
$GLOBALS['message'] = '密码与邮箱不匹配';
return;
}
用户密码比对:比对用户密码是否与表单提交过来的相同
if ($users['password'] !== $password) {
//密码错误
$GLOBALS['message'] = '密码与邮箱不匹配';
return;
}
第十一步:访问控制及登陆状态保持
至此,我们已经完成了登录校验的功能,但是如果我们记住登陆后(约定如此)才可以访问的页面地址,直接在地址栏输入,则可以越过登陆页面的限制直接访问。这样的话登录页形同虚设,没有任何价值。
解决方法:在需要访问权控制页面加上访问控制(硬性控制),具体方法就是根据用户之前的登录状态来决定是否可以访问,如果没有登录就不让其访问。
Session 这种机制会更加适合于做登录状态保持,因为客户端不再保存具体的数据,只是保存一把“钥匙”,伪造一把可以用的钥匙,可能性是极低的,所以不需要在意。
session_start();
if (empty($_SESSION['login_user'])) {
//如果没有当前用户信息,意味着没有登录过
header('Location:/admin/login.php');
}