Correcting teacher:灭绝师太
Correction status:qualified
Teacher's comments:php中文网不教授没有用武之地的课程, 设计程序要从用户体验出发, 便民利民才能走的更远, 参考支付宝,微信给你带来的遍历~(除了微信中聊天转账功能不分离有待商榷)
数据分页其实就是SQL构造语句的Limit使用,其有两种形式:SELECT * FROM user LIMIT 0,10;
和SELECT * FROM USER LIMIT 10 OFFSET 0;
。若是LIMIT 0,10
则偏移量是0,10是记录条数,而是使用LIMIT 10 OFFSET 0
表示意思是一样的,即若LIMIT后面只有一个数字,则表示取得记录条数。
关于偏移量的计算是offset=(page-1)*num;
。page
是当前页的页码,即是index.php?p=2
中2。num则表示每页要显示的记录条数。
写本博文开始自己封装常用的类了,如数据库类Db,目前是第一版,实现了数据库链式查询和CURD操作,至于预处理和事务则后续版本中逐渐增加。
declare(strict_types=1);
namespace WOXIAOYAO;
use \PDO;
use \Exception;
/*
* @Descripttion: 自己封装的PDO类,可支持MySQL、MSSQL、ORACLE和SQLite的数据库操作
* @version: 1.0.0
* 准备要完成功能:1、支持PDO的query和exec(1.0.0) 2、支持PDO的预处理 3、支持PDO的事务处理
*/
// 准备知识:四种数据库连接方式
// MySQL:'dsn'=>'mysql:host=localhost;dbname=talk','username'=>'root','password'=>'123456'
// MSSQL:'dsn'=>'odbc:Driver={SQL Server};Server=192.168.1.60;Database=his','username'=>'sa','password'=>'xxxxx'
// Oracle:'dsn'=>'oci:dbname=orcl','username'=>'BAOCRM','password'=>'BAOCRM'
// SQLite:'dsn'=>'sqlite:'.dirname(__FILE__).'\log.db'
// 抽象类完成单例模式连接、准备处理方法和接口的定义
// 抽象类的保护静态成员为所有子类共享
abstract class aDb
{
// 定义单例模式连接
protected static $pdo = null;
protected static $config = null;
final protected function connect(array $config)
{
$config = array_change_key_case($config, CASE_LOWER);
if ($config === false) throw new Exception('连接配置不是数组');
if (empty($this->is_assoc($config))) throw new Exception('连接配置不是关联数组');
if (empty(self::$config)) {
self::$config = $config;
} else if (!empty(array_diff_assoc(self::$config, $config))) {
self::$config = $config;
} else {
return self::$pdo;
}
try {
$pdo = new \PDO(self::$config['dsn'], self::$config['username'], self::$config['password']);
// 若没报错则先清除旧连接,重置为新连接
self::$pdo = null;
self::$pdo = $pdo;
self::$pdo->query("set names utf8");
//属性名 属性值 数组以关联数组返回
self::$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
} catch (\Exception $e) {
echo '数据库连接失败,详情: ' . $e->getMessage() . ' 请在配置文件中数据库连接信息';
exit();
}
}
// 判断是否是关联数组
final protected function is_assoc(array $arr)
{
if (is_array($arr)) {
$key = array_keys($arr);
return $key === array_keys($key) ? false : true;
}
return null;
}
// 定义接口规范
// 链式规范
abstract public function table(string $table);
abstract public function field(string $fields);
abstract public function where($where);
abstract public function order(string $order);
abstract public function limit(string $limit);
// 单条记录和多条记录查询
abstract public function find();
abstract public function select();
// 插入、更新和删除规范
abstract public function insert(array $data);
abstract public function update(array $data);
abstract public function delete();
}
// 工作类,实现CURD操作
class Db extends aDb
{
private $res;
private $table;
private $fields = '*';
private $where = 'true';
private $order;
private $limit;
function __construct(array $config)
{
$this->connect($config);
}
function getConfig()
{
return parent::$config;
}
function getPDO()
{
return parent::$pdo;
}
private function reset(){
// $table='';
$this->fields = '*';
$this->where = 'true';
$this->order = '';
$this->limit = '';
}
// 链式查询
function table(string $table)
{
if (!is_string($table)) throw new Exception("参数是字符串,形式如'user'表示user表");
if (!empty($table))
$this->table = $table;
return $this;
}
function field(string $fields)
{
if (!is_string($fields)) throw new Exception("参数是字符串,形式如'id,name,pwd'表示获取3个字段");
if (!empty($fields))
$this->fields = $fields;
return $this;
}
function where($where)
{
if (is_string($where)) {
if (!empty($fields))
$this->where .= " and {$where}";
return $this;
}
if ($this->is_assoc($where)) {
while (current($where)) {
$this->where .= ' and ' . key($where) . '=' . current($where);
next($where);
}
return $this;
}
throw new Exception('请检查条件');
}
function order(string $order)
{
if (!is_string($order)) throw new Exception("参数是字符串,形式如'id asc'表示id升序");
if (!empty($order))
$this->order = $order;
return $this;
}
function limit(string $limit)
{
if (!is_string($limit)) throw new Exception("参数是字符串,形式如'0,5'表示偏移0数量是5");
if (!empty($limit))
$this->limit = $limit;
return $this;
}
// 查询单条记录
function find()
{
try {
if (empty($this->table)) throw new Exception('没有查询表');
$sql = "SELECT {$this->fields} FROM {$this->table} WHERE {$this->where}";
if (!empty($this->order))
$sql .= " ORDER BY {$this->order}";
$this->res = parent::$pdo->query($sql);
$this->reset();
return $this->res->fetch(PDO::FETCH_ASSOC);
} catch (\PDOException $e) {
return '查询错误信息:' . $e->getMessage();
}
}
// 查询所有记录
function select()
{
try {
if (empty($this->table)) throw new Exception('没有查询表');
$sql = "SELECT {$this->fields} FROM {$this->table} WHERE {$this->where}";
if (!empty($this->order))
$sql .= " ORDER BY {$this->order}";
if (!empty($this->limit))
$sql .= " LIMIT {$this->limit}";
$this->res = parent::$pdo->query($sql);
$this->reset();
return $this->res->fetchAll(PDO::FETCH_ASSOC);
} catch (\PDOException $e) {
return '查询错误信息:' . $e->getMessage();
}
}
// 插入、更新和删除操作
public function insert(array $data)
{
if (empty($this->is_assoc($data))) throw new Exception('插入数据不是关联数组');
if (empty($this->table)) throw new Exception('插入时必须指定表');
$sql = "INSERT INTO {$this->table}";
$key = key($data);
$value = "'" . current($data) . "'";
next($data);
while (current($data)) {
$key .= "," . key($data);
$value .= ",'" . current($data) . "'";
next($data);
}
$sql .= " ({$key}) VALUES ({$value})";
$this->res = parent::$pdo->exec($sql);
$this->reset();
return $this->res;
}
public function update(array $data)
{
if (empty($this->is_assoc($data))) throw new Exception('更新数据不是关联数组');
if (empty($this->table)) throw new Exception('更新时必须指定表');
if ($this->where == 'true') throw new Exception('更新时必须指定条件');
$sql = "UPDATE {$this->table}";
$item = key($data) . "='" . current($data) . "'";
next($data);
while (current($data)) {
$item .= "," . key($data) . "='" . current($data) . "'";
next($data);
}
$sql .= " SET {$item} WHERE {$this->where}";
$this->res = parent::$pdo->exec($sql);
$this->reset();
return $this->res;
}
public function delete()
{
try {
if (empty($this->table)) throw new Exception('删除时必须指定表');
if ($this->where == 'true') throw new Exception('删除时必须指定条件');
$sql = "DELETE FROM {$this->table} WHERE {$this->where}";
$this->res = parent::$pdo->exec($sql);
$this->reset();
return $this->res;
} catch (\PDOException $e) {
return '查询错误信息:' . $e->getMessage();
}
}
}
上面已经准备了数据库,也理解了分页的知识,下面代码反而比较简单,直接看代码
//pagnate.php
require_once 'Db.php';
use WOXIAOYAO\Db;
$config = [
'dsn' => 'mysql:host=localhost;dbname=test',
'username' => 'root',
'password' => 'root'
];
$obj = new Db($config);
//分页获取数据
$num = 10;
$res = $obj->table('user')->field('count(id) as total')->select();
$total = intval($res[0]['total']);
$pages = ceil($total / $num);
$page = $_GET['p'] ?? 1;
$offset = ($page - 1) * $num;
$users = $obj->table('user')->limit("{$offset},{$num}")->select();
// index.php
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
a {
text-decoration: none;
display: inline-block;
/* width: 2em; */
height: 2em;
line-height: 2em;
}
.container {
width: 60vw;
margin: 1em auto;
}
td {
text-align: center;
}
.page {
margin-top: 1em;
text-align: center;
}
td a:first-child {
margin-right: 5px;
}
td a:last-child {
margin-left: 5px;
}
.page a {
padding: 0 0.5em;
margin: 0 5px;
}
.page a.cur {
background-color: #007d20;
color: white;
}
</style>
<div class="container">
<table border='1' cellspacing="0" width="100%">
<caption>用户信息表</caption>
<thead>
<tr bgColor="lightgray">
<th>ID</th>
<th>name</th>
<th>password</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php
include_once 'pagnate.php';
foreach ($users as $user) {
$trdata = "<tr>";
foreach ($user as $item) {
$trdata .= "<td>{$item}</td>";
}
$trdata .= "<td><a href='#'>编辑</a><a href='#'>删除</a></td>";
$trdata .= "</tr>";
echo $trdata;
}
?>
</tbody>
</table>
<div class="page">
<?php
echo "<a href='{$_SERVER["PHP_SELF"]}?p=1'>首页</a>";
$prev = ($page - 1 > 1) ? ($page - 1) : 1;
if ($page > 1)
echo "<a href='{$_SERVER["PHP_SELF"]}?p={$prev}'>上一页</a>";
for ($i = 1; $i <= $pages; $i++) :
if ($i == $page)
echo "<a class='cur' href='{$_SERVER["PHP_SELF"]}?p={$i}'>{$i}</a>";
else
echo "<a href='{$_SERVER["PHP_SELF"]}?p={$i}'>{$i}</a>";
endfor;
$next = ($page + 1) < $pages ? ($page + 1) : $pages;
if ($page < $pages)
echo "<a href='{$_SERVER["PHP_SELF"]}?p={$next}'>下一页</a>";
echo "<a href='{$_SERVER["PHP_SELF"]}?p={$pages}'>未页</a>";
?>
</div>
</div>
无论是PHP中文网或老师演示的省略号仅仅展示,而无实际功能,而我认为前省略号相当于前一页,后面省略号相当于后一页。具体代码我在老师的基础上进行了精简。下面分布导航既有老师所演示的省略号功能,而且省略号也能起到跳转页码作用。
//pagnate.php
// 改进的导航栏(在基础版中增加)
$startPage = 1;
// 显示页码数最好为奇数
$showPage = 5;
if (($page - ceil(($showPage - 1) / 2)) > $startPage)
$startPage = $page - ceil(($showPage - 1) / 2);
//index2.php
// 分页数据代码不变,对分页页码导航代码重新编写
<div class="page">
<?php
echo "<a href='{$_SERVER["PHP_SELF"]}?p=1'>首页</a>";
$prev = ($page - 1 > 1) ? ($page - 1) : 1;
if ($startPage > 1)
echo "<a href='{$_SERVER["PHP_SELF"]}?p={$prev}'>...</a>";
for ($i = $startPage; $i < $startPage+$showPage; $i++) :
if ($i == $page)
echo "<a class='cur' href='{$_SERVER["PHP_SELF"]}?p={$i}'>{$i}</a>";
else
echo "<a href='{$_SERVER["PHP_SELF"]}?p={$i}'>{$i}</a>";
endfor;
$next = ($page + 1) < $pages ? ($page + 1) : $pages;
if ($startPage+$showPage <= $pages+1)
echo "<a href='{$_SERVER["PHP_SELF"]}?p={$next}'>...</a>";
echo "<a href='{$_SERVER["PHP_SELF"]}?p={$pages}'>未页</a>";
?>
</div>
关于单条数据的编辑和删除: 其实就是数据库的更新和删除操作,比较简单,我这里就不演示了