第5章内容管理系统

内容管理系统(Content Management System,CMS)是互联网上对信息进行分类管理,并快捷更新的Web应用系统。它能够将杂乱无章的信息,及时、准确、有序地呈现在网络用户面前,使信息的共享更加快捷和方便。
本章通过一个简单的内容管理系统的开发,向读者介绍PHP Web应用程序开发的基本方法、技术规范,以及相关的开发技术。


视频讲解


5.1项 目 简 介
CMS应用非常广泛,例如企业网站、政府信息平台等。在网络中,常见的门户、新闻、博客、文章等类型的网站,以及各类信息、数据查询系统,都可以利用CMS进行开发。
5.1.1功能描述
作为第一个项目,通过该项目的开发,熟悉PHP Web应用项目开发的各项技术,进一步巩固PHP程序设计语言的语法及编程规范并能综合应用基础理论知识,解决实际的工程、技术或应用问题。鉴于此目的,本项目采用面向过程的程序设计方法,实现内容管理系统的基本功能,包括前台展示功能与后台管理功能。
前台功能包括内容的列表展示、内容的分类展示、内容的详情展示、内容的搜索,以及用户的注册与登录等。
后台功能包括用户管理、内容管理、分类管理等。
5.1.2运行预览
本章项目的完整运行效果,请读者自行运行本书提供的源码,项目目录名称为chap05。这里只展示前台首页、内容详情、分类查询、后台主页、后台用户管理页面的运行效果,如图5.1~图5.5所示。






图5.1前台主页





图5.2内容详情






图5.3分类查询






图5.4后台主页





图5.5用户管理


其他页面效果请运行源码,本章项目源码目录为chap05。运行时请先阅读案例项目运行说明。
5.2项 目 准 备
Web项目的开发是一项复杂的系统工程,一般包括系统分析、系统设计、系统实现,以及系统测试等诸多环节,其中,系统分析与系统设计需要在项目的准备阶段完成。


视频讲解





图5.6项目目录结构


由于篇幅的限制,这里只简单地介绍一下系统设计,项目的系统分析,例如需求分析、可行性分析、项目计划书的编写等工作,读者可自行参考其他技术文档学习完成。
5.2.1系统初步设计
系统初始设计包括确定系统目标、系统功能、数据库类型,以及系统开发框架等内容。下面简单介绍项目的创建、静态资源的准备以及初始化文件设计等内容。

1. 创建PHP项目
创建一个PHP本地项目,项目名称为“微梦内容管理系统”,项目目录为chap05,对应的虚拟主机为book.php.chp05。项目目录结构如图5.6所示。


该项目分为前台与后台两个模块,其中,前台文件存放在项目根目录下的home子目录中,后台模块文件存放在admin目录下。项目根目录下的common与public子目录中存放前后台模块的公用文件。项目结构中的目录及文件说明如表5.1所示。


表5.1项目目录及文件说明



属性
说明
admin
后台模块目录
admin/
后台模块中的页面文件目录
admin/view
后台模块视图文件目录
common
前后台公共文件目录
common/captcha/
图形验证码文件目录
common/lib/
自定义库文件目录
error
错误页面文件目录
frame
系统配置与初始化目录
home
前台模块目录
public
系统公共静态资源目录
Index.php
系统入口文件

这里只介绍了主要目录与文件,详情请参见源码。
2. 准备公共资源
项目的公共资源,主要包括项目前后台模块使用的公共CSS样式文件、JavaScript脚本文件、图像文件,以及共同使用的自定义和第三方开发的库文件等。
1) 引入CSS和JS文件
本项目前端使用Layui及jQuery前端框架。下载Layui和jQuery资源,并将其复制到项目根目录下的public\static子目录中。
2) 加载自定义函数库
在项目根目录下的common目录中创建子目录lib,并在其中新建一个名为function.php的PHP文件。该文件是自定义库文件,存放项目模块的公共函数。
函数库文件function.php的加载,一般在项目的初始化文件中完成。下面创建项目的初始化文件init.php。
3. 创建初始化文件
项目的初始化文件主要用于完成定义常量、加载资源,以及启动SESSION等工作。在项目根目录下的frame子目录中,新建init.php文件并编写如下代码。

<?php

/**

* 项目初始化文件

* 定义常量、加载资源、启动SESSION

* @author weiwenping

*/

define('APP_NAME', '微梦内容管理系统');

define('ADMIN_NAME', '微梦后台管理系统');



define('DOC_ROOT', $_SERVER['DOCUMENT_ROOT']);

define('DOC_COMMON', DOC_ROOT.'/common/');

define('DOC_LIB', DOC_COMMON.'lib/');

define('CAPTCHA_FONT_PATH', DOC_COMMON.'captcha/');

define('CONFIG_PATH', DOC_ROOT.'/frame/');

//前台文件目录常量

define('HOME_ROOT', DOC_ROOT.'/home/');

define('HOME_VIEW_PATH', HOME_ROOT.'/view/');

//后台文件目录常量

define('ADMIN_ROOT', DOC_ROOT.'/admin/');

define('ADMIN_VIEW_PATH', ADMIN_ROOT.'/view/');

//公共资源引用目录常量

define('WEB_ROOT', 'http://book.php.chp05/');

define('WEB_STATIC', WEB_ROOT.'public/static/');

define('LAYUI_PATH', WEB_STATIC.'layui/');

define('LAYUI_CSS_PATH', LAYUI_PATH.'css/');

define('LAYUI_JS_PATH', LAYUI_PATH);

define('JQUERY_PATH', WEB_STATIC.'jquery/');

define('CSS_PATH', WEB_STATIC.'css/');

define('JS_PATH', WEB_STATIC.'js/');

define('UPLOAD_PATH', WEB_ROOT.'public/upload/');

//后台模块目录常量

define('WEB_ADMIN', WEB_ROOT.'admin/');

//加载公共资源函数库

require DOC_LIB.'function.php';

//开启SESSION

session_start();



/**

* 项目启动函数

* @return string[]|unknown[]

*/

function run(){

//获取请求的URI

$request_uri = $_SERVER['REQUEST_URI'];

//获取请求的入口文件

$script_name = $_SERVER['SCRIPT_NAME'];

//获取URL中的模块、方法和查询字符串

$request = str_replace($script_name, '', $request_uri);

$request = ltrim($request, '/');

//将查询字符串分离

$request_array = explode('?', $request);

//将模块和方法存放在数组中

$module_action = $request_array[0];

if (empty($module_action)) {

$module = 'home';

$action = 'index';

}else{

$module_action = explode('/', $module_action);

//获取模块

if (isset($module_action[0]) && !empty($module_action[0])) {

$module = $module_action[0];

}else{

$module = 'home';

}

//获取方法

if (isset($module_action[1]) && !empty($module_action[1])) {

$action = $module_action[1];

}else{

$action = 'index';

}

}

//请求的文件

$require = DOC_ROOT.'/'.$module.'/'.$action.'.php';

if (file_exists($require)) {

require_once $require;

}else{

require_once DOC_ROOT.'/error/error.html';

}    

}

