资源描述
PHP 【 中级篇 】 项目二:内容管理系统 模块五 后台功能实现 MySQL安装与使用 HTTP、会话技术 PHP操作数据库 文件、图像技术 任务一 任务二 任务三 项目准备 管理员登录 栏目管理 任务四 任务五 文章管理 排序与搜索 任务五 分页导航 目录 在项目开发的初始阶段,先进行项目的目录结构划分,才能合理、规范地 管理项目中的各种文件。根据功能模块划分,本项目的目录结构如下所示。 项目初始化 任务一:项目准备 项目初始化 文件 说明 common 前后台 公共文件目录 upload 前后台 上传文件目录 css 前台 CSS样式文件目录 js 前台 JavaScript文件目录 image 前台 图片文件目录 view 前台 HTML模板文件目录 init.php 前台 初始化文件 index.php 前台 首页 文件 说明 admin 后台 文件目录 admincss 后台 CSS样式文件目录 adminjs 后台 JavaScript文件目录 adminimage 后台 图片文件目录 adminview 后台 HTML模板文件目录 admininit.php 后台 初始化文件 adminindex.php 后台 首页 任务一:项目准备 项目初始化 项目首先分成了前台和后台两个平台 将 common和 upload目录作为前后台公共目录 后台相关的文件全部放到了 admin目录中 从而在目录结构上对前后台进行了区分 任务一:项目准备 项目初始化 在完成目录划分后,接下来为项目的前后台创建初始化文件,为项目定义一 些基础的常量,方便在项目中使用。 任务一:项目准备 项目初始化 为前台创建初始化文件 init.php 定义常量 APP_DEBUG,用于表示是否开启调试,其值可以为 true和 false。 当开启时将提示完整的错误信息以便于调试,否则只进行简单的错误提示。 定义常量 COMMON_PATH和 UPLOAD_PATH,用于表示公共文件目录和上 传文件目录的路径。 从前台进行访问时,从当前目录“ ./”开始。 任务一:项目准备 项目初始化 为后台创建初始化文件 admininit.php 定义常量 APP_DEBUG,用于表示是否开启调试,其值可以为 true和 false。 当开启时将提示完整的错误信息以便于调试,否则只进行简单的错误提示。 定义常量 COMMON_PATH和 UPLOAD_PATH,用于表示公共文件目录和上 传文件目录的路径。 从后台进行访问时,从上级目录“ ./”开始。 任务一:项目准备 函数库与配置文件 函数库 在项目开发时,有许多常用的功能可以通过函数来完成,因此应该为项目创 建一个函数库,保存项目中的常用函数。 常用函数的名称,通过一个大写字母的命名风格,既书写方便,又便于程序 阅读。 任务一:项目准备 函数库与配置文件 函数库 在 common目录中创建文件 function.php,编写一个“ E()”函数。 函数名是英文单词“ error”的首字母缩写,表示程序遇到错误。 该函数有两个参数 $msg和 $debug,参数 $debug的默认值为空字符串 该函数的参数 $msg表示错误信息, $debug表示调试信息 当开启调试时,显示错误信息和调试信息,而关闭调试时,只显示错误信息 在完成编写函数库后,接下来在项目的初始化文件中载入函数库 任务一:项目准备 函数库与配置文件 配置文件 在项目中通常有一些常用的配置,如数据库连接信息,使用独立的配置文 件来保存配置可以使代码更利于维护。 任务一:项目准备 函数库与配置文件 配置文件 接下来在 common目录中创建配置文件 config.php,保存数据库的连接信息。 在 config.php中,直接返回数组,数组中的元素用于设置项目的配置信息 DB_CONNECT元素的值是一个数组,用于保存数据库的连接信息,如 数 据库服务器地址、用户名、密码、默认数据库和端口号 。 DB_CHARSET元素用于保存数据库字符集 任务一:项目准备 函数库与配置文件 访问配置文件 完成配置文件 commonconfig.php的创建后,接下来还需要对配置文件进 行访问。 任务一:项目准备 函数库与配置文件 访问配置文件 在函数库文件 commonfunction.php中编写函数 C()实现此功能。 函数名是英文字母“ config”的首字母缩写,表示访问程序的设置。 参数 $name表示待访问的数组元素,如 DB_CONNECT。 在 C()函数中设置静态变量 $config,用于保存项目配置信息 判断 $config是否为 null,若为 null,则载入配置文件 判断要获取的配置信息是否存在,若不存在赋值为空字符串 返回要获取的配置信息 任务一:项目准备 函数库与配置文件 示例演示 在后台初始化文件 admininit.php中,添加以下代码用于测试函数的使用。 /访问数据库连接信息 $config = C(DB_CONNECT); echo $confighost; /输出结果: localhost /访问数据库字符集 echo C(DB_CHARSET); /输出结果: utf8 任务一:项目准备 数据库函数 在基于数据库的项目中,对数据库的操作是非常频繁的,因此可以利用函数 将这部分代码提取出来,以方便后续的代码编写。 接下来在 common目录中创建 db.php,保存数据库的常见操作函数 任务一:项目准备 数据库函数 连接数据库 编写 db_connect()函数,用于完成数据库连接和设置字符集操作 设置静态变量 $link用于保存数据库连接 实现 函数第一次调用时进行数据库连接 实现 函数执行后返回数据库连接 $link 任务一:项目准备 数据库函数 执行 SQL语句 在完成数据库连接后接下来就可以执行 SQL语句。由于 MySQLi扩展提供 的预处理语句更加高效和安全,因此在项目中执行 SQL语句时,将通过预处理 来实现。 任务一:项目准备 数据库函数 执行 SQL语句 编写函数 db_query(),完成 SQL语句的预处理操作 该函数的 参数依次为 SQL语句、数据格式 和 数据内容 。其中,数据格式的默 认值为空字符串、数据内容的默认值为空数组。 获取数据库连接后,利用 mysqli_prepare()预处理 SQL语句,获取 $stmt 数据内容为空,利用 mysqli_stmt_execute()直接执行 SQL语句。 数据内容不为空,则执行自定义函数 db_bind_param()进行参数绑定后,再 执行 SQL语句 最后返回预处理结果 $stmt 任务一:项目准备 数据库函数 执行 SQL语句 编写函数 db_bind_param(),完成 SQL语句的参数绑定 该函数的 参数依次 $stmt、数据格式和引用传递的数据内容 利用 $params保存预处理函数 mysqli_stmt_bind_param()需要的每个参数 首先依次保存前两个参数 $stmt和数据格式 由于 mysqli_stmt_bind_param()的参数绑定需要引用传参,因此通过遍历数 据内容,为每个参数创建引用,并依次赋值给 $param 最后调用 mysqli_stmt_bind_param()函数完成参数的绑定 任务一:项目准备 数据库函数 示例演示 /准备 SQL语句 $sql = INSERT INTO cms_category (name, sort) VALUES (?, ?); /执行 SQL语句 db_query($sql, si, test, 0); 第二个参数 si,表示插入数据的类型 s表示字符串 i表示整型 任务一:项目准备 数据库函数 批量操作 在 db_query()函数中,第 3个参数用于传入数据内容,目前只支持一维数组。 MySQLi扩展的预处理机制还可以实现批量操作。 因此,还可以改进 db_query()函数,当传入数据内容是二维数组时,自动 进行批量操作。 任务一:项目准备 数据库函数 批量操作 在 db_query()函数实现参数绑定的位置进行修改 首先进行预处理操作的参数绑定并执行预处理操作 然后删除执行完的数据 最后批量执行剩余的其他数据 任务一:项目准备 数据库函数 示例演示 /准备 SQL语句 $sql = INSERT INTO cms_category (name, sort) VALUES (?, ?); /准备数据 $data = aa, 1, bb, 12, cc, 30; /执行 SQL语句 db_query($sql, si, $data); 任务一:项目准备 数据库函数 其他数据库操作 在数据库操作中,除了执行 SQL语句,还需要处理结果集、获取受影响行 数、获取最后插入的 ID等后续操作。接下来继续完善数据库操作函数 。 任务一:项目准备 数据库函数 其他数据库操作 定义常用常量,用于表示后续的具体操作 定义函数 db_fetch(),处理有结果集的 SQL语句;定义函数 db_exec(),处理 没有结果集的 SQL语句。 两个函数的参数依次为后续的操作, SQL语句,数据格式和数据内容 最后结合 switch语句根据不同的操作,执行不同的处理函数 任务一:项目准备 数据库函数 示例演示 / 查询数据,获得保存所有结果的关联数组 $data = db_fetch(DB_ALL, SELECT * FROM cms_category); var_dump($data); / 插入数据,获得最后插入的 ID $sql = INSERT INTO cms_category (name, sort) VALUES (?, ?); $id = db_exec(DB_LASTID, $sql, si, test, 0); echo $id; 任务一:项目准备 输入过滤函数 在项目开发中,对于 $_GET、 $_POST数组的访问是非常频繁的,但是这两 个数组保存的是来自外部提交的数据,如果直接进行访问,会带来安全隐患。 因此,通过函数来统一接收外部变量,可以使项目代码更加严谨和规范。接 下来,在 commonfunction.php中编写 I()函数用于接收外部变量 。 任务一:项目准备 输入过滤函数 该函数名取自英文单词“ input”的首字母,表示输入。 该函数的参数依次为需要处理的变量名、接收方法、数据类型、默认值 根据接收方法确定数据变量使用哪种接收类型 通过 isset()判断接收的数据变量是否存在,若不存在将其值设为默认值 结合 switch和数据类型进行不同的过滤处理,具体有字符串型、 html转义、整 数、无符号整数、页码、浮点数、布尔型、数组型。 任务一:项目准备 输入过滤函数 结合 htmlspecialchars()函数把一些预定义的 HTML 实体转换为字符,并设置 第二个参数 ENT_QUOTES完成对单引号和双引号的转义操作。 结合 trim()函数去除字符串首尾处的空白字符(或者其他字符) 结合 str_replace()函数将字符串中的空格替换成 其中 , 在对数据进行 HTML特殊字符转义时,为方便管理和操作,接下来在 commonfunction.php中实现该函数 toHTML()。具体思路如下: 任务一:项目准备 输入过滤函数 示例演示 / POST方式 $_POSTname = 测试 ; $name = I(name, post, html); echo $name; /输出结果:测试 文本 / GET方式 $_GETid = 123abc; $id = I(id, get, id); echo $id; /输出结果: 123 任务一:项目准备 后台页面布局 下面开始进入网站后台页面的开发。在设计后台页面时,通常使用“品” 字形的页面布局,此布局的具体结构如图 。 t o p n a v c o n t e n t top是页面的顶部,通常用于显示系统 名称、相关链接; nav是页面的左侧导航菜单,后台中的 各个功能模块通过这个菜单进入; content是页面内容,根据当前访问的 功能而改变。 任务一:项目准备 后台页面布局 在 adminview目录中创建后台的布局文件 layout.html,页面顶部和 左侧导航 标签中添加响应 ,页面内容部分嵌套一个框架,用于动态改变内容 创建后台首页 adminindex.php,载入后台初始化文件和布局文件 接下来 在当前目录下, 创建 admincp_index.php文件 ,载入后台首页信息 创建文件 adminviewindex.html,该文件是后台首页的 HTML模板文件 任务一:项目准备 后台页面布局 后台页面布局展示图 任务一:项目准备 实现管理员登录 添加管理员信息 在管理员表中添加初始数据,具体字段应包括 ID、用户名和密码, SQL 语句如下。 INSERT INTO cms_admin VALUES (1, admin, 123456); 任务二:管理员登录 实现管理员登录 创建后台登录表单 在后台 adminview中创建后台用户登录的 HTML表单 login.html。 用户名: 密 码: 任务二:管理员登录 实现管理员登录 载入数据库操作函数 修改前后台的初始化文件 admininit.php,载入数据库操作函数 db.php。 将数据库操作函数载入后,在项目中就可以使用来自 db.php中的函数。 任务二:管理员登录 实现管理员登录 接收登录表单 创建后台登录文件 adminlogin.php,实现载入 HTML模板显示登录页面,当 接收到提交的登录表单时处理表单。 任务二:管理员登录 实现管理员登录 接收登录表单 载入后台初始化文件 init.php和表单模板文件 login.html 通过 if与 $_POST处理提交后的表单内容 在处理表单时,先通过 I()函数接收用户名和密码,到数据库中查询信息,然后取 出密码后进行验证。 如果验证通过,则将用户登录信息保存到 Session中,然后利用自定义 redirect() 函数跳转到后台首页 index.php 如果验证失败,则调用 E()函数停止程序继续执行。 任务二:管理员登录 实现管理员登录 接收登录表单 接下来在 commonfunction.php中编写用于实现页面跳转的 redirect()函数。 function redirect($url) header(Location:$url); /重定向到目标 URL地址 exit; 任务二:管理员登录 页面信息提示 当 PHP处理用户提交表单时,如果在处理过程中发生了成功或失败的信息,应 该以友好的提示信息在页面中显示。 为了利于程序的维护,可以将载入 HTML页面的代码放到一个函数中,通过调 用函数的方式来决定程序在什么情况下显示页面。 任务二:管理员登录 页面信息提示 编写显示页面的函数 接下来在 adminlogin.php中编写 display()函数,将载入 HTML模板的代码放 到函数中 。 function display($msg=null) require ./view/login.html; exit; 任务二:管理员登录 页面信息提示 在页面中输出提示信息 编辑后台登录页面 adminviewlogin.html,在页面中添加 元素用于 显示信息提示。 任务二:管理员登录 页面信息提示 在页面中输出提示信息 在 commonfunction.php中编写该函数 tips()输出提示信息。 function tips($msg=null) if(!$msg) return ; /没有提示信息时直接返回空字符串 return $msg0 ? $msg1 : $msg1; tips()函数的参数 $msg用于传入一个数组,数组的第 1个元素表示成功或失败 第 2个元素是表示提示信息 当省略参数 $msg,或 $msg为空时,直接返回空字符串,表示没有提示信息 任务二:管理员登录 页面信息提示 显示页面并提示信息 在完成信息提示功能后,修改 adminlogin.php中登录失败的提示代码 /修改前的代码: /E(登录失败:用户名或密码错误。 ); /修改后的代码: display(false, 登录失败:用户名或密码错误。 ); 任务二:管理员登录 页面信息提示 没有表单提交时显示页面 在完成创建 display()函数后,当没有表单提交时,应显示登录页面 if($_POST) /有表单提交时,处理表单 else /没有表单提交时,显示登录页面 display(); 任务二:管理员登录 页面信息提示 效果展示 任务二:管理员登录 判断登录状态 在实现了用户登录功能后,还需要判断用户是否登录,如果没有登录则提 示用户进行登录,并阻止用户访问本来的功能。 任务二:管理员登录 判断登录状态 在后台中, Session操作是项目中的公共功能,为了更好地维护项目中的 Session,可以在初始化文件中统一开启 Session,并为项目中的 Session创建前 缀。修改文件 admininit.php,具体如下: 开启 Session /启动 session session_start(); /为项目创建 Session,统一保存到 cms中 if(!isset($_SESSIONcms) $_SESSION = cms = ; 任务二:管理员登录 判断登录状态 接下来在 admininit.php中继续编写代码,实现检查用户登录。 检查用户登录 判断是否已经定义了 NO_CHECK_LOGIN常量 如果没有定义则检查用户是否登录,如果已经定义了则不检查用户是否登录 当用户登录时,取出用户信息保存到变量 $user中 如果没有登录则跳转到登录页面 login.php并停止脚本继续执行 当其他脚本载入这个文件时,就会自动判断用户是否登录,而如果不需要判断 登录,则在载入 admininit.php之前,先定义 NO_CHECK_LOGIN常量。 任务二:管理员登录 判断登录状态 在 adminlogin.php文件中,当用户登录成功时,将用户信息( ID和用户名)保 存到了 Session中。 在 admininit.php中判断用户是否登录,如果已经登录,则从 Session中取出用 户信息,保存到 $user中。 当用户登录成功后进入后台首页时,可通过输出 $user将用户名显示在页面中。 显示当前登录的用户名 任务二:管理员登录 判断登录状态 效果展示 任务二:管理员登录 登录验证码 在开发管理员登录功能时,还要考虑一个问题,就是除了浏览器,其他软 件也可以向服务器提交数据。 从系统安全的角度看,如果使用软件自动大批量向服务器提交表单,那么 管理员的用户名、密码将会被穷举出来,导致管理员账号被盗取。为此,验证 码就是一种防御的手段。 任务二:管理员登录 登录验证码 通常情况下,验证码是一张带有文字的图片,要求用户输入图中的文字。 对于图片中的文字,人类识别非常容易,而软件识别非常困难。 因此,验证码是一种区分人类和计算机的程序。接下来,就在项目中实现 验证码的功能。 任务二:管理员登录 登录验证码 生成验证码文本 在项目中创建 commoncaptcha.php,该文件用于保存验证码相关的函数。 编写 captcha_create()函数 , 用于生成指定位数的验证码 该函数有一个参数,用于 表示生成 文本的 位数 ,默认值为 5 从 $charset字符串中随机取出 $count个字符,保存到 $code中 调用该 函数后,返回生成的验证码文本 任务二:管理员登录 登录验证码 生成验证码图像 通过 PHP提供的 GD库扩展,可以绘制一张图片。在绘制图片时,可以将文 本写入到图片中 。 任务二:管理员登录 登录验证码 生成验证码图像 在 commoncaptcha.php中继续编写代码,实现输出验证码图像的函数。 编写 captcha_show()函数 , 用于验证码图像 该函数有一个参数,用于表示验证码文本 创建图片资源,随机生成背景颜色, 设置字体颜色和样式 生成指定长度的验证码 添加 8个干扰线 和 250个噪点 向浏览器输出 PNG格式的 验证码图片 任务二:管理员登录 登录验证码 调用验证码函数 在完成了验证码文本和图像生成的函数后,接下来在后台中调用函数显示 验证码。 创建文件 admincaptcha.php,完成验证码函数的调用 载入验证码函数 文件 commoncaptcha.php 生成验证码值 输出验证码图像 , 将验证码保存到 Session中 任务二:管理员登录 登录验证码 显示验证码 在完成验证码的调用后,接下来在后台管理员登录的表单中显示验证码。修 改登录表单 adminviewlogin.html,在表单中使用 载入验证码图片 。 验证码: 任务二:管理员登录 登录验证码 效果展示 任务二:管理员登录 登录验证码 判断验证码 在用户提交表单后,在判断用户名和密码之前,应该先判断验证码是否正确。 如果验证码有误,则没有必要继续判断用户名和密码。 任务二:管理员登录 登录验证码 判断验证码 通过 I()函数获取用户输入的验证码 载入验证码函数 调用自定义函数 checkCode()验证用户输入的验证码是否正确 若验证错误,则显示验证码验证失败 若验证正确,接着继续验证用户名和密码是否正确 任务二:管理员登录 登录验证码 判断验证码 接着自定义函数 checkCode() 该函数的参数是用户输入的验证码 $code 接着取出保存到 Session中的验证码 $captcha 在 $captcha 不为空的情况下,为防止重复验证,清除验证码 在不区分大小写的情况下,返回 $code和 $captcha 的比较结果 在 $captcha 为空的情况下,直接返回 false 任务二:管理员登录 退出登录 在完成管理员登录功能后,还需要开发管理员退出功能。 编辑 adminviewlayout.html文件,在显示用户信息的位置,添加一个退出登 录的链接 。 您好, 前台首页 退出 任务二:管理员登录 退出登录 接下来在 adminlogin.php中接收参数,实现退出功能。 /接收操作参数 $action = I(a, get, string); /执行操作 if($action=logout) /退出登录 unset($_SESSIONcmsadmin); /清除 Session display(true, 您已经成功退出。 ); 任务二:管理员登录 读取栏目 准备测试数据 在管理员登录后,就可以对栏目进行管理。在项目数据库中,为栏目表添 加测试数据,用于读取栏目功能的开发。添加测试数据的 SQL语句如下。 INSERT INTO cms_category (id, pid, name, sort) VALUES (1, 0, PHP, 0), (2, 0, Java, 1), (3, 1, PHP基础 , 0), (4, 1, PHP高级 , 1); 任务三:栏目管理 读取栏目 读取栏目数据 在项目中,读取栏目数据的需求可能会频繁出现,因此将此功能写在函数中。 在 common目录下创建文件 module.php,用于保存和数据相关的功能模块函数。 任务三:栏目管理 读取栏目 读取栏目数据 定义函数 module_category(),用于获取栏目列表 该函数的参数 $mode表示索引方式: id 或 pid,默认返回两种格式 定义一个静态变量 $result,用于缓存查询结果 当第一次调用函数时,到数据库中获取数据,并分别根据 id和 pid 创建数组索引,方便查找。 最后根据索引方式返回查询结果 任务三:栏目管理 编辑栏目 输出已有栏目 在项目中创建 cp_category.php文件,该文件用于读取栏目数据显示在 HTML模板中。 在该文件中载入初始化文件 接着定义 display()函数,显示页面 从数据库中根据 pid取出数据,载入 HTML模板文 adminviewcateogory.html 调用函数 display() 任务三:栏目管理 编辑栏目 输出已有栏目 接下来编写用于显示栏目的 adminviewcateogory.html文件。为了提高后台 管理的操作效率,可以将栏目显示、添加、修改功能都在一个页面中完成。 pid的顶级分类为 0 外层循环输出顶级栏目 接着判断该分类下是否存在子栏目,若存在则循环输出 任务三:栏目管理 编辑栏目 效果展示 任务三:栏目管理 编辑栏目 添加栏目 在完成已有栏目的输出后,还需要开发栏目添加功能,在实现栏目添加时,为 了更直观地在页面中添加栏目和子栏目,这里通过 jQuery实现了页面的灵活处理。 编辑 adminviewcp_category.html文件,在页面底部添加 JavaScript代码如下。 任务三:栏目管理 编辑栏目 添加栏目 当单击页面中的 class属性为 jq-add的元素时,就会触发点击事件 在该元素的前面添加 HTML内容,内容是添加新栏目的输入框 对于添加表单的 name属性,这里使用了名称为 add的二维数组,其外层用于 区分多个添加的内容,内层是 sort、 name、 pid三个字段 由于是顶级栏目,所以 pid的值为 0 顶级分类栏目添加 任务三:栏目管理 编辑栏目 添加栏目 当单击页面中的 class属性为 jq-sub-add的元素时,就会触发点击事件 为“添加子栏目”元素添加 data-id属性,用于保存子栏目的上级栏目 ID。 添加子栏目的事件函数中应该先获取到此 ID,然后保存到隐藏域的 pid字段中。 二级分类栏目添加 任务三:栏目管理 编辑栏目 效果展示 任务三:栏目管理 批量保存 接收表单 调用 addData()函数添加栏目,调用 saveData()函数修改栏目 实现接收表单并处理 批量添加 添加栏目的信息保存在了 add二维数组中 利用 I()函数接收数组后保存到数据库中即可 批量修改 批量修改的实现方式和批量添加类似 在接收表单时,修改栏目的信息保存在了 save数组中 任务三:栏目管理 修改层级 添加编辑链接 为每个栏目添加“编辑”超链接,链接到 cp_category_edit.php文件 同时,传递参数 ID,表示编辑指定 ID的栏目 取出指定栏目信息 先取出待编辑栏目的信息,然后取出所有顶级栏目信息 保存信息 在 admincp_category_edit.php中 接收表单数据,将信息保存到数据库中 任务三:栏目管理 删除栏目 添加删除链接 在开发删除功能时,为了防止误操作,在执行操作前进行弹框提示 执行操作 判断 待删除 栏目下是否有子级栏目, 若 有则不执行删除操作; 若 没有 则 删除 任务三:栏目管理 删除栏目 效果展示 任务三:栏目管理 删除栏目 令牌保护 在前面实现删除功能时,通过一个 URL地址直接实现了删除数据,然而这 种方式在 Web开发中存在安全隐患。 当管理员在登录系统的状态下进行其他操作时,如果访问了其他用户恶意 构造的危险 URL地址,就会导致后台的操作被执行,这种安全漏洞称为 CSRF (跨域请求伪造)。 任务三:栏目管理 删除栏目 令牌保护 防御 CSRF安全问题的一个有效的措施,是为所有涉及更改数据的操作加上 令牌保护,该令牌将在用户登录时随机生成,每个更改的操作都附加上令牌, 没有令牌时将无法执行操作。 任务三:栏目管理 删除栏目 令牌保护 token_get():利用 md5()和 microtime(true)生成令牌, 将其保存在 Session中 token_check():先从 GET参数中取出 token,然后再与保存到 Session中的 token 进行比较,判断是否正确。如果令牌有误,说明用户当前的操作是非法的。 在 commonfunction.php中,自定义 token_get()和 token_check()函数 任务三:栏目管理 删除栏目 令牌保护 在 admininit.php中添加代码实现令牌的自动生成和验证 对删除栏目的操作添加令牌验证 需要注意的是,为了避免项目中频繁的令牌验证影响代码演示,在 本书后面的开发步骤中并没有加上令牌验证功能。同时为了确保项 目的严谨性,在本书的配套源代码中已经全部加上了令牌验证。 任务三:栏目管理 文章列表 准备测试数据 在开发具体功能前,先向数据库中插入测试数据 查询文章列表 通过文章表和栏目表的左连接查询,从数据库中获取到文章列表, 在列表中包含了文章所属栏目的信息 展示文章列表 编写代码输出文章列表 任务四:文章管理 文章列表 效果展示 任务四:文章管理 编辑文章 添加编辑链接 当单击“编辑”链接时,访问 cp_article_edit.php并传入参数 id 查询指定文章信息 接收 文章 id,显示修改页面 如果没有收到,或 id的值为 0时,直接显示空表单,用于发布新文章 显示文章编辑表单 创建一个表单显示文章编辑内容 输出栏目列表 下面将栏目输出到一个下拉菜单中 任务四:文章管理 编辑文章 引入在线编辑器 在 CKEditor的官方网站可以下载此编辑器 将编辑器放入到项目的 adminjs目录中,并将编辑器目录命名为“ ckeditor” 在文章编辑页面 adminviewarticle_edit.html的底部编写 JavaScript代码, 实现在线编辑器的引入 。 修改容器的 name属性值 任务四:文章管理 编辑文章 效果展示 任务四:文章管理 保存文章 接收表单 接收用户填写的基本信息 接收由在线编辑器提交的内容时,没有进行 HTML转义,这是因为 编辑器提交内容的本来就是 HTML。 从安全角度来说,服务器端不进行 HTML转义会带来安全问题,原因是浏览 器端任何限制都可以被绕过。 在项目开发时,可以考虑使用富文本过滤器(如 HTML Purifier)对 HTML内 容进行安全过滤,这部分内容将在后面的项目中进行讲解。 任务四:文章管理 保存文章 实现文章修改 在执行接收表单后的操作时,如果接收到文章 ID,就执行文章修改操作。 实现文章添加 在处理表单时,如果没有接收到文章 ID,就执行文章添加操作。 任务四:文章管理 上传封面图 检查上传文件 在进行添加或修改数据之前,先处理上传文件 在 commonfunction.php中,自定义 check_upload()函数 , 用于判断上传文 件是否成功,成功时返回 true,失败时将错误信息保存到 $error中 准备缩略图函数 为图片生成缩略图 image_thumb 根据原图文件创建图像资源 image_create 保存图像资源 image_save 任务四:文章管理 上传封面图 获取图片信息 实现 image_thumb()函数的功能,完成缩略图的生成 实现图片等比例缩放 通常会保持图片比例,防止图片被拉伸或者压扁,影响图片的美观 生成保存路径 可以根据日期为图片生成子目录,并为文件自动生成文件名,防止文件名冲突 调用缩略图函数 在调用时,需要指定原图路径、缩略图宽高、上传目录等信息 任务四:文章管理 上传封面图 效果展示 任务四:文章管理 删除文章 添加删除链接 该功能和栏目删除时的代码相同 执行删除操作 当用户单击删除链接后,就会向 PHP脚本发送待删除的文章 ID参数 在实际进行文章记录删除操作之前,需要先判断文章是否有封面图。 如果封面图存在,就先删除图片文件,再删除文章记录 任务四:文章管理 列表功能区 准备排序条件 在 admincp_article.php文章列表程序中编写代码,获取列表相关的 GET参 数,并定义排序的 方式以及对应的 SQL语句 显示列表功能区 栏目筛选和列表排序功能是两个下拉菜单 列表搜索功能是一个文本框 三个功能各放在三个表单中,单击表单提交按钮即可完成对应的功能操作。 任务五:排序与搜索 列表功能区 效果展示 任务五:排序与搜索 组合 SQL语句 组合 ORDER和 WHERE子句 在前面的步骤中,已经使用 $cid、 $search、 $order三个变量接收了来自表单提 交的数据。接下来就可以根据这些数据组合 SQL语句进行查询。在文章列表功能 admincp_article.php中继续编写代码,在接收变量后组合 SQL语句。 /拼接排序条件 $sql_order = ORDER BY $sql_order .= isset($order_arr$order) ? $order_arr$ordersql : a.id DESC; /拼接 WHERE条件 $sql_where = WHERE 1=1 ; $sql_where .= $cid ? AND a.cid IN (.module_category_sub($cid).) : ; $sql_where .= AND a.title LIKE ? ; $sql_search = %.db_escape_like($search).%; 任务五:排序与搜索 组合 SQL语句 根据栏目 ID取出所有子栏目 ID 下面在 commonmodule.php文件中编写 module_category_sub()函数,实现 根据栏目 ID取出所有子栏目 ID的功能。 function module_category_sub($id) $data = module_category(pid); $sub = isset($data$id) ? array_keys($data$id) : ; array_unshift($sub, $id); /将 $id放入数组开头 return implode(, $sub); 任务五:排序与搜索 组合 SQL语句 转义 LIKE搜索字符串 接下来在 commondb.php文件中编写 db_escape_like()函数,用于实现转义 LIKE搜索字符串中的所有特殊字符。 function db_escape_like($like) return strtr($like, %=%, _=_, =); 在单引号字符串中书写“ ”字符时,如果一个“ ”后面跟一个单引号,单引 号将会被转义成字符串中的字符,而非字符串定界符,因此需要在“ ”前面加一 个“ ”进行转义(字符串中实际只保存了一个“ ”字符)。 任务五:排序与搜索 组合 SQL语句 修改文章列表查询 SQL 在完成对 ORDER和 WHERE的组合后,接下来继续编写 admincp_article.php, 修改查询文章列表数据的代码,将筛选和排序条件加入到 SQL语句中。 $data = db_fetch(DB_ALL, SELECT a.id, a.cid, a.title, a.author, a.show, a.time, c.name AS cname FROM cms_article AS a LEFT JOIN cms_category AS c ON a.cid=c.id. $sql_where $sql_order, s, $sql_search); 任务五:排序与搜索 组合 SQL语句 效果展示 任务五:排序与搜索 分页显示信息 分页查询原理 实现分页的原理是对 SQL语句中的 LIMIT进行控制,示例代码如下。 SELECT title FROM cms_article LIMIT 0, 10; # 获取第 1页的 10条数据 SELECT title FROM cms_article LIMIT 10, 10; # 获取第 2页的 10条数据 SELECT title FROM cms_article LIMIT 20, 10; # 获取第 3页的 10条数据 SELECT title FROM cms_article LIMIT 30, 10; # 获取第 4页的 10条数据 任务六:分页导航 分页显示信息 分页查询原理 LIMIT的第 2个参数“ 10” 表示每次读取的最大条数;第 1个参数与页码之 间存在一定的数学关系,具体如下: LIMIT 第 1个参数 = (页码 - 1) * 每页查询的条数 任务六:分页导航 分页显示信息 分页查询原理 根据上述条件,接下来在 common目录中创建 page.php,用于实现分页功 能。下面在文件中编写用于生成 LIMIT参数的函数。 ?php /获取 SQL分页 Limit( $page表示页码, $size表示每页查询条数) function page_sql($page, $size) return ($page-1) * $size . , . $size; 任务六:分页导航 分页显示信息 实现分页查询数据 接下来在文章列表功能 admincp_article.php文件中实现分页查询,在 display()函数中查询文章列表数据之前 , 编写代码定义每页显示的记录数和 页码,并调用 page_sql函数生成 LIMIT参数 。 任务六:分页导航 分页显示信息 实现分页查询数据 $page = I(page, get, page); /获取页码(限制最小值为 1) $page_size = 3; /每页显示 3条记录 require COMMON_PATH.page.php; /载入分页函数 $sql_limit = LIMIT .page_sql($page, $page_size); /拼接 LIMIT /获取文章列表时,将 LIMIT放入 SQL语句中 $data = db_fetch(DB_ALL, SELECT a.id, a.cid, a.title, a.author, a.show, a.time, c.name AS cname FROM cms_article AS a LEFT JOIN cms_category AS c ON a.cid=c.id. $sql_where $sql_order $sql_limit, s, $sql_search); 任务六:分页导航 生成分页导航 获取总页数 查询出符合 $sql_where条件的总记录数,通过总记录数和每页显示的记录数, 即可计算出总页数,计算公式为“总记录数每页数量”,然后向上取整。 例如,总记录数为 7,每页显示 3条,则 7除以 3的结果大于 2且小于 3,超出第 2页的记录在第 3页显示,因此总页数为 3。 任务六:分页导航 生成分页导航 生成分页导航 生成分页导航的原理是,根据当前页码和总记录数,计算出“上一页”、 “下一页”、“尾页”的页码值。 其中,为了 在输出分页链接时携带 GET参数 ,获取原来所有的 GET参数, 清 除原来的 page参数 , 重新构造参数字符串 并返回。 在 commonpage.php中编写程序,实现分页导航的自动生成 。 任务六:分页导航 生成分页导航 效果展示 任务六:分页导航
展开阅读全文