批改状态:合格
老师批语:如果参数是关联数组, 则与顺序无关, 索引数组才需要关注参数顺序
创建库表

初始数据(部分)

代码结构图

PlayerController.php
<?phpnamespace controller;use model\facade\PlayerModel;use PDO;class PlayerController {/*** 查询球员分页数据* $p: 当前页码* $r: 每页显示记录数*/public function players($p = 1, $r = 3) {// 查询球员总数$count = PlayerModel::playerCount();// 调整异常页码数值if(!filter_var($p, FILTER_VALIDATE_INT)){$p = 1;}$p < 1 ? 1 : ($p > $count ? $count : $p);if(!filter_var($r, FILTER_VALIDATE_INT)){$p = 3;}// 查询分页数据$players = PlayerModel::players($p, $r);// 加载视图文件,渲染数据require(dirname(__DIR__) . '/view/player/list.php');}// 跳转到球员信息编辑界面public function edit($id) {// 判断球员id有效性if(!filter_var($id, FILTER_VALIDATE_INT, ['option' => ['min_range' => 1]])) {echo("<script>alert('无效的参数');window.history.go(-1);</script>");exit;}// 查询球员信息$player = PlayerModel::player($id);// 加载视图模板,并渲染数据require(dirname(__DIR__) . '/view/player/edit.php');}// 保存修改public function doUpdate() {// post请求, 修改后的球员信息在其中$player = $_POST;// 执行保存PlayerModel::savePlayer($player);}// 跳转到删除确认页面public function del() {require(dirname(__DIR__) . '/view/player/del.php');}// 执行删除球员public function doDel() {// 球员id$id = filter_var($_GET['id'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);// 球员列表的分页参数$pos = !empty($_GET['pos']) ? '?' . urldecode($_GET['pos']) : '';// 无效的id,不处理if(!$id) {echobr("<script>alert('无效的参数');window.location.href='/player/index.php/player/players{$pos}'</script>");}// 执行删除PlayerModel::delPlayer($id, $pos);}}
2.2 模型类PlayerModel.php
\view\PlayerModel.php
<?phpnamespace model;use PDO;class PlayerModel {// PDOprotected $pdo = null;public function __construct(PDO $pdo){$this->pdo = $pdo;}// 分页查询球员信息public function players($page = 1, $pageSize = 3) {// 分页起始记录偏移量$start = ($page - 1) * $pageSize;$sql = "SELECT * FROM `player` LIMIT {$start}, {$pageSize}";$players = $this->pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);return $players;}// 查询球员总数public function playerCount() {// 查询记录总数$sql = 'SELECT count(`id`) as row_count FROM `player`';$count = ($this->pdo->query($sql)->fetch(PDO::FETCH_NUM))[0] ?? 0;return $count;}// 根据id查询某个球员信息public function player($id) {$sql = "SELECT * FROM `player` WHERE `id` = ?";$stmt = $this->pdo->prepare($sql);$stmt->execute([$id]);// 查不到球员信息, 提示错误,不继续执行if ($stmt->rowCount() !== 1) {echo ("<script>alert('无效的ID值');window.history.go(-1);</script>");die;}// 获取查到的球员信息$player = $stmt->fetch(PDO::FETCH_ASSOC);return $player;}// 保存修改public function savePlayer($playerInfo) {$sql = "UPDATE `player` SET `name` = :name, `team` = :team, `height` = :height, `weight` = :weight, `position` = :position, `update_time` = :update_time WHERE `id` = :id";$param = $playerInfo;// 更新时间=当前时间$param['update_time'] = time();// 球员分页信息(返回球员列表用)$pos = $param['pos'];unset($param['pos']);$pos = empty($pos) ? '' : '?' . $pos;// PDO执行更新$stmt = $this->pdo->prepare($sql);$stmt->execute($param);// 验证更新结果if ($stmt->rowCount() === 1) {if (empty($pos))echo ("<script>alert('修改成功');window.history.back();</script>");else {$pos = urldecode($pos);echo ("<script>alert('修改成功');window.location='/player/index.php/player/players{$pos}';</script>");}} else {echo ("<script>alert('修改失败');</script>");}}// 根据球员id删除球员信息public function delPlayer($id, $pos='') {$sql = "DELETE FROM `player` WHERE `id` = :id";$stmt = $this->pdo->prepare($sql);$stmt->execute(['id' => $id]);$pos = empty($pos) ? '' : '?' . $pos;// 判断处理条数if ($stmt->rowCount() === 1) {echobr("<script>alert('删除成功');window.location.href='/player/index.php/player/players{$pos}'</script>");} else {echobr("更新失败");printfpre($stmt->errorInfo());echobr("<a href='/player/index.php/player/players{$pos}'>返回</a>");}}}
\view\facade\PlayerModel.php
<?phpnamespace model\facade;use core\Facade;class PlayerModel extends Facade {}
\core\Facade.php
<?phpnamespace core;// 门面类基类class Facade {// 无法解决创建类实例时的参数传输public static function __callStatic($name, $arguments) {return Container::make(lcfirst(ltrim(lcfirst(strrchr(static::class, '\\')), '\\')))->$name(...$arguments);}}
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>球员列表</title><style>@import '/player/style/page_style.css';@import '/player/style/page_style.css';@import '/player/style/list.css';</style></head><?php// require('/pagination.php');?><body><table cellspacing="0" align="center"><caption>球员列表</caption><thead><tr><th>ID</th><th>姓名</th><th>球队</th><th>身高(cm)</th><th>体重(kg)</th><th>位置</th><th>创建时间</th><th>修改时间</th><th>操作</th></tr></thead><tbody><?php if (!empty($players) && count($players) > 0) : ?><?php foreach ($players as $player) : ?><tr><td><?php echo $player['id']; ?></td><td><?php echo $player['name']; ?></td><td><?php echo $player['team']; ?></td><td><?php echo $player['height']; ?></td><td><?php echo $player['weight']; ?></td><td><?php echo $player['position']; ?></td><td><?php echo date('Y-m-d H:i:s', $player['create_time']); ?></td><td><?php echo date('Y-m-d H:i:s', $player['update_time']); ?></td><td><a href="/player/index.php/player/edit?id=<?php echo $player['id']; ?>&pos=<?php echo urlencode($_SERVER['QUERY_STRING']); ?>">修改</a><a href="/player/index.php/player/del?id=<?php echo $player['id']; ?>&name=<?php echo $player['name']; ?>&pos=<?php echo urlencode($_SERVER['QUERY_STRING']); ?>">删除</a></td></tr><?php endforeach; ?><?php else : ?><tr><td colspan="9">啥也没查到...</td></tr><?php endif ?></tbody></table><?php (new \view\Pagination($count, $p, $r, 5))->echoPagination();?><?php //echo (new Pagination($count, $currentPage, 3, 5));?></body></html>
edit.php
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>修改球员信息</title><style>@import url('/player/style/common.css');@import url('/player/style/edit.css');</style></head><body><section><div class="player-edit-header">修改球员信息</div><div class="player-info"><form action="/player/index.php/player/doUpdate" method="post"><input type="hidden" name="id" value="<?php echo $player['id']; ?>"><input type="hidden" name="pos" value="<?php echo $_GET['pos']; ?>"><div class="info-item"><label for="name">姓名: </label><input type="text" name="name" id="name" value="<?php echo $player['name']; ?>" required autofocus></div><div class="info-item"><label for="team">球队: </label><input type="text" name="team" id="team" value="<?php echo $player['team']; ?>" required></div><div class="info-item"><label for="height">身高(cm): </label><input type="number" name="height" id="height" value="<?php echo $player['height']; ?>" required></div><div class="info-item"><label for="weight">体重(kg): </label><input type="number" name="weight" id="weight" value="<?php echo $player['weight']; ?>" required></div><div class="info-item"><label for="position">位置: </label><input type="text" name="position" id="position" value="<?php echo $player['position']; ?>" required></div><div class="info-item"><button type="submit">保存</button></div></div></form></section></body></html>
del.php
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>确认删除</title><style>@import url('/player/style/common.css');@import url('/player/style/del.css');</style></head><body><section><span>确认要删除<strong><?php echo $_GET['name'] ?></strong>吗?</span><a href="/0512/player.php<?php echo $_GET['pos'] ? '?' . $_GET['pos'] : ''; ?>" class="btn">取 消</a><a href="do_del.php?id=<?php echo $_GET['id'] ?>&pos=<?php echo urlencode($_GET['pos']) ?>">确 认</a></section></body></html>
\view\Pagination.php
<?phpnamespace view;class Pagination{// 第一页private $start = 1;// 一页可容纳的记录数private $pageSize = 3;// 显示的页码个数private $pageNumSize = 5;// 显示的页码列表private $pageNumList = [];// 总页数private $pageCount = 0;// 页码左右偏移量private $pageNumOffset = 0;// 当前页码private $currentPage = 1;// 记录总数private $rowCount = 0;public function __construct(int $rowCount, int $currentPage = 1, int $pageSize = 3, int $pageNumSize = 5){// 初始化各种属性$this->rowCount = $rowCount;$this->pageSize = $pageSize;$this->pageNumSize = $pageNumSize;$this->pageNumOffset = ($pageNumSize - 1) / 2;$this->pageCount = ceil(floatval($rowCount) / $pageSize);/* 当传入的当前页码效于最小页码时,初始化为1;大于最大页码时,初始化为最大页码 */$this->currentPage = $currentPage < 1 ? 1 : ($currentPage > $this->pageCount ? $this->pageCount : $currentPage);$this->getPageNumList();}/*** 获取要显示的页码列表*/private function getPageNumList(){// 如果要显示的页码数量>=总页码数量,则显示所有页码。if ($this->pageCount <= $this->pageNumSize) {$this->pageNumList = range(1, $this->pageCount, 1);return;}// 起始页码,取“当前页码-页码偏移量”和起始页码的最大值。$pageNumStart = ($this->currentPage - $this->pageNumOffset) < $this->start ? $this->start : ($this->currentPage - $this->pageNumOffset);// 结束页码,取“当前页码+页码偏移量”和总页码数的最小值。$pageNumEnd = ($pageNumStart + $this->pageNumSize - 1) > $this->pageCount ? $this->pageCount : ($pageNumStart + $this->pageNumSize - 1);// 若结束页码等于总页码,则再计算一次起始页码(避免当前页到结束页码的差值小于页码偏移量的情况)if ($pageNumEnd === $this->pageCount) {// 起始页码,取“最大页码-要显示的页码数量+1”和起始页码的最大值。$pageNumStart = ($this->pageCount - $this->pageNumSize + 1) < $this->start ? $this->start : ($this->pageCount - $this->pageNumSize + 1);}// 生成要显示的页码数组$this->pageNumList = range($pageNumStart, $pageNumEnd, 1);}// 拼接字符串,形成分页HTML代码字符串。public function getPagination(){$pageHtml = '<div class="pagination">';// 首页$tmpHtml = $this->currentPage === 1 ? '<span class="pageNum disabled">首页</span>': sprintf('<a class="pageNum" href="%s?p=1&r=%d">首页</a>', $_SERVER['PHP_SELF'], $this->pageSize);$pageHtml .= $tmpHtml;// 上一页$tmpHtml = $this->currentPage === 1 ? '<span class="pageNum disabled">上一页</span>': sprintf('<a class="pageNum" href="%s?p=%d&r=%d">上一页</a>', $_SERVER['PHP_SELF'], $this->currentPage - 1, $this->pageSize);$pageHtml .= $tmpHtml;// 间隔符$tmpHtml = $this->pageNumList[0] >= 2 ? '...' : '';$pageHtml .= $tmpHtml;// 页码foreach ($this->pageNumList as $pageNum) {$tmpHtml = $pageNum == $this->currentPage ? sprintf('<span class="active">%d</span>', $pageNum): sprintf('<a class="pageNum {$pageNum}" href="%s?p=%d&r=%d">%d</a>', $_SERVER['PHP_SELF'], $pageNum, $this->pageSize, $pageNum);$pageHtml .= $tmpHtml;}// 间隔符$tmpHtml = $this->pageNumList[array_key_last($this->pageNumList)] + 1 <= $this->pageCount ? '...' : '';$pageHtml .= $tmpHtml;// 下一页$tmpHtml = $this->currentPage >= $this->pageCount ? '<span class="pageNum disabled">下一页</span>': sprintf('<a class="pageNum" href="%s?p=%d&r=%d">下一页</a>', $_SERVER['PHP_SELF'], $this->currentPage + 1, $this->pageSize);$pageHtml .= $tmpHtml;// 末页$tmpHtml = $this->currentPage >= $this->pageCount ? '<span class="pageNum disabled">末页</span>': sprintf('<a class="pageNum" href="%s?p=%d&r=%d">末页</a>', $_SERVER['PHP_SELF'], $this->pageCount, $this->pageSize);$pageHtml .= $tmpHtml;// 总页码$pageHtml .= "<span>共{$this->pageCount}页</span>";// 页码跳转表单$tmpHtml = "<form action='{$_SERVER['PHP_SELF']}' method='get'> <input type='text' name='p'><input type='hidden' name='r' value='{$this->pageSize}'><button type='submit'>跳转</button></form>";$pageHtml .= $tmpHtml;$pageHtml .= '</div>';return $pageHtml;}// 直接向浏览器输出分页信息public function echoPagination(){echo $this->getPagination();}}
autoload.php
<?php# 自动加载函数// 封装自动加载器try {// 系统函数: spl_autoload_register(), 把demo8中加载文件的代码复制过来spl_autoload_register(function ($className) {// 1. 将类名中的反斜线改为当前系统中的目录分隔符$path = str_replace('\\', DIRECTORY_SEPARATOR, $className);// echobr($path);// 2. 生成真正要加载的类文件名称/* autoload.php放到core目录中,所以需要用dirname()函数来获取上一级目录的路径 */$file = dirname(__DIR__) . DIRECTORY_SEPARATOR . $path . '.php';// echobr($file);// 3. 加载这个文件require $file;});} catch (Exception $e) {die($e->getMessage());}
\core\Container.php
<?phpnamespace core;// 服务容器类class Container {// 类实例容器protected static $instances = [];// 私有化构造方法private function __construct(){}// 把类实例的创建闭包绑定到容器中(约定别名为小驼峰类名)public static function bind($alias, \Closure $process) {static::$instances[$alias] = $process;}// 从容器中获取类实例public static function make($alias, array $param=[]) {return isset($alias) ? static::$instances[$alias](...$param) : false;}// 销毁类实例public static function destroy($alias) {unset(static::$instances[$alias]);}}
\core\Route.php
<?phpnamespace core;class Route {public static $config = ['controller_suffix' => 'Controller','action_suffix' => '','view_suffix' => 'View','model_suffix' => 'Model',];/*** 解析路由,约定:若存在路径信息,则以路径信息的前两位作为controller和action,剩下的路径信息参数跟查询参数合并作为action的* 入参;若路径信息小于2,则action由查询参数的第一个键值对指定,剩下的查询参数作为action的入参;*/public static function analysis() {// 加载配置文件$config = require((dirname(__DIR__)) . '/config.php');array_merge(static::$config, $config);// 处理路径信息$pathInfoParam = empty($_SERVER['PATH_INFO']) ? [] : static::analysisPathInfo($_SERVER['PATH_INFO']);// 处理查询字符串$queryStrParam = empty($_SERVER['QUERY_STRING']) ? [] : static::analysisQueryStr($_SERVER['QUERY_STRING']);$reqInfo = $pathInfoParam;// 路径信息和查询字符串解析结果中都没有控制器信息,则不再继续if(!isset($reqInfo['controller']) && !isset($queryStrParam['c'])) {echobr('无法处理的URL,未指定控制器');die;}// 否则,优先考虑使用路径信息中的控制器;最后再考虑使用请求参数中指定的控制器(参数名为c)if(isset($reqInfo['controller'])) {unset($queryStrParam['c']);} else {$reqInfo['controller'] = $queryStrParam['c'];unset($queryStrParam['c']);}// 路径信息和查询字符串解析结果都没有方法信息,则不再继续if(!isset($reqInfo['action']) && !isset($queryStrParam['c'])) {echobr('无法处理的URL,未指定action');die;}// 否则,优先考虑使用路径信息中的方法,最后在考虑使用请求参数中指定的方法(参数名为a)if(isset($reqInfo['action'])) {unset($queryStrParam['a']);} else {$reqInfo['action'] = $queryStrParam['a'];unset($queryStrParam['a']);}// 合并action的参数if(count($queryStrParam) > 0) {$reqInfo['param'] = array_merge($reqInfo['param'], $queryStrParam);}return $reqInfo;}// 解析路径信息private static function analysisPathInfo($pathInfo) : array {// 保存路径信息解析结果的数组$pathInfoParam = ['param' => []];// 切割字符串,形成数组$pathInfoArr = explode('/', trim($pathInfo, '/'));$paramCount = count($pathInfoArr);// 解析出来,有值,那么弹出第一个作为控制器if($paramCount > 0) {$pathInfoParam['controller'] = lcfirst(array_shift($pathInfoArr)) . static::$config['controller_suffix'];}// 值数量超过1个,则再弹出第二个作为方法if($paramCount > 1) {$pathInfoParam['action'] = lcfirst(array_shift($pathInfoArr)) . static::$config['action_suffix'];}// 值数量超过3个,则剩下的拼成参数键值对if($paramCount > 2) {for($index = 0; $index < count($pathInfoArr); $index += 2) {if(isset($pathInfoArr[$index + 1])) {$param[$pathInfoArr[$index]] = $pathInfoArr[$index + 1];}}$pathInfoParam['param'] = $param;}return $pathInfoParam;}// 解析查询参数private static function analysisQueryStr($queryStr) {// echobr($queryStr);$queryStrParam = [];parse_str($queryStr, $queryStrParam);return $queryStrParam;}}
\core\Runner.php
<?phpnamespace core;use controller\PlayerController;use model\PlayerModel;use PDO;class Runner {private static $init = false;public static function run() {// 解析路由$reqInfo = Route::analysis();// 获取控制器$controller = Container::make($reqInfo['controller']);// 执行控制器方法$action = $reqInfo['action'];//echobr($action);$controller->$action(...array_values($reqInfo['param']));}// 把创建类实例的闭包放入容器中public static function init() {if(self::$init) return;// pdo$alias = 'pdo';Container::bind($alias, function() {return new PDO('mysql:host=localhost;dbname=phpedu;charset=utf8;port=3306', 'root', 'root');});// 默认用类的小驼峰命名作为别名$alias = lcfirst(ltrim(strrchr(PlayerModel::class, '\\'), '\\'));Container::bind($alias, function () {$pdo = Container::make('pdo');return new PlayerModel($pdo);});// 控制器$alias = lcfirst(ltrim(strrchr(PlayerController::class, '\\'), '\\'));Container::bind($alias, function() {return new PlayerController;});self::$init = true;}}
config.php
<?phpreturn ['controller_suffix' => 'Controller',// 控制器后缀'action_suffix' => '',// 方法后缀'view_suffix' => 'View',// 视图后缀'model_suffix' => 'Model',// 模型后缀];
index.php
<?phpuse core\Runner;require('../out.php');require('core/autoload.php');// 前端控制器// 把创建类闭包放入容器Runner::init();// go! go! go!Runner::run();
运行结果:
与查询分类作业一样: 查询分类作业链接
把路由, 服务容器和MVC框架的作业用一个小实例实现了.
实现过程遇到的问题:
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号