在系统初始化文件中,除定义一些必要的常量外,还定义了一个名为run()的函数,该函数是系统的启动函数,起着一个简单的路由作用。
5.2.2系统流程设计
系统功能确定以后,需要设计系统架构,也就是系统的整体框架。本系统是一个简单的PHP设计项目,所以项目功能不复杂。本项目系统架构模拟MVC设计模式。
下面通过测试页面,对项目前台模块执行流程进行设计。首先设计系统入口文件。在项目根目录下创建index.php文件,代码如下。

<?php

/**

* 系统入口文件

* @author weiwenping

*/

//加载项目初始化文件

require './frame/init.php';

//启动项目

run();

接着,在前台模块home目录中新建测试文件,分别为test.php和view\test.php,代码如下。

<?php

defined('APP_NAME') or exit('非法访问');

/**

* 前台测试文件test.php

*/

$data['test'] = array(

[

'id' => '1',

'name' => '面向对象程序设计(C++语言描述)',

'author' => '马石安 魏文平',

'press' => '清华大学出版社'

],

…

);

//加载视图

require HOME_VIEW_PATH.'v_test.php';



<!-- 前台测试视图文件home\view\test.php -->

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<title>前台测试页面</title>

<link rel="stylesheet" href="<?php echo LAYUI_CSS_PATH;?>layui.css" />

</head>

<body class="layui-body">

<h2>前台测试页面</h2>

<hr>

<div class="layui-table">

<table>

<thead>

<tr><th>序号</th><th>教材名称</th><th>作者</th><th>出版社</th></tr>

</thead>

<tbody>

<?php foreach($data['test'] as $v): ?>

<tr><td><?php echo $v['id'];?></td><td><?php echo $v['name'];?></td>

<td><?php echo $v['author'];?></td><td><?php echo $v['press'];?></td>

</tr>

<?php endforeach; ?>

</tbody>

</table>

</div>

</body>

</html>


打开浏览器,在地址栏中输入“http://book.php.chp05/index.php/home/test”,运行效果如图5.7所示。



图5.7前台测试页面运行效果


在上述URL中,“index.php”表示系统入口文件;  “home”表示系统前台模块;  “test”表示需要访问的前台文件。如果与MVC模式项目进行类比,可以看出,前台模块中的PHP文件相当于MVC的控制器;  视图目录view中的PHP文件相当于MVC的视图;  而控制器中的数据准备则相当于MVC的模型。
所以,本系统的访问流程确定为:  系统域名→入口文件→模块→页面文件→页面视图。
在上述测试页面中,测试数据是通过数组的方式直接准备的,在后续的开发过程中,页面中的数据要从数据库中通过“模型”来获取。
5.2.3数据库设计
数据库设计是PHP Web项目开发的重要环节,数据库设计的质量直接影响着系统运行的效率与速度。
为项目创建一个名为wmchap05db的MySQL数据库,并在其中创建数据表,数据表名均使用“wm_”前缀。由于篇幅的限制,这里只展示系统管理员数据表、文章数据表和菜单数据表,其他数据表请参见源码包中的相关资源。
项目管理员数据表wm_admin的结构及字段含义分别如图5.8和表5.2所示。



图5.8数据表wm_admin的结构




表5.2数据表wm_admin字段说明



字段
说明
id
序号。主键
username
用户名
password
密码。MD5加密
truename
用户真实姓名
gid
用户类型分组ID。对应数据表tb_admin_groups中的GID
status
管理员账号状态。1表示禁用,0表示正常
add_time
注册时间

项目文章数据表wm_article的结构及字段含义分别如图5.9和表5.3所示。



图5.9数据表wm_article的结构





表5.3数据表wm_article字段说明



字段
说明
id
序号,主键
title
文章标题
title_img
文章标题图片,在前台主页列表显示时使用,如图5.1所示
is_hot
是否是热门文章
is_top
是否将文章置顶
cate_id
文章类型ID
user_id
作者ID
content
文章内容
pv
文章浏览次数
status
文章显示状态
create_time
文章提交时间
update_time
文章修改时间

项目菜单数据表wm_menu的结构及字段含义分别如图5.10和表5.4所示。



图5.10数据表wm_menu的结构





表5.4数据表wm_menu字段说明



字段
说明
id
序号。主键
name
菜单名称
href
菜单URL
position
菜单位置
pid
菜单的父菜单ID
status
菜单状态
order
菜单排列顺序

上述对数据库的操作与管理,使用的是SQLyog工具软件。请读者选择自己熟悉的数据库管理工具进行操作。
5.3后台功能实现
Web项目的开发往往都是从后台开发开始的。下面实现项目后台管理系统的部分功能,包括管理员信息显示,管理员登录验证,管理员添加、编辑与删除;  内容分类管理、内容详情管理等。


视频讲解


5.3.1主页设计
系统后台主页运行效果如图5.4所示。
1. 后台布局
系统后台模块主要负责系统数据的管理,例如,管理员的添加、删除、信息修改,以及信息显示;  管理员权限设置;  内容的添加、编辑、删除、审核等。
把后台模块的所有操作集中到页面左侧的菜单,操作内容则在页面的主显示区域中进行显示,如图5.5所示。
单击页面中侧边栏“用户管理”菜单下的“用户列表”子菜单,则会在页面的主显示区内,显示所有的系统用户信息,同时显示各种操作按钮。
单击页面左上角的主页图标及文本,可以重新加载后台模块封面页面;  单击页面右上角的“退出系统”,则可以退出系统的后台管理,如图5.11所示。



图5.11系统后台主页


2. 侧边栏设计
侧边栏中的导航菜单信息不是直接固定在页面代码中的,它们来自数据库中的wm_menus数据表。
1) 数据库操作
在系统自定义库文件目录common\lib中新建db.php文件,在该文件中编写数据库连接、数据表查询等函数,代码如下。

<?php

/**

* 数据库连接

* @return NULL|mixed

*/

function db_connect() {

static $link = null;

if (!$link) {

$link = call_user_func_array('mysqli_connect', getConfigItem('DB_CONNECT'));

if (!$link) {

exit('数据库连接错误');

}        

}

return $link;

}

/**

* 数据查询

* @param string $sql

* @param string $type

* @param array $data

* @return object

*/

