阿里百秀后台管理项目笔记 ---- Day03
来吧展示:
step1:所有文章数据展示
- 引入 functions.php 文件执行数据库查询以及判断用户登录状态
require_once '../functions.php';
get_userinfo();
- 查询文章数据
$posts = mysql_all('select * from posts;');
- 文章数据渲染
在表格中将多余的 tr 标记移除,通过 foreach 遍历查询到的 $posts 变量,绑定数据
<tbody>
<?php foreach ($posts as $item): ?>
<tr>
<td class="text-center"><input type="checkbox"></td>
<td><?php echo $item['title']; ?></td>
<td><?php echo $item['user_id']?></td>
<td><?php echo $item['category_id']?></td>
<td class="text-center"><?php echo $item['created']?></td>
<td class="text-center"><?php echo $item['status']?></td>
<td class="text-center">
<a href="javascript:;" class="btn btn-default btn-xs">编辑</a>
<a href="javascript:;" class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
<?php endforeach?>
</tbody>
step2:函数处理所有文章页面中的时间显示
1.用函数去格式化时间
// 用函数去处理时间
function convert_date ($created) {
// 数据库中存储的时间 '2017-07-01 08:08:00'
//格式化成 => '2017年07月01日 08:08:00
// 如果配置文件没有配置时区
// date_default_timezone_set('PRC');
$timestamp = strtotime($created);
return date('Y年m月d日<b\r>H:i:s', $timestamp);
}
- 在时间的标签中用函数的方式去显示对应的时间
<td class="text-center"><?php echo convert_date($item['created']); ?></td>
step3:函数处理所有文章页面中的状态显示
- 用函数去处理状态
// 用函数去处理状态
function convert_status($status){
$dict = array(
'published' => '已发布',
'drafted' => '草稿',
'trashed' => '回收站'
);
return isset($dict[$status]) ? $dict[$status] : '未知';
}
- 在状态的标签中用函数的方式去显示对应的状态
<td class="text-center"><?php echo convert_status($item['status']); ?></td>
step4:函数处理所有文章页面中的作者和分类显示
- 用函数去处理作者和分类
//php部分
//用函数去处理作者和分类方法一
function get_user($user_id){
return mysql_one ("select nickname from users where id = {$user_id}['nickname']");
}
function get_category($category_id){
return mysql_one ("select name from categories where id = {$category_id}['name']");
}
- 在分类和作者的标签中用函数的方式去显示对应的作者和分类
//Html部分
//用函数去处理作者和状态
<td><?php echo get_user($item['user_id']); ?></td>
<td><?php echo get_category($item['category_id']); ?></td>
step5:用关联数据库的方式去处理所有文章页面中的作者和分类显示(减少数据库的查询次数)
- 关联查询的方式介绍
1.1 关联查询 posts 表与 categories表
查询总条数 = posts的总条数 * categories的总条数
24 = 4 * 6
1.2 带有条件的关联查询
1.3 带有多个条件的关联查询
1.4 带有多个条件并查询多张表
1.5
如果这几个表中有相同名称的字段,在查询过后转换为关联数组就会有问题(关联数组 的键是不能重复的),所以我们需要指定需要查询的字段,同时还可以给每一个字段起一个别名,避免冲突分别给 users.nickname 和 categories.name 起别名
users.nickname ==> user_name
categoies.name == > category_name
- 关联查询的方式使用
2.1 将之前的查询语句改更改成关联查询的语句
//php部分
$posts = mysql_all('select
posts.id,
posts.title,
posts.created,
posts.status,
users.nickname as user_name,
categories.name as category_name
from posts
inner join categories on posts.category_id = categories.id
inner join users on posts.user_id = users.id');
2.2 更改html部分
//html部分
<td><?php echo $item['user_name']; ?></td>
<td><?php echo $item['category_name']; ?></td>
step6: 实现页面呈现多页数据
想要是实现分页的话,posts表中的数据显然不够做这个功能,所以需要导入更多的测试数据
- 复制 post-test-data.sql 文件代码 在数据库新建查询中粘贴,在代码末尾加上 commit; 然后点击运行
可以看到 posts 表中新增了很多数据
step7 : 分页加载文章数据
- 查询一部分数据
1.1 当数据过多过后,如果还是按照以上操作每次查询全部数据,页面就显得十分臃肿,加载起来也非常慢,所以必须 要通过分页加载的方式改善(每次只显示一部分数据)。
操作方式也非常简单,就是在原有 SQL 语句的基础之上加上 limit 和 order by 子句
1.2 imit 用法:limit [offset, ]rows
limit 10 -----只取前 10 条数据
limit 5, 10 ----- 从第 5 条之后,第 6 条开始,向后取 10 条数据
step8 : 分页参数计算
limit 子句中的 0 和 10 不是一成不变的,应该跟着页码的变化而变化,具体的规则就是:
第 1 页 limit 0, 10
第 2 页 limit 10, 10
第 3 页 limit 20, 10
第 4 页 limit 30, 10 ...
根据以上规则得出公式: offset = (page - 1) * size
// 处理分页参数
$size = 5;
// // 计算越过多少条
$offset = ($page - 1) * $size;
// 用关联数据中方式去查询数据库
// 获取全部数据
$posts = mysql_all("select
posts.id,
posts.title,
users.nickname as user_name,
categories.name as category_name,
posts.created,
posts.status
from posts
inner join categories on posts.category_id = categories.id
inner join users on posts.user_id = users.id
order by posts.created desc
limit {$offset}, {$size};
");
step9 : 获取当前页码
一般分页都是通过 URL 传递一个页码参数
也就是说,我们应该在页面开始执行的时候获取这个 URL 参数
//// 获取分页参数 没有或传过来的不是数字的话默认为 1 $page = isset($_GET['p']) && is_numeric($_GET['p'
$page = empty($_GET['page']) ? 1 : (int)$_GET['page'];
step 10 : 处理页面页码显示高亮并与地址栏同步
//Php部分
// 计算页码
$visiables = 5;
$region = ($visiables -1) / 2;
$begin = $page - $region;
$end = $page + $region;
//Html部分
<ul class="pagination pagination-sm pull-right">
<li><a href="#">上一页</a></li>
<?php for ($i = $begin; $i <= $end; $i++): ?>
<li<?php echo $i === $page ? ' class="active"' : '' ?>><a href="?page=<?php echo $i ;?>"><?php echo $i; ?></a></li>
<?php endfor ?>
<li><a href="#">下一页</a></li>
</ul>
step 11. 处理页面分页最大和最小页码限制
// 计算页码开始
$visiables = 5;
$region = ($visiables - 1) / 2; // 左右区间
$begin = $page - $region; // 开始页码
// $end = $page + $region;
$end = $begin + $visiables; // 结束页码 + 1
//出现$begin不合理的情况处理
//$begin必须大于1
if ($begin < 1) {
$begin = 1;
// begin 修改意味着必须要改 end
$end = $begin + $visiables;
}
//$end不得超过最大page
//求出最大页码
$total_count = (int)mysql_one('select count(1) as num from posts;')['num'];
$total_pages = (int)ceil($total_count / $size);
// 判断
if ($end > $total_pages + 1) {
// end 超出范围
$end = $total_pages + 1;
// end 修改意味着必须要改 begin
$begin = $end - $visiables;
if ($begin < 1) {
$begin = 1;
}
}
//html部分,与上面不同的是
//将for ($i = $begin; $i <= $end; $i++)更改成 for ($i = $begin; $i < $end; $i++)
<li>
<a href="#">上一页</a></li>
<?php for ($i = $begin; $i < $end; $i++): ?>
<li<?php echo $i === $page ? ' class="active"' : ''; ?>><a href="?page=<?php echo $i; ?>"><?php echo $i; ?></a></li>
<?php endfor ?>
<li><a href="#">下一页</a>
</li>
step 12 : 解决step11中的bug
- bug1 : 到50的时候就不能显示高亮(原因,因为50以后就是小数float,要取整数)
解决办法一:将小数类型强制转换成整数类型
之前代码
$total_pages = ceil($total_count / $size);
修改后的代码
$total_pages = (int)ceil($total_count / $size);
解决办法二:将三个 === 更改成 两个 == (让它变得没有那么严谨)
<li<?php echo $i == $page ? ' class="active"' : ''; ?>><a href="?page=<?php echo $i; ?>"><?php echo $i; ?></a></li>
- bug2 : 当点击最后一个页码时,会报错
解决办法:
将:
//$end不得超过最大page
//求出最大页码
$total_count = (int)mysql_one('select count(1) as num from posts;')['num'];
$total_pages = (int)ceil($total_count / $size);
更改成:
//$end不得超过最大page
//求出最大页码
$total_count = (int)mysql_one('select count(1) as num
from posts
inner join categories on posts.category_id = categories.id
inner join users on posts.user_id = users.id;')['num'];
$total_pages = (int)ceil($total_count / $size);
step 13 : 处理筛选分类,显示对应的分类页面
// 分类筛选
if(!empty($_GET['category'])){
$where .= ' and posts.category_id = ' . $_GET['category'];
}
//防止category=all时报错,所以更改成
if (isset($_GET['category']) && $_GET['category'] !== 'all') {
$where .= ' and posts.category_id = ' . $_GET['category'];
}
//HTML部分 利用foreach遍历循环得到的categories表中的数据并呈现在页面上
//给form表单设置action,表单自动默认method = "get",此处应该使用get方式
//所以可以不用设置method
<form class="form-inline" action="<?php echo $_SERVER['PHP_SELF']; ?>">
//给select加上name属性
<select name="category" class="form-control input-sm">
//给所有分类添加一个value="all",用来默认显示所有分类
<option value="all">所有分类</option>
//将未分类删掉,遍历循环categories表中数据
//<option value="">未分类</option>
<?php foreach ($categories as $item): ?>
//给分类的option添加value = "id" ,用来作为筛选的标识
//再加上一个判断当前是否存在$_GET['category'],用来判断当前所选项,记住筛选状态,并展示在框内
<option value="<?php echo $item['id']; ?>"<?php echo isset($_GET['category']) && $_GET['category'] == $item['id'] ? ' selected' : '' ?>>
<?php echo $item['name']; ?>
</option>
<?php endforeach ?>
</select>
step 14 : 处理筛选状态,显示对应的状态页面
//PHP部分
//筛选状态
if (isset($_GET['status']) && $_GET['status'] !== 'all') {
$where .= " and posts.status = '{$_GET['status']}'";
}
//HTML部分
//给每一个option标签都加上对应的value属性
//并且判断是否存在$_GET['status'],用来判断当前所选项,记住筛选状态,并展示在框内
<select name="status" class="form-control input-sm">
<option value="all">所有状态</option>
<option value="drafted"<?php echo isset($_GET['status']) && $_GET['status'] == 'drafted' ? ' selected' : '' ?>>草稿</option>
<option value="published"<?php echo isset($_GET['status']) && $_GET['status'] == 'published' ? ' selected' : '' ?>>已发布</option>
<option value="trashed"<?php echo isset($_GET['status']) && $_GET['status'] == 'trashed' ? ' selected' : '' ?>>回收站</option>
</select>
step 15 : 根据筛选的参数,显示筛选数据的页面
当我们加上 where 语句时,可实现数据的筛选,where语句筛选数据都是根据数据的唯一标识(也就是id)来进行筛选
所以,在之前的每一项查询中添加 where 子句
//sql语句的注入
// 数据库查询筛选条件(默认为 1 = 1,相当于没有条件)
$where = '1 = 1';
$posts = mysql_all("select
posts.id,
posts.title,
users.nickname as user_name,
categories.name as category_name,
posts.created,
posts.status
from posts
inner join categories on posts.category_id = categories.id
inner join users on posts.user_id = users.id
where {$where}
order by posts.created desc
limit {$offset}, {$size};
");
$total_count = (int)mysql_one("select count(1) as num from posts
inner join categories on posts.category_id = categories.id
inner join users on posts.user_id = users.id
where {$where};")['num'];
$total_pages = (int)ceil($total_count / $size);
step 16 : 根据筛选的数据显示完整的 url 地址
///Php部分
//定义一个serch
$search = '';
//在每个筛选下加上serch
// 分类筛选
// if(!empty($_GET['category'])){
if (isset($_GET['category']) && $_GET['category'] !== 'all') {
$where .= ' and posts.category_id = ' . $_GET['category'];
$search .= '&category=' . $_GET['category'];
}
//筛选状态
if (isset($_GET['status']) && $_GET['status'] !== 'all') {
$where .= " and posts.status = '{$_GET['status']}'";
$search .= '&status=' . $_GET['status'];
}
//Html部分
//没有处理分类和状态显示所有分类和状态之前
<li><a href="#">上一页</a></li>
<?php for ($i = $begin; $i < $end; $i++): ?>
<li<?php echo $i === $page ? ' class="active"' : ''; ?>><a href="?page=<?php echo $i; ?>">
<?php echo $i; ?></a></li>
<?php endfor ?>
<li><a href="#">下一页</a></li>-->
//处理分类和状态显示所有分类和状态之后
<li><a href="#">上一页</a></li>
<?php for ($i = $begin; $i < $end; $i++): ?>
<li<?php echo $i == $page ? ' class="active"' : '' ?>><a href="?page=<?php echo $i . $search; ?>"><?php echo $i; ?></a></li>
<?php endfor ?>
<li><a href="#">下一页</a></li>
step 17 : 如果用户在url地址中输入大于总页数的页码,则跳到最大页码
if ($page > $total_pages) {
// 跳转到第最后页
header('Location: /admin/posts.php?page=' . $total_pages . $search);
}
step 18 : 文章中单条数据删除功能
//Html部分
<a href="/admin/post-delete.php?id=<?php echo $item['id']; ?>" class="btn btn-danger btn-xs">删除</a>
新增一个 post-delete.php 文件与 category-delete.php 类似
post-delete.php
<?php
/**
* 根据客户端传递过来的ID删除对应数据
*/
require_once '../functions.php';
if (empty($_GET['id'])) {
exit('缺少必要参数');
}
// $id = (int)$_GET['id'];
$id = $_GET['id'];
$rows = mysql_change('delete from posts where id in (' . $id . ');');
header('Location: ' . $_SERVER['HTTP_REFERER']);
// http 中的 referer 用来标识当前请求的来源
//末尾这句话的作用是为了当用户删除最后一页数据时,不会报错,返回的是倒数第二页,显示正确
posts.php完整代码
<?php
header("Content-Type: text/html;charset=utf-8");
require_once '../functions.php';
get_userinfo();
// 处理分页参数
$page = empty($_GET['page']) ? 1 : (int)$_GET['page'];
$size = 20;
// 计算出越过多少条
$offset = ($page - 1) * $size;
// 接收筛选数据
$where = '1 = 1';
$search = '';
// 分类筛选
if (isset($_GET['category']) && $_GET['category'] !== 'all') {
$where .= ' and posts.category_id = ' . $_GET['category'];
$search .= '&category=' . $_GET['category'];
}
//筛选状态
if (isset($_GET['status']) && $_GET['status'] !== 'all') {
$where .= " and posts.status = '{$_GET['status']}'";
$search .= '&status=' . $_GET['status'];
}
// 用关联数据中方式去查询数据库
// 获取全部数据
$posts = mysql_all("select
posts.id,
posts.title,
users.nickname as user_name,
categories.name as category_name,
posts.created,
posts.status
from posts
inner join categories on posts.category_id = categories.id
inner join users on posts.user_id = users.id
where {$where}
order by posts.created desc
limit {$offset}, {$size};
");
// 处理分页页码===============================
// 计算页码
// 计算页码开始
$visiables = 5;
$region = ($visiables - 1) / 2; // 左右区间
$begin = $page - $region; // 开始页码
$end = $begin + $visiables; // 结束页码 + 1
//出现$begin不合理的情况处理
//$begin必须大于1
if ($begin < 1) {
$begin = 1;
// begin 修改意味着必须要改 end
$end = $begin + $visiables;
}
//$end不得超过最大page
//求出最大页码
$total_count = (int)mysql_one("select count(1) as num from posts
inner join categories on posts.category_id = categories.id
inner join users on posts.user_id = users.id
where {$where};")['num'];
$total_pages = (int)ceil($total_count / $size);
// 判断
if ($end > $total_pages + 1) {
// end 超出范围
$end = $total_pages + 1;
// end 修改意味着必须要改 begin
$begin = $end - $visiables;
if ($begin < 1) {
$begin = 1;
}
}
if ($page > $total_pages) {
// 跳转到第最后页
header('Location: /admin/posts.php?page=' . $total_pages . $search);
}
// 用函数去处理状态
function convert_status($status){
$dict = array(
'published' => '已发布',
'drafted' => '草稿',
'trashed' => '回收站'
);
return isset($dict[$status]) ? $dict[$status] : '未知';
}
// 用函数去处理时间,格式化时间
function convert_date ($created) {
// 如果配置文件没有配置时区
// date_default_timezone_set('PRC');
$timestamp = strtotime($created);
return date('Y年m月d日<b\r>H:i:s', $timestamp);
}
$categories = mysql_all('select * from categories;');
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>Posts « Admin</title>
<link rel="stylesheet" href="/static/assets/vendors/bootstrap/css/bootstrap.css">
<link rel="stylesheet" href="/static/assets/vendors/font-awesome/css/font-awesome.css">
<link rel="stylesheet" href="/static/assets/vendors/nprogress/nprogress.css">
<link rel="stylesheet" href="/static/assets/css/admin.css">
<script src="/static/assets/vendors/nprogress/nprogress.js"></script>
</head>
<body>
<script>NProgress.start()</script>
<div class="main">
<?php include 'com/nav.php';?>
<div class="container-fluid">
<div class="page-title">
<h1>所有文章</h1>
<a href="post-add.php" class="btn btn-primary btn-xs">写文章</a>
</div>
<div class="page-action">
<!-- show when multiple checked -->
<a class="btn btn-danger btn-sm" href="javascript:;" style="display: none">批量删除</a>
<form class="form-inline" action="<?php echo $_SERVER['PHP_SELF']; ?>" >
<select name="category" class="form-control input-sm">
<option value="all">所有分类</option>
<!-- <option value="">未分类</option> -->
<?php foreach ($categories as $item): ?>
<option value="<?php echo $item['id']; ?>"<?php echo isset($_GET['category']) && $_GET['category'] == $item['id'] ? ' selected' : '' ?>>
<?php echo $item['name']; ?>
</option>
<?php endforeach ?>
</select>
<select name="status" class="form-control input-sm">
<option value="all">所有状态</option>
<option value="drafted"<?php echo isset($_GET['status']) && $_GET['status'] == 'drafted' ? ' selected' : '' ?>>草稿</option>
<option value="published"<?php echo isset($_GET['status']) && $_GET['status'] == 'published' ? ' selected' : '' ?>>已发布</option>
<option value="trashed"<?php echo isset($_GET['status']) && $_GET['status'] == 'trashed' ? ' selected' : '' ?>>回收站</option>
</select>
<button class="btn btn-default btn-sm">筛选</button>
</form>
<ul class="pagination pagination-sm pull-right">
<li><a href="#">上一页</a></li>
<?php for ($i = $begin; $i < $end; $i++): ?>
<li<?php echo $i == $page ? ' class="active"' : '' ?>><a href="?page=<?php echo $i . $search; ?>"><?php echo $i; ?></a></li>
<?php endfor ?>
<li><a href="#">下一页</a></li>
</ul>
</div>
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th class="text-center" width="40"><input type="checkbox"></th>
<th>标题</th>
<th>作者</th>
<th>分类</th>
<th class="text-center">发表时间</th>
<th class="text-center">状态</th>
<th class="text-center" width="100">操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($posts as $item): ?>
<tr>
<td class="text-center"><input type="checkbox"></td>
<td><?php echo $item['title']; ?></td>
//用关联数据库的方式去处理作者和状态
<td><?php echo $item['user_name']; ?></td>
<td><?php echo $item['category_name']; ?></td>
//用函数去处理时间
<td class="text-center"><?php echo convert_date($item['created']); ?></td>
// 用函数处理后显示状态
<td class="text-center"><?php echo convert_status($item['status']); ?></td>
<td class="text-center">
<a href="javascript:;" class="btn btn-default btn-xs">编辑</a>
<!-- <a href="javascript:;" class="btn btn-danger btn-xs">删除</a> -->
<a href="/admin/post-delete.php?id=<?php echo $item['id']; ?>" class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
<?php endforeach?>
</tbody>
</table>
</div>
</div>
<?php $current_page='posts';?>
<?php include 'com/sidebar.php';?>
<script src="/static/assets/vendors/jquery/jquery.js"></script>
<script src="/static/assets/vendors/bootstrap/js/bootstrap.js"></script>
<script>NProgress.done()</script>
</body>
</html>