简易数据管理案例带有分页展示、新增、删除、修改和服务容器的概念与实际测试
程序员文章站
2022-03-24 21:20:22
...
数据管理案例
获取数据页面
pageData.php
获取前端页面的基本数据,加入了一个小判断
<?php
$dsn = 'mysql:host=localhost;dbname=phpedu';
$username = 'root';
$password = 'root';
$db = new PDO($dsn,$username,$password);
// 设置每页的条数
$num = 2;
// 获取页数,P存在使用p,不存在就使用1
$page = $_GET['p']?? 1;
// 获取总页数,结果需要向上取整CEIL 向上取整,count统计记录
$sql = "SELECT CEIL(COUNT(*) / {$num}) AS `pages` FROM `users` ";
$pages = $db->query($sql)->fetch()['pages'];
// 更多安全设置的$page,当页数小于1 都设置为1 当页数超过总页数设为第一页
if($page <= 1){
$page = 1;
}else if($page > $pages){
$page = 1;
}
// 得到偏移量 (p - 1) * n
$offset = ($page - 1) * $num;
$sql = "SELECT `id`,`name`,`email` FROM `users` LIMIT {$offset}, {$num}";
$users = $db->query($sql)->fetchAll(PDO::FETCH_ASSOC);
// print_r($pages);
分页技术
前端页面test3.php
这是完整的页面,上部分是分页的逻辑判断,下部分是渲染部分,表格渲染,分页渲染
<?php
//引入分页数据页面
require 'pageData.php';
// 设置$prev上一页和$next下一页的值
if($page > 1){
$prev = $page -1;
}
if($page < $pages){
$next = $page + 1;
}
// 智能分页的设置
//------------------------------------------------------------------
// 显示页码数量 需要是奇数
$showPages = 5;
// 新的起始页码,只是显示的页码 默认给1
$startPage = 1;
// 新的结束页码 默认给总页数
$endPage = $pages;
// 偏移量:当页数变动成新的时候,起始页码是当前页码减去偏移量,结束页码是当前页码加上偏移量
$offsetPage = ($showPages-1) / 2;
// 前提条件 智能分页 需要展示条数小于总页数才显示
if($showPages < $pages){
// 当前页码要高于偏移量加1 就是 例如这里 页码需要大于 3 以上才可以出现,
// 低于3就是在1、2、3页内前面都没有多余页码,显示来干嘛
if($page > $offsetPage+1 ) $startOmit='...';
// 当前页码大于偏移量的时候就要改变起始页码和结束页码了
if($page > $offsetPage){
$startPage = $page-$offsetPage;
$endPage = $page+$offsetPage;
// 判断结束页是否越界,不允许结束页大于总页数的情况
if($endPage > $pages){
$endPage = $pages;
// 当endPage也就是($page+$offsetPage) 大于总页数,就将endPage设置为总页数
// endPage值改变了,startPage也应该改变,不然显示数量就不对了
$startPage = $pages-($showPages-1);
// 解释:起始页码等于 总页数减去(展示页数减一)
// 举例总页数11 展示页码 5 结束页码等于总页数11 起始页码等于11-5=6 下面的循环从6开始 6 7 8 9 10 11这样就会出现6条
// 减1的话就是11-4=7从 7 8 9 10 11 这样才是正确的起始页码,展示5位页码
}
// 如果小于偏移量起始页就是1 结束页就是显示条数的值
}else{
$startPage = 1;
$endPage = $showPages;
}
// 当前页码大于展示页数,并且当前页码加上偏移量还小于总页码(不包含等于)的情况才显示这个
if( $page+$offsetPage < $pages )$endOmit='...';
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>高端智能的分页条</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<p>
<a href="">用户登录</a>
</p>
<table>
<caption>用户信息表 <button onclick="location.href='handles.php?action=add'">添加</button></caption>
<thead>
<th>ID</th>
<th>姓名</th>
<th>邮箱</th>
<th>操作</th>
</thead>
<tbody>
<?php foreach($users as $user) :?>
<tr>
<td><?=$user['id']?></td>
<td><?=$user['name']?></td>
<td><?=$user['email']?></td>
<td><button onclick="location.href='handles.php?action=edit&id=<?=$user['id']?>'" >编辑</button>
<button onclick="del(<?=$user['id']?>)">删除</button></td>
</tr>
<?php endforeach ?>
</tbody>
</table>
<p>
<!-- 判断显示 在第一页的时候应该没有首页和上一页的按钮 -->
<?php if($page != 1) :?>
<!-- 固定首页 将p对应值设置为1 -->
<a href="<?=$_SERVER['PHP_SELF'].'?p=1'?>">首页</a>
<!-- 显示上一页 -->
<a href="<?=$_SERVER['PHP_SELF'].'?p='.$prev?>">上一页</a>
<!-- 判断是否有值 -->
<?php if(isset($startOmit)) :?>
<a href="#"><?=$startOmit?></a>
<?php endif ?>
<?php endif ?>
<!-- 循环输出页数 -->
<?php for($i=$startPage; $i<=$endPage; $i++) : ?>
<!-- 当$i等于页码 给class赋值 -->
<?php $active = ($i == $page ) ? 'active' : null; ?>
<a href="<?=$_SERVER['PHP_SELF'].'?p='.$i?>" class="<?=$active?>" ><?=$i?></a>
<?php endfor ?>
<!-- 判断显示 在最后一页的时候应该没有尾页和下一页的按钮 -->
<?php if($page != $pages) :?>
<!-- 判断是否有值 -->
<?php if(isset($endOmit)) :?>
<a href="#"><?=$endOmit?></a>
<?php endif ?>
<!-- 显示下一页 -->
<a href="<?=$_SERVER['PHP_SELF'].'?p='.$next?>">下一页</a>
<!-- 固定尾页 将P对应值设为总页数 -->
<a href="<?=$_SERVER['PHP_SELF'].'?p='.$pages?>">尾页</a>
<?php endif ?>
</p>
</body>
<script>
//确认事件,用户点击确定的时候才跳转URL
function del(id){
let url = 'http://php.edu/0912/handles.php?action=delete&id='+id;
return confirm('是否删除?') ?location.href=url : false;
}
</script>
</html>
在首页不显示上一页和首页,会显示更多按钮…
完全显示全部按钮
在尾页不显示下一页和尾页,会显示更多按钮…
分页总结
- 计算出符合条件的总页数,
CEIL(COUNT(*)) / n
,CEIL
向上取整,Count
统计,*
全部或某个字段,n
=每页条数 - 计算偏移量
(p-1)*n
公式计算,p=页码从1开始,n=每页条数 - 前端循环出全部页码或部分页码,改变URL中p=页码数,改变显示数据。
http://php.edu/0912/test3.php?p=1
- 基本概念如上,自己可以在此基础上优化,例如案例中固定5位变化显示页码,上下页,首尾页等
其他操作代码,更新,插入,删除
handles.php
操作分发器
<?php
// 1.进行数据库连接
$dsn ='mysql:host=localhost;dbname=phpedu';
$username = 'root';
$password = 'root';
$db = new PDO($dsn,$username,$password);
// 2.获取操作类型和id
$action = $_GET['action'];
$id = $_GET['id'];
// 3.操作分发器
switch($action){
case 'edit':
// 3.1引入编辑页码操作
$user = $db->query('SELECT * FROM `users` WHERE id ='.$id)->fetch(PDO::FETCH_ASSOC);
include 'edit.php';
break;
case 'save':
// 3.2保存数据操作
// print_r($_POST);
$sql = 'UPDATE `users` SET `name`=?, `email`=? WHERE `id`=?;';
$stmt = $db->prepare($sql);
if(!empty($_POST)){
$stmt->execute([$_POST['name'], $_POST['email'], $id]);
if($stmt->rowCount()==1){
echo '<script>alert("更新成功");location.href="test3.php";</script>';
}else{
echo "<script>alert('没有数据被更新,请检查内容是否被修改');location.href='test3.php'</script>";
}
}
break;
case 'add':
// 3.3引入新增用户操作
include 'add.php';
break;
case 'saveAdd':
// 3.4 保存新用户数据
// print_r($_POST);
// 可以进行更多的安全判断,和使用数据过滤器
if(empty($_POST['email'])){
echo "<script>alert('请输入邮箱!');location.href='test3.php'</script>";
}
// 实际操作
$sql = 'INSERT INTO `users` SET `name`=?, `email`=? ,`password`=?;';
$stmt = $db->prepare($sql);
$stmt->execute([$_POST['name'], $_POST['email'], sha1($_POST['password'])]);
if($stmt->rowCount()==1){
echo '<script>alert("添加成功");location.href="test3.php";</script>';
}else{
echo "<script>alert('添加失败');location.href='test3.php'</script>";
}
break;
case 'delete':
// 3.5删除操作
$stmt = $db->prepare('DELETE FROM `users` WHERE id=?;');
$stmt->execute([$id]);
if($stmt->rowCount() == 1){
echo '<script>alert("删除成功");location.href="test3.php";</script>';
}
break;
default:
// 默认操作,直接打开此页面跳转回数据页
echo '<script>alert("非法操作");location.href="test3.php";</script>';
}
add.php
添加页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户添加页</title>
</head>
<body>
<form action="<?= $_SERVER['PHP_SELF'].'?action=saveAdd'?>" method="POST" >
<fieldset>
<legend>用户添加</legend>
<p>
<label for="">用户名:</label>
<input type="text" name="name" id="name" >
</p>
<p>
<label for="">邮箱:</label>
<input type="email" name="email" id="email" >
</p>
<p>
<label for="">密码:</label>
<input type="password" name="password" id="password" >
</p>
<p>
<input type="submit" value="提交">
</p>
</fieldset>
</form>
</body>
</html>
edit.php
修改页面
<?php
// 编辑表单
// http://php.edu/0912/handle.php?action=edit&id=13
// 从上面的url中解析出id,根据id查询获取对应的用户数据
$user = $pdo->query('select * from users where id='.$id)->fetch();
// print_r($user);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户编辑</title>
</head>
<body>
<h3>用户编辑</h3>
<form action="<?php echo $_SERVER['PHP_SELF']. '?action=doedit&id='.$id?>" method="post">
<p>
<label for="name">用户名:</label>
<input type="text" id="name" name='name' value="<?=$user['name']?>">
</p>
<p>
<label for="email">邮箱:</label>
<input type="email" name='email' id="email" value="<?=$user['email']?>">
</p>
<p>
<button>保存</button>
</p>
</form>
</body>
</html>
服务容器
Model.php
数据获取类
<?php
namespace mvc_test;
// 当声明了命名空间,不进行USE或者是\表示清楚PDO,它就会去找命名空间下的PDO这样是会找不到的
use PDO;
class Model{
public function getData(){
return
(new PDO('mysql:host=localhost;dbname=phpedu','root','root'))
->query('SELECT id,name,email from users limit 5')
->fetchAll(PDO::FETCH_ASSOC);
}
}
View.php
视图渲染类
<?php
// 视图类
namespace mvc_demo;
class View
{
// 展示模型中的数据
public function fetch($data)
{
$table = '<table>';
$table .= '<caption>用户信息表</caption>';
$table .= '<tr><th>ID</th><th>姓名</th><th>邮箱</th></tr>';
// 遍历用户数据
foreach ($data as $user) {
$table .= '<tr>';
$table .= '<td>'.$user['id'].'</td>';
$table .= '<td>'.$user['name'].'</td>';
$table .= '<td>'.$user['email'].'</td>';
$table .='</tr>';
}
$table .= '</table>';
return $table;
}
}
echo '<style>
table {border-collapse: collapse; border: 1px solid;text-align: center; width: 500px;height: 150px;width: 600px;}
caption {font-size: 1.2rem; margin-bottom: 10px;}
tr:first-of-type { background-color:coral;}
td,th {border: 1px solid; padding:5px}
</style>';
Controller.php
控制器页面
<?php
namespace mvc_test;
use closure;
// 匿名函数类。
require __DIR__.DIRECTORY_SEPARATOR.'Model.php';
require __DIR__.DIRECTORY_SEPARATOR.'View.php';
//----------------------------------------------------------------
// 服务容器
class Container{
// 1.对象容器,数组可以存储各种数据包括类实例
protected $instances = [];
// 2.实例绑定到容器中,
// 参数1:对象别名,推荐还是和类名一致
// 参数2:对象, closure声明参数是匿名函数,可以省略
public function bind($alias,closure $process)
{
$this->instances[$alias] = $process;
}
// 3.容器内取出对象(类实例)
public function make($alias,$params=[])
{
// 避免调用影响,使用回调方式去调用
return call_user_func_array($this->instances[$alias],$params);
}
}
// 将依赖的外部对象绑定到服务容器中
$container = new Container();
// 将模型绑定到容器中
$container->bind('model',function(){ return new Model();});
// 将视图绑定到容器中
$container->bind('view',function(){ return new View();});
// print_r($container);die();
//----------------------------------------------------------------
// 控制器
class Controller
{
// 存放服务容器,给其他方式调用
protected $container ='';
// 构造方法,使控制器实例的时候传入服务容器赋值给属性存放
public function __construct(Container $container)
{
$this->container = $container;
}
// 获取数据并展示出来,传入服务容器
public function index()
{
//1. 获取数据
$data = $this->container->make('model')->getData();
// 2. 渲染模板
return $this->container->make('view')->fetch($data);
}
}
// -------------------------------------------------
// 客户端 :测试
// 实例控制器,将服务容器对象作为参数传入
$controller = new Controller($container);
echo $controller->index();
服务容器的总结
- 简单的服务容器概念
- 就是将各种模型类,视图类等等的实例化存储到一个类(服务容器)中,这样可以容纳多个类,访问也很简单
- 控制器创建属性,构造方法存储服务容器,方法需要的时候只需调用这个属性,取出对应的对象进行操作