function db_query($sql, $type='', $data=[]) {

$link = db_connect();

$stmt = mysqli_prepare($link, $sql);

if (!$stmt) {

$error = mysqli_error($link).'<br>SQL语句:'.$sql;

exit('数据库操作错误!'.$error);

}

if ($data == []) {

mysqli_stmt_execute($stmt);

}else{

$data = (array)$data;

db_bind_param($stmt, $type, $data);

mysqli_stmt_execute($stmt);

}

return $stmt;

}

/**

* 查询数据绑定

* @param object $stmt

* @param string $tyle

* @param array $data

*/

function db_bind_param($stmt, $tyle, &$data) {

$params = [$stmt, $tyle];

foreach ($data as &$params[]) { }

call_user_func_array('mysqli_stmt_bind_param', $params);

}



define('DB_ALL', 0);

define('DB_ROW', 1);

define('DB_COLUMN', 2);

define('DB_AFFECTED', 3);

define('DB_LASTED', 4);

/**

* 查询结果处理

* @param int $mode

* @param string $sql

* @param string $type

* @param array $data

* @return array

*/

function db_fetch($mode, $sql, $type='', $data=[]) {

$stmt = db_query($sql,$type,$data);

$result = mysqli_stmt_get_result($stmt);

switch ($mode) {

case DB_ROW:

return mysqli_fetch_assoc($result);        ;

break;

case DB_COLUMN:

return current((array)mysqli_fetch_row($result));

break;



default:

return mysqli_fetch_all($result, MYSQLI_ASSOC);

break;

}

}

/**

* 数据查询,没有结果集的查询

* @param int $mode

* @param string $sql

* @param string $type

* @param array $data

* @return int

*/

function db_exec($mode, $sql, $type='', $data=[]) {

$stmt = db_query($sql, $type, $data);

switch ($mode) {

case DB_LASTED:

return mysqli_stmt_insert_id($stmt);

break;       

default:

return mysqli_stmt_affected_rows($stmt);

break;

}

}



function db_connect() {

static $link = null;

if (!$link) {

$link = call_user_func_array('mysqli_connect', getConfigItem('DB_CONNECT'));

if (!$link) {

exit('数据库连接错误');

}        

}

return $link;

}

上述代码中的getConfigItem()是自定义函数,用于获取配置文件中的数据库连接信息。数据库连接数据存放在系统根目录下的frame子目录中,文件名为config.php。
数据库操作文件db.php中的功能函数,由于代码都比较简单,这里不再详细说明,请使用源码自行学习。
2) 获取菜单数据
在系统common\lib\function.php文件中,编写菜单处理函数。其中,getMenus()函数返回某个模块、某个位置中的全部菜单;  getSubMenus()函数返回某个菜单项的二级菜单。为了简单一些,本项目菜单只分为二级,代码如下。

function getMenus($module, $position) {

//从数据库中获取菜单信息

$sql = "select * from wm_menus  where  module = '".

$module."' and  position  = '".$position."'"

.' order by  order';

$menus = db_fetch(DB_ALL, $sql); 

return $menus ? $menus : null;

}



function getSubMenus($menus, $menus_id) {

$subMenus = array();

foreach ($menus as $v) {

if ($v['pid'] == $menus_id) {

array_unshift($subMenus, $v);

}

}

return $subMenus ? $subMenus : null;

}

在后台页面的PHP文件中,调用上述方法,获取菜单数据,代码如下。

//获取侧边栏导航菜单

$side_navs = getMenus('admin','side');

foreach ($side_navs as $k => $v) {    

$data['side_navs'][$k] = $v;

$data['side_navs'][$k]['sub'] =  getSubMenus($side_navs, $v['id']);

}

最后,设计侧边栏视图。文件为admin\view\side.php,代码如下。

<?php defined('APP_NAME') or exit('非法访问');?>

<div class="layui-collapse" lay-accordion>

<?php foreach ($data['side_navs'] as $v):?>

<?php if($v['pid']==0):?>

<div class="layui-colla-item" style="padding-left:10px;">

<h2 class="layui-colla-title"><?=$v['name']?></h2>

<div class="layui-colla-content">

<?php if($v['sub'] === null): ?>

<span style="padding:5px;"><a href="<?=$v['href']?>">管理主页</a></span>

<?php else: ?>

<ul>

<?php foreach($v['sub'] as $sub): ?>

<li style="padding:5px;border-bottom: 1px dashed #FF5722;">

<a href="<?=$sub['href']?>"><?=$sub['name']?></a>				

</li>

<?php endforeach; ?>

</ul>

<?php endif;?>

</div>

</div>

<?php endif;?>

<?php endforeach;?>	

</div>

侧边栏运行效果如图5.12所示。



图5.12后台页面中的侧边栏


这里使用了Layui的面板组件,单击一级菜单名称,展开二级菜单列表;  单击二级菜单,在页面右侧的区域显示主内容。
3. 后台主页设计
用户访问系统后台主页,就是加载系统的admin\index.php文件,该文件代码如下。


<?php

defined('APP_NAME') or exit('非法访问');

/**

* 系统后台首页文件

* admin\index.php

*/

//视图数据

$data = array(

'title'=>'后台首页',

'css_files' =>array(

LAYUI_CSS_PATH.'layui.css',        

CSS_PATH.'admin_index.css',        

),

'js_files' =>array(

JQUERY_PATH.'jquery-3.3.1.min.js',

LAYUI_JS_PATH.'layui.js',        

),

…

);

//加载视图

require ADMIN_VIEW_PATH.'v_index.php';

接着,编写视图文件admin\view\v_index.php,代码如下。

<?php defined('APP_NAME') or exit('非法访问');?>

<?php include ADMIN_VIEW_PATH.'header.php';?>

<div class="layui-fluid">

<div class="layui-row layui-bg-cyan header">

<?php include ADMIN_VIEW_PATH.'top.php';?>

</div>

<div class="layui-row" style="border: 1px solid #2F4056;">

<div class="layui-col-md3 side">

<?php include ADMIN_VIEW_PATH.'side.php';?>

</div>

<div class="layui-col-md9 content">

<h3>系统后台首页</h3>

</div>

</div>

<div class="layui-row layui-bg-cyan footer">

<?php include ADMIN_VIEW_PATH.'bottom.php';?>

</div>	

</div>

<?php include ADMIN_VIEW_PATH.'footer.php';?>

<script>

$(document).ready(function() {

layui.use('element', function(){

var element = layui.element;

});

});

</script>

视图由公共部分和特别部分组成,公共部分文件也是存放在admin\view目录中,各部分代码详见源码。
4. 后台封面设计
为系统后台设计一个封面页面,用于承载系统介绍、使用说明、用户登录,以及广告信息等内容,如图5.11所示。
在系统admin目录下新建welcome.php文件,代码如下。 

<?php

defined('APP_NAME') or exit('非法访问');

/**

* 系统后台欢迎页面

* admin\welcome.php

*/

//视图数据

$data = array(

'title'=>'后台管理系统',

'css_files' =>array(

LAYUI_CSS_PATH.'layui.css',

CSS_PATH.'admin_welcome.css',  

),

'js_files' =>array(

JQUERY_PATH.'jquery-3.3.1.min.js',

LAYUI_JS_PATH.'layui.js',

),

'content' => array(

'title' => '微梦后台管理系统',

'author' => '开发者: 马石安 魏文平',

'time' => '2020 年  1 月'

),

);



//加载视图

require ADMIN_VIEW_PATH.'v_welcome.php';

在admin/view目录中,新建视图文件v_welcome.php。该视图文件非常简单,请参考项目源码。


视频讲解



5.3.2登录与登出
用户的登录与登出,是任何Web项目都必须具有的功能。用户登录功能实现时,重点注意用户输入数据的验证。
1. 登录表单设计
1) 创建页面
在系统后台模块中,添加管理员登录视图文件admin\view\v_login.php,页面效果如图5.13所示。



图5.13后台用户登录表单


系统后台管理员登录页面,通过单击图5.11页面中的“用户登录”超级链接来实现。加载该页面的PHP文件为admin\login.php,其代码与系统后台模块中的其他PHP文件相似,请参见源码。
在如图5.13所示的页面中有一个图形验证码,用于加强用户登录验证。下面实现验证码图片的显示,以及单击验证码图片更新验证码的功能。
2) 生成验证码
在项目根目录下的common\lib子目录中添加captcha.php文件,用于存放生成图形验证码、输出图形验证码、检验验证码是否正确的函数,代码如下。

<?php

/**

* 生成验证码

* @param int $count 验证码长度

* @return string

*/

function create_captcha($count=5) {

$code = '';

$charset = 'ABCDEFGHJKLMNPQRSTUVWXY23456789';

$len = strlen($charset) - 1;

for ($i = 0; $i < $count; $i++) {

$code .= $charset[mt_rand(0, $len)];

}

return $code;

}

/**

* 显示图形验证码

* @param string $code

*/

function captcha_img($code) {

$width = 120;//验证码图片宽度

$height = 35;   //验证码图片高度

$img = imagecreate($width, $height);   //创建验证码图像

//设置验证码图像的背景颜色

imagecolorallocate($img, mt_rand(50,255), mt_rand(0,155), mt_rand(0,155));    

$fontSize = 18;   //验证码文字大小

$fontColor = imagecolorallocate($img, 255, 255, 255);   //验证码文字颜色

$fontStyle = CAPTCHA_FONT_PATH.'font.TTF';   //验证码文字样式

$len = strlen($code);

//生成指定长度的验证码

for($i=0; $i<$len; ++$i){  

imagettftext(

$img, 

$fontSize,

mt_rand(0,20) - mt_rand(0,25),//设置验证码文字倾斜角度

//随机设置验证码文字显示坐标

$fontSize * $i + 12,

mt_rand($height/2, $height),             

$fontColor,  

$fontStyle,  

$code[$i] 

);

}

//为验证码图片生成彩色噪点

for($i=0; $i<200; ++$i){

//随机生成颜色

$color = imagecolorallocate($img,mt_rand(0,255),mt_rand(0,255),mt_rand(0,255));

//随机绘制干扰点

imagesetpixel($img,mt_rand(0,$width),mt_rand(0,$height),$color);

}    

//绘制10条干扰线

for($i=0; $i<5; ++$i){

//随机生成干扰线颜色

$color = imagecolorallocate($img,mt_rand(0,255),mt_rand(0,255),mt_rand(0,255));

//随机绘制干扰线imageline($img,mt_rand(0,$width),0,mt_rand(0,$width),$height,$color);

}

header('Content-Type: image/png'); 

imagepng($img);   //输出图像

imagedestroy($img);   //释放内存

}

/**

* 检查验证码是否正确

* @param string $captcha

* @return boolean

*/

function captcha_check($captcha) {    

if ($captcha == $_SESSION['wmcms']['captcha']) {

return true;

}

return false;

}

验证码需要以某种样式显示,所以需要扩展名为“.ttf”的字体样式文件。该文件可以在操作系统的字体库中复制。这里将其存放在项目common\captcha子目录中。
上述代码生成了图形验证码,下面将其在用户登录页面中显示出来。在项目的admin目录下,新建一个名为captcha.php文件,代码如下。

<?php

/**

* 生成图形验证码并输出

* @author weiwenping

*/

//加载验证码函数文件

require DOC_LIB.'captcha.php';

//生成验证码

$code = create_captcha();

//输出验证码图像

captcha_img($code);

//将验证码保存到SESSION中

$_SESSION['wmcms']['captcha'] = $code;

最后,修改登录表单中的验证码图片的HTML元素属性,代码如下。

<div class="layui-form-item">

<label for="captcha" class="layui-form-label">验证码</label>

<div class="layui-input-inline">

<input type="text" name="captcha" id="captcha" class="layui-input">

</div>

<img src="captcha " id="captchaImg" />

</div>

3) 验证码更新
在用户输入登录数据时,页面上的验证码有时会看不清楚,这时要让用户单击验证码图片来进行重置。这个功能可以用jQuery轻松实现,代码如下。 

//单击图形验证码进行重置

$('#captchaImg').click(function(event) {

$(this).attr('src', 'captcha?rand='+Math.random());

});

代码中的“captchaImg”是显示验证码图片的img标签ID。
2. 测试数据准备
为了测试用户登录功能,需要在数据库中添加一些测试数据。登录MySQL数据库服务器,在项目数据库wmchapt05db的wm_admin数据表中插入测试数据。例如,用户名admin,用户密码md5('123456')等。
3. 数据验证
表单数据的验证分为前端验证与后端验证。前端验证就是在页面上通过JavaScript代码进行验证,而后端验证是通过PHP与数据库的交互来对用户输入数据进行检验。
1) 前端验证
新建public\static\js\admin_login.js文件,并在文件中添加onlogin()函数,用于处理表单提交,代码如下。

//处理登录表单提交

function onlogin() {

//获取表单数据

var username = $.trim($('#username').val());

var password = $.trim($('#password').val());

var captcha = $.trim($('#captcha').val());

//检查输入框是否为空

if (username == '') {

layer.alert('请输入用户名!', {'icon': 2 });

return;

}

if (password == '') {

layer.alert('请输入密码!', { 'icon': 2 });

return;

}

if (captcha == '') {

layer.alert('请输入验证码!', { 'icon': 2 });

return;

}

//提交表单数据

$.post(

$('form').first().attr('action'), 

{ 'username': username, 'password': password, 'captcha': captcha },

function(res) {

if (res.code > 0) {

//重置验证码

$('#captcha').val('');                

reloadImg();

//错误信息提示

layer.alert(res.msg, { icon: 2 });

} else {

//登录成功信息提示

layer.msg(res.msg);

//1秒后跳转到后台主页

setTimeout(function() {

window.location.href = 'index'

},

1000);

}



}, 'json');

}

这里只简单地验证表单数据是否为空。其他验证,如用户名长度不能少于3位,密码长度不能少于6位等,请自行实现。页面效果如图5.14所示。



图5.14表单前端验证


图5.14是用户直接单击“登录”按钮后的效果。用户单击“登录”按钮后,页面中的jQuery代码会调用上述的onlogin()函数,代码如下。

//用户单击“登录”按钮,提交数据登录

$('#submit').click(function(event) {

//处理表单提交

onlogin();

});

注意: 由于表单的提交按钮“登录”位于form标签内,需要阻止该按钮的默认表单提交事件发生。
2) 后端验证
在前端的验证中,只是验证了用户输入的验证码是否为空,并没有判断它的正确性。下面在后端编写代码,检查用户输入的验证码是否正确。
在系统后台模块中添加admin\loginExc.php文件,并编写代码。

<?php

/**

* 微梦后台管理系统

* 管理员登录处理

* @author weiwenping

*/

//加载验证码函数文件

require_once DOC_LIB.'captcha.php';

//接收表单数据

$username = trim($_POST['username']);

$password = trim($_POST['password']);

$captcha = trim($_POST['captcha']);

//验证输入是否为空

if ($username == '') {

exit(json_encode(array('code'=>1,'msg'=>'用户名不能为空!')));

}

if ($password == '') {

exit(json_encode(array('code'=>1,'msg'=>'密码不能为空!')));

}

if ($captcha == '') {

exit(json_encode(array('code'=>1,'msg'=>'验证码不能为空!')));

}

//验证验证码

if (!captcha_check(strtoupper($captcha))) {

exit(json_encode(array('code'=>1,'msg'=>'验证码错误!')));

}


页面效果如图5.15所示。



图5.15验证码后端验证


后端验证码检查完毕后,接着进行用户输入数据的后端验证,也就是与数据库中的数据进行比对。
在admin\loginExc.php文件中添加代码,完成数据库验证,代码如下。

//数据库验证,用户验证

$sql = 'select * from wm_admin where username = ?';

$admin = db_fetch(DB_ROW, $sql, 's', array('username'=> htmlentities($username)));

//用户验证

if (!$admin) {

exit(json_encode(array('code'=>1,'msg'=>'该用户不存在!')));

}

//验证密码

if (md5($admin['username'].$password) != $admin['password']) {

exit(json_encode(array('code'=>1,'msg'=>'密码错误!')));

}

//账号是否被禁用

if ($admin['status'] == 1) {

exit(json_encode(array('code'=>1,'msg'=>'该用户已被禁用!')));

}

//设置SESSION

$_SESSION['wmcms']['admin'] = $admin;

//登录成功返回信息

exit(json_encode(array('code'=>0,'msg'=>'恭喜,登录成功!')));

测试效果如图5.16所示。



图5.16用户登录数据库验证


图中展示的是“用户名”输入错误时的返回信息。注意,这里表单提交采用的是AJAX请求方式,详细代码请参见源码。
4. 管理员退出系统
管理员退出系统功能的实现非常简单,只需要删除SESSION中的管理员数据即可。
首先,在页面中添加代码,监听用户是否单击了“退出系统”按钮,代码如下。

//退出系统处理

$('#logout').click(function(event) {

var url = $(this).attr(‘href’);

layer.confirm('您确定要退出吗?', {

btn: ['确定', '取消'] ,

icon: 3,

title: '温馨提示'                  

}, function(index, layero){

onlogout(url);

});

});

上述代码中的“logout”是“退出系统”按钮标签的ID,当用户单击提示框中的“确定”按钮时,调用onlogout()函数完成用户退出功能。onlogout()函数代码如下。

function onlogout(url) {    

$.post(url, {},

function(res) {

if (res.code > 0) {

layer.alert(res.msg, { icon: 2 });

} else {

layer.msg(res.msg);

setTimeout(function() {

window.location.href = 'index';

},

1000);

}

}, 'json');

}

从上述代码可以看出,用户退出系统的处理逻辑是在admin\logout.php文件中实现的。该文件代码如下。 

<?php

/**

* 管理员退出系统处理

* @author weiwenping

*/

//退出系统

if (isset($_SESSION['wmcms']['admin'])) {

unset($_SESSION['wmcms']['admin']);

}

//返回提示信息

exit(json_encode(array('code'=>0,'msg'=>'您已退出后台管理系统!')));

从上述代码可以看出,用户退出系统时,直接删除SESSION中的用户数据即可。用户退出系统后,不能再访问系统后台内页,需要对用户权限进行控制。
在自定义函数库common\lib\function.php中,新建函数privilege(),代码如下。 

//用户权限控制

function privilege() {

$res = false;

//用户是否登录

$res = isset($_SESSION['wmcms']['user']) ? true : false; 

return $res;

}

在系统后台主页admin\index.php中添加代码,进行用户访问控制,代码如下。

//判断用户是否有权限访问

if (!privilege()) {

header('Location: admin/welcome');

exit;

}

如果用户没有访问权限,跳转到系统后台欢迎页面。


视频讲解



5.3.3用户信息管理
系统用户分为系统管理员和普通用户,系统管理员可以登录到系统后台,而普通用户只能使用系统前台功能。
用户信息管理,包括用户信息列表显示、用户的添加/编辑和删除、用户权限管理等。下面以管理员用户信息管理为例来进行介绍。
1. 信息显示
要对系统用户进行管理,首先必须将所有的用户信息在页面中显示出来,然后再针对不同的用户账号进行相应的操作。用户信息列表如图5.5所示。
页面中显示的管理员信息来自数据表wm_admin和wm_admin_groups。数据表wm_admin_groups中存储系统管理员的分组,不同组管理员具有不同的操作权限。
在系统后台模块中添加admin\user_list.php文件,在文件中编写代码从数据库获取数据,代码如下。

<?php

…

//从数据库中读取全部管理员信息

$sql = 'select * from wm_admin';

$user_lists = db_fetch(DB_ALL, $sql);

//将管理员的GID转换成名称

$sql = 'select * from wm_admin_groups';

$roles = db_fetch(DB_ALL, $sql);

foreach ($user_lists as $key => $v) {

foreach ($roles as $vr) {

if ($v['gid'] == $vr['gid']) {

$user_lists[$key]['role'] = $vr['title'];

}

}

}

…

管理员数据表wm_admin中的分组gid记录的是数据表wm_admin_group中的序号gid,它是一个整型数据,所以必须将其替换成相应的名称,如系统管理员、开发人员、文章编辑等。
最后,编写视图文件admin\view\v_user_list.php代码。其中数据循环输出代码如下。

<?php foreach ($data['user_lists'] as $v): ?>

<tr>

<td><?php echo $v['id']?></td>

<td><?php echo $v['username']?></td>

<td><?php echo $v['truename']?></td>

<td><?php echo $v['role']?></td>

<td><?php echo $v['status'] ? '<span style="color:red">禁用<span>' : '正常';  ?></td>

<td><?php echo date('Y-m-d H:i:s',$v['add_time'])?></td>

<td>

<button class="layui-btn layui-btn-xs" onclick="edit()">编辑</button>

<button class="layui-btn layui-btn-danger layui-btn-xs" onclick="del()">删除</button>

</td>

</tr>

<?php endforeach;?>

完整代码请参见源码。
2. 添加管理员
管理员的添加功能,通过单击如图5.5所示页面中的“添加”按钮来实现。表单页面如图5.17所示。



图5.17添加管理员表单


该表单页面文件为admin\view\v_admin_add.php,视图通过Layui的弹窗来渲染。在文件admin\view\v_user_list.php中添加JS代码。

//处理添加事件

$('#add').click(function(event) {

add();

});



/**

* 加载管理员添加表单页面

*/

function add() {

layer.open({

type: 2,

title: '添加管理员',

shade: 0.3,

area: ['480px', '420px'],

content: 'admin_add?rand='+Math.random(),

});

}

编写上述表单页面的PHP文件admin\admin_add.php代码。

<?php

…

//从数据库中读取管理员分组信息

$sql = 'select * from tb_admin_groups';

$data['role'] = db_fetch(DB_ALL, $sql);

…

//加载视图

require ADMIN_VIEW_PATH.'v_admin_add.php';

处理表单提交,在admin\view\v_admin_add.php文件中添加JS代码,如下所示。

//使用户名输入框获得输入焦点

$('input[name="username"]').focus();

//处理表单提交

$('#submit').click(function(event) {

add_submit($('form').first().attr('action'));

});



function add_submit(action) {

var username = $.trim($('input[name="username"]').val());

var password = $.trim($('input[name="password"]').val());

var gid = $.trim($('select[name="gid"]').val());

var truename = $.trim($('input[name="truename"]').val());    

if (username == '') {

layer.alert('请输入用户名', { icon: 2 });

return;

}    

if (gid == '0') {

layer.alert('请选择角色', { icon: 2 });

return;

}

if (password == '') {

layer.alert('请输入密码', { icon: 2 });

return;

}

if (truename == '') {

layer.alert('请输入真实姓名', { icon: 2 });

return;

}

$.post(action, $('form').serialize(), function(res) {

if (res.code > 0) {

layer.alert(res.msg, { icon: 2 });

} else {

layer.msg(res.msg);

setTimeout(function() {

parent.window.location.reload();

}, 1000);

}

}, 'json');

}

编写表单提交PHP处理文件admin\insertExc.php,代码如下。

<?php

…

//获取表单数据

$data['username'] = trim(htmlspecialchars($_POST['username']));

$password = trim(htmlspecialchars($_POST['password']));

$data['truename'] = trim(htmlspecialchars($_POST['truename']));

$data['gid'] = (int)trim(htmlspecialchars($_POST['gid']));

$data['status'] = isset($_POST['status']) ? (int)trim(htmlspecialchars($_POST['status'])) : 0;

$data['add_time'] = time();

//数据非空验证

if (!$data['username']) {

exit(json_encode(array('code'=>1,'msg'=>'用户名不能为空!')));

}

if (!$password) {

exit(json_encode(array('code'=>1,'msg'=>'密码不能为空!')));

}

if (!$data['truename']) {

exit(json_encode(array('code'=>1,'msg'=>'真实姓名不能为空!')));

}

if (!$data['gid']) {

exit(json_encode(array('code'=>1,'msg'=>'角色不能为空!')));

}

//检查用户名是否已被注册

$sql = 'select username from wm_admin where username = ?';

$itme = db_fetch(DB_ROW, $sql,'s',$data['username']);

if ($itme) {

exit(json_encode(array('code'=>1,'msg'=>'用户名已经被注册!')));;

}

//密码加密

$data['password'] = md5($data['username'].$password);

//完成数据插入

$sql = "INSERT INTO wm_admin (username, truename, gid, status, add_time, password) VALUES (?,?,?,?,?,?)";

$id = db_exec(DB_LASTED, $sql, 'ssisis', array_values($data));

if ($id) {

exit(json_encode(array('code'=>0,'msg'=>'管理员添加成功!'))); 

}else{

exit(json_encode(array('code'=>1,'msg'=>'管理员添加失败!'))); 

}

管理员添加页面效果,如图5.18所示。



图5.18管理员添加页面效果


从图中输出结果可以看出,新管理员被成功插入数据表wm_admin中。这里采用了AJAX和Layui的弹窗来共同实现管理员的添加功能,以获得最佳的用户体验。
3. 编辑管理员
管理员的编辑功能,通过单击如图5.5所示页面中的“编辑”按钮来实现。表单页面如图5.19所示。



图5.19管理员编辑表单


该表单页面其实就是图5.17中管理员的添加页面,只是在窗口弹出时,在表单元素中显示了需要编辑的某个管理员的信息。
单击图5.19页面中的“编辑”按钮,触发按钮单击事件,调用edit()函数实现编辑窗口的显示以及数据的初始化。在文件admin\view\v_user_list.php中,给“编辑”按钮绑定事件,代码如下。

<button class="layui-btn layui-btn-xs" onclick="edit(<?php echo $v['id']?>)">编辑</button>

在admin/js/admin_lists.php文件中,新建edit()函数,代码如下。

function edit(id) {

layer.open({

type: 2,

title: '编辑管理员信息',

shade: 0.3,

area: ['480px', '420px'],

content: 'admin_add?id=' + id,

});

}

修改管理员添加功能模块的PHP文件admin\admin_add.php,在加载视图之前获取需要编辑的管理员信息,代码如下。

//获取需要编辑的管理员数据

if (isset($_GET['id']) && (int)$_GET['id'] > 0) {

//获取需要编辑的管理员信息

$sql = 'select * from wm_admin where id = ?';

$admin = db_fetch(DB_ROW, $sql, 'i', [(int)$_GET['id']]);

}


修改管理员添加功能模块的视图文件admin\view\v_admin_add.php,使其适合添加、编辑两种情况。详细代码请参见源码。
修改表单提交文件insertExc.php,使其适合添加、编辑两种情况。完整代码如下。

<?php

…

//获取表单数据

$id = isset($_POST['id']) ? (int)trim(htmlspecialchars($_POST['id'])) : 0;

$data['username'] = trim(htmlspecialchars($_POST['username']));

$password = trim(htmlspecialchars($_POST['password']));

$data['truename'] = trim(htmlspecialchars($_POST['truename']));

$data['gid'] = (int)trim(htmlspecialchars($_POST['gid']));

$data['status'] = isset($_POST['status']) ? (int)trim(htmlspecialchars($_POST['status'])) : 0;

$data['add_time'] = time();

//数据非空验证

if (!$data['username']) {

exit(json_encode(array('code'=>1,'msg'=>'用户名不能为空!')));

}

//添加管理员时判断

if ($id==0 && !$password) {

exit(json_encode(array('code'=>1,'msg'=>'密码不能为空!')));

}

//编辑时,若用户没有修改密码,则使用原密码

if ($id > 0) {

if (!$password){

$data['password'] = $_POST['password1'];         

}else{

//密码加密        

$data['password'] = md5($data['username'].$password);

}

}else{

//密码加密

$data['password'] = md5($data['username'].$password);

}



if (!$data['truename']) {

exit(json_encode(array('code'=>1,'msg'=>'真实姓名不能为空!')));

}

if (!$data['gid']) {

exit(json_encode(array('code'=>1,'msg'=>'角色不能为空!')));

}

//执行添加或编辑操作

if ($id > 0) {

//完成信息编辑

$sql = "update wm_admin set truename = ?, gid = ?, status = ?, password =? where id = ?";

$eid = db_exec(DB_AFFECTED, $sql, 'siisi', [$data['truename'],$data['gid'],$data['status'],$data['password'],$id]);

if ($eid) {

exit(json_encode(array('code'=>0,'msg'=>'管理员编辑成功!')));

}else{

exit(json_encode(array('code'=>1,'msg'=>'管理员编辑失败!')));

}

}else{

//检查用户名是否已被注册

$sql = 'select username from wm_admin where username = ?';

$itme = db_fetch(DB_ROW, $sql,'s',$data['username']);

if ($itme) {

exit(json_encode(array('code'=>1,'msg'=>'用户名已经被注册!')));;

}

//完成数据插入

$sql = "INSERT INTO wm_admin (username, truename, gid, status, add_time, password) VALUES (?,?,?,?,?,?)";

$nid = db_exec(DB_LASTED, $sql, 'ssisis', array_values($data));

if ($nid) {

exit(json_encode(array('code'=>0,'msg'=>'管理员添加成功!')));

}else{

exit(json_encode(array('code'=>1,'msg'=>'管理员添加失败!')));

}

}


注意: 编辑时表单中的“用户名”元素是只读的,也就是不允许修改管理员的用户名。编辑时,若密码框为空,则默认为用户不修改密码。
运行效果如图5.20所示。



图5.20管理员编辑页面效果


从图中可以看出,第6条记录中的“角色”与“状态”数据已被成功修改。
4. 删除管理员
删除管理员功能的实现非常简单,只需要给管理员列表页面中的“删除”按钮绑定一个单击函数del(),并在admin\deleteExc.php文件中将数据从数据表中删除即可。
页面中的JS函数del()代码如下。

function del(id) {

layer.confirm(

'确定要删除吗?',

{ btn: ['确定', '取消'], icon: 3 },

function() {

$.post(

'./deleteExc',

{ 'id': id },

function(res) {

if (res.code > 0) {

layer.alert(res.msg, { icon: 2 });

} else {

layer.msg(res.msg);

setTimeout(function() {

window.location.reload();

}, 1000);

}

}, 'json');

});

}

删除之前需要再次询问用户是否确定删除,这里用Layui的“确认”框来实现,如图5.21所示。



图5.21管理员删除确认


实现数据删除的文件为admin\deleteExc.php,代码如下。


<?php

…

//获取表单数据

$id = isset($_POST['id']) ? (int)trim(htmlspecialchars($_POST['id'])) : 0;

if ($id > 0) {

$sql = 'delete from wm_admin where id = ?';

$did = db_exec(DB_AFFECTED, $sql, 'i', [$id]);

if ($did) {        

exit(json_encode(array('code'=>0,'msg'=>'删除成功!'))); 

}      

else {

exit(json_encode(array('code'=>1,'msg'=>'删除失败!')));

}

}

exit(json_encode(array('code'=>1,'msg'=>'删除失败!')));

由于篇幅的限制,用户信息管理模块功能就介绍这些,请读者依照本节方法完善该模块的其他功能。例如,信息显示时的翻页、用户头像的上传等。


视频讲解


5.3.4内容管理
系统后台的内容管理与前述的用户管理相似,主要实现内容的添加、修改和删除等功能。
1. 内容显示
文章内容的列表在系统后台的主页上显示,如图5.4所示。该功能的实现非常简单,只需要在admin\index.php文件中从数据表wm_article中获取到全部文章即可,代码如下。

//获取全部文章

$sql = 'select id,title,create_time from wm_article';

$articles = db_fetch(DB_ALL, $sql);

在视图文件admin\view\v_index.php中循环输出这样文章的ID、标题和添加时间,代码如下。

<?php foreach ($data['articles'] as $k=>$v): ?>

<tr>

<td><?php echo $k+1;?></td>

<td><?php echo $v['title']?></td>

<td><?php echo date('Y-m-d H:i:s',$v['create_time'])?></td>

<td>

<button class="layui-btn layui-btn-xs" onclick="edit(<?php echo $v['id']?>)">
编辑</button>

<button class="layui-btn layui-btn-danger layui-btn-xs" onclick=
"del(<?php echo $v['id']?>)">删除</button>

</td>

</tr>

<?php endforeach;?>

完整代码请参见源码。
2. 内容添加
单击后台页面侧边栏中的“添加文章”菜单,或者单击页面右侧的“添加”按钮,即可打开文章添加页面,如图5.22所示。



图5.22添加文章表单


页面中的文章类型数据来自数据库中的wm_article_category数据表。选择框展开后的效果如图5.23所示。



图5.23文章类型选择


添加文章表单页面设计完成后,就可以编写代码完成数据的插入了。实现该功能的代码与上述添加管理员代码相同,只是将操作的数据表更改为wm_article即可。这里不再赘述。



视频讲解


5.4前台功能实现
PHP Web应用项目的前台一般用于展示系统信息,因此,主要使用的是数据库的查询。本节实现案例项目前台的部分功能,包括首页、详情,以及分类查询等。
5.4.1前台首页
系统前台首页用于概要性地展示系统发布的信息,页面效果如图5.1所示。
1. 主菜单
系统前台首页顶部的导航菜单,与5.3.1节中的侧边栏导航菜单一样,其数据也是来自数据库中的wm_menus表。因此,获取菜单数据的方式也与前述相同,只是这里获取的是home模块中位置为top的菜单项,代码如下。

//获取菜单

$top_navs = getMenus('home','top');

foreach ($top_navs as $k => $v) {    

$data['top_navs'][$k] = $v;

$data['top_navs'][$k]['sub'] =  getSubMenus($top_navs, $v['id']);

}


菜单效果如图5.24所示。



图5.24前台主菜单


菜单视图采用Layui的水平菜单,请参见源码。
2. 全部文章
在系统前台主页的左侧,显示了系统中的全部文章概要,包括图标、标题、作者、发布日期,以及内容缩略。
系统文章数据存放在数据库中的wm_article数据表中,将其取出显示即可,代码如下。

//获取全部文章

$sql = 'select * from wm_article as a join wm_user as u where a.user_id = u.id';

$articles = db_fetch(DB_ALL, $sql);


注意: 这里使用了数据表的联合查询,因为需要从wm_user表中读取作者的姓名。在wm_article表中,只记录了作者的ID,也就是wm_user表中的id字段的值。
3. 热门浏览
在系统前台主页的右侧,显示了最近的热门文章概要,包括文章标题和浏览次数。热门文章是从上面获取到的全部文章中筛选出来的,代码如下。

//获取全部文章

$sql = 'select * from wm_article as a join wm_user as u where a.user_id = u.id';

$articles = db_fetch(DB_ALL, $sql);

//获取热门文章

$hot_articles = array();

foreach ($articles as $a){

if ($a['is_hot'] == 1) {

array_push($hot_articles, $a);

}

}


热门文章在数据表中存储时,其is_hot字段的值为1。
5.4.2内容详情
单击主页中显示的文章标题,即可查看该篇文章的详情,如图5.2所示。
1. 获取数据
根据文章标题链接传递过来的文章ID,从数据表wm_article中查询该篇文章的详细内容,代码如下。

//获取文章ID

$article_id = isset($_GET['id']) ? (int)$_GET['id'] : null;

if ($article_id === null) {

header('Location: /');

exit;

}

//获取该篇文章

$sql = 'select * from wm_article where id = '.$article_id;

$article = db_fetch(DB_ALL, $sql);

//获取作者姓名

$sql = 'select name from wm_user where id = '.$article[0]['user_id'];

$user = db_fetch(DB_ALL, $sql);

$article[0]['uname']= $user[0]['name'];

//获取热门文章

$sql = 'select * from wm_article where is_hot = 1';

$hot_articles = db_fetch(DB_ALL, $sql);


与主页一样,在文章详情页面的右侧也显示了最近的热门文章,所以这里也要再次获取到这些文章的标题及浏览次数。
2. 显示详情
在视图中显示文章详情,代码如下。

<div class="layui-col-md8">

<div class="layui-card main-title" >

<div class="layui-card-header">文章详情</div>

</div>	

<div class="layui-row" style="height:130px;border-bottom: 1px solid #eee;">	

<h2 style="color:#009688"><?=$data['article']['title']?></h2>

<p style="padding-top:5px;line-height: 25px;color:#aaa;">

<span style="padding-right: 20px;">作者: <?=$data['article']['uname']?></span>

<span>发布时间: <?=date('Y-m-d',$data['article']['create_time'])?></span></p>

<p style="border-top: 1px dashed #eee;padding-top: 8px; ">

<?php echo htmlspecialchars($data['article']['content']); ?></p>

</div>

</div>


注意: 在文章详情页面,单击右侧的热门文章标题,也可以在页面左侧显示该文章详情。实现代码参见源码。
5.4.3分类查询
本项目中文章的分类查询通过主菜单来实现。也就是说,系统前台主菜单中的PHP、JAVA、CSS等实际上是文章的分类名称。
例如,单击PHP菜单,则会查询出所有类型为PHP的文章,如图5.25所示。



图5.25分类查询


注意: 主页中原来的“全部文章”文本,更改为“php”,说明查询到的是“php”分类的文章。
查询代码如下。 

//获取分类名称

$article_category = isset($_GET['cate_name']) ? htmlentities(trim($_GET['cate_name'])): null;

if ($article_category === null) {

header('Location: /');

exit;

}

$sql = "select id from wm_article_category where name = '".$article_category."'";

$category_id = db_fetch(DB_ALL, $sql);

//获取分类文章

$sql = 'select a.id as aid,a.title,u.name,a.create_time,a.content,a.title_img,a.is_hot,a.pv from wm_article as a join wm_user as u where a.user_id = u.id and a.cate_id = '.$category_id[0]['id'];

$articles = db_fetch(DB_ALL, $sql);

//获取热门文章

$sql = 'select * from wm_article where is_hot = 1';

$hot_articles = db_fetch(DB_ALL, $sql);

注意: 分类名称由菜单的href属性传递,代码如下。

<li class="layui-nav-item"><a href="<?php echo $m['href'].'?cate_name='.strtolower($m['name'])?>"><?=$m['name']?></a></li>


详细代码请参见源码。
5.4.4文章搜索
文章的搜索通过主菜单右侧的搜索表单来实现,这里搜索的是文章标题中的关键字,采用模糊查询方式。运行效果如图5.26所示。



图5.26文章搜索


数据查询代码如下。 

//获取文章标题中的关键字

$keyword = isset($_POST['keyword']) ? htmlentities(trim($_POST['keyword'])): null;

if ($keyword === null) {

header('Location: /');

exit;

}

//获取查询到的全部文章

$sql = "select a.id as aid,a.title,u.name,a.create_time,a.content,a.title_img,a.is_hot,a.pv from wm_article as a join wm_user as u where a.user_id = u.id and a.title like '%".$keyword."%'";

$articles = db_fetch(DB_ALL, $sql);


页面头部右侧的搜索框视图代码存放在home\view\top_search.php文件中,代码如下。

<?php defined('APP_NAME') or exit('非法访问');?>

<form class="layui-form" action="/index.php/home/search" method="post">

<div class="layui-inline" style="padding:10px;">

<div class="layui-input-inline">

<input type="text" name="keyword" required  lay-verify="required" placeholder="请输关键字" autocomplete="off" class="layui-input">

</div>

<div class="layui-inline">

<div class="layui-input-inline">

<input type="submit" value="搜索" class="layui-btn layui-btn-primary" />

</div>

</div>

</div>

</form>

5.5本 章 小 结
本章详细介绍了PHP语言面向过程的编程方法,以及开发一个简单的内容管理系统的过程。由于篇幅的限制,这里重点讲述了系统前/后台一些基本功能的实现。关于系统中的一些其他功能,或者是读者觉得案例中不是很满意的功能的实现,留给读者作为本章学习的课后作业。希望读者充分发挥自己的想象力与创造力,使本章案例项目变得更加完美。