如何用PHP项目实现FAQ系统?

wen java案例 4

本文目录导读:

如何用PHP项目实现FAQ系统?

  1. 系统设计
  2. 项目结构
  3. 核心代码实现
  4. 功能特点
  5. 安全建议
  6. 性能优化建议
  7. 部署步骤

我来为您详细介绍如何用PHP实现一个完整的FAQ系统。

系统设计

数据库设计

首先创建数据库表:

CREATE TABLE `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `sort_order` int(11) DEFAULT 0,
  `created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `faqs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `category_id` int(11) NOT NULL,
  `question` text NOT NULL,
  `answer` text NOT NULL,
  `status` tinyint(1) DEFAULT 1,
  `sort_order` int(11) DEFAULT 0,
  `created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
  `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

项目结构

faq-system/
├── config/
│   └── database.php
├── includes/
│   ├── header.php
│   └── footer.php
├── admin/
│   ├── index.php
│   ├── categories.php
│   ├── faqs.php
│   └── login.php
├── api/
│   ├── search.php
│   └── categories.php
├── assets/
│   ├── css/
│   │   └── style.css
│   └── js/
│       └── main.js
├── index.php
└── search.php

核心代码实现

数据库连接配置 (config/database.php)

<?php
class Database {
    private $host = "localhost";
    private $db_name = "faq_system";
    private $username = "root";
    private $password = "";
    public $conn;
    public function getConnection() {
        $this->conn = null;
        try {
            $this->conn = new PDO(
                "mysql:host=" . $this->host . ";dbname=" . $this->db_name,
                $this->username,
                $this->password
            );
            $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch(PDOException $exception) {
            echo "Connection error: " . $exception->getMessage();
        }
        return $this->conn;
    }
}
?>

FAQ主页面 (index.php)

<?php
require_once 'config/database.php';
$database = new Database();
$db = $database->getConnection();
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">FAQ 常见问题系统</title>
    <link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
    <?php include 'includes/header.php'; ?>
    <div class="container">
        <!-- 搜索功能 -->
        <div class="search-box">
            <input type="text" id="searchInput" placeholder="搜索常见问题..." onkeyup="searchFAQ()">
        </div>
        <!-- FAQ列表 -->
        <div class="faq-list">
            <?php
            // 获取所有分类
            $query = "SELECT * FROM categories ORDER BY sort_order ASC";
            $stmt = $db->prepare($query);
            $stmt->execute();
            $categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
            foreach ($categories as $category) {
                echo "<div class='category'>";
                echo "<h2>" . htmlspecialchars($category['name']) . "</h2>";
                // 获取该分类下的FAQ
                $faqQuery = "SELECT * FROM faqs WHERE category_id = :category_id AND status = 1 ORDER BY sort_order ASC";
                $faqStmt = $db->prepare($faqQuery);
                $faqStmt->bindParam(':category_id', $category['id']);
                $faqStmt->execute();
                $faqs = $faqStmt->fetchAll(PDO::FETCH_ASSOC);
                foreach ($faqs as $faq) {
                    echo "<div class='faq-item'>";
                    echo "<div class='faq-question' onclick='toggleAnswer(this)'>";
                    echo "<span>" . htmlspecialchars($faq['question']) . "</span>";
                    echo "<span class='arrow'>▶</span>";
                    echo "</div>";
                    echo "<div class='faq-answer' style='display:none'>";
                    echo nl2br(htmlspecialchars($faq['answer']));
                    echo "</div>";
                    echo "</div>";
                }
                echo "</div>";
            }
            ?>
        </div>
    </div>
    <?php include 'includes/footer.php'; ?>
    <script src="assets/js/main.js"></script>
</body>
</html>

AJAX搜索功能 (api/search.php)

<?php
require_once '../config/database.php';
$database = new Database();
$db = $database->getConnection();
$searchTerm = isset($_GET['q']) ? $_GET['q'] : '';
if ($searchTerm) {
    // 搜索相关问题
    $query = "SELECT f.*, c.name as category_name 
              FROM faqs f 
              LEFT JOIN categories c ON f.category_id = c.id 
              WHERE (f.question LIKE :search1 OR f.answer LIKE :search2) 
              AND f.status = 1 
              ORDER BY f.sort_order ASC";
    $stmt = $db->prepare($query);
    $searchTerm = "%{$searchTerm}%";
    $stmt->bindParam(':search1', $searchTerm);
    $stmt->bindParam(':search2', $searchTerm);
    $stmt->execute();
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
    header('Content-Type: application/json');
    echo json_encode($results);
}
?>

后台管理 (admin/faqs.php)

<?php
session_start();
require_once '../config/database.php';
// 简单登录检查
if (!isset($_SESSION['admin_logged_in'])) {
    header('Location: login.php');
    exit();
}
$database = new Database();
$db = $database->getConnection();
// 添加FAQ
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
    if ($_POST['action'] === 'add') {
        $query = "INSERT INTO faqs (category_id, question, answer, status) VALUES (:category_id, :question, :answer, :status)";
        $stmt = $db->prepare($query);
        $stmt->bindParam(':category_id', $_POST['category_id']);
        $stmt->bindParam(':question', $_POST['question']);
        $stmt->bindParam(':answer', $_POST['answer']);
        $stmt->bindParam(':status', $_POST['status']);
        $stmt->execute();
    }
    // 更新FAQ
    if ($_POST['action'] === 'update') {
        $query = "UPDATE faqs SET category_id = :category_id, question = :question, answer = :answer, status = :status WHERE id = :id";
        $stmt = $db->prepare($query);
        $stmt->bindParam(':id', $_POST['id']);
        $stmt->bindParam(':category_id', $_POST['category_id']);
        $stmt->bindParam(':question', $_POST['question']);
        $stmt->bindParam(':answer', $_POST['answer']);
        $stmt->bindParam(':status', $_POST['status']);
        $stmt->execute();
    }
    // 删除FAQ
    if ($_POST['action'] === 'delete') {
        $query = "DELETE FROM faqs WHERE id = :id";
        $stmt = $db->prepare($query);
        $stmt->bindParam(':id', $_POST['id']);
        $stmt->execute();
    }
}
// 获取所有分类
$categoryQuery = "SELECT * FROM categories ORDER BY sort_order ASC";
$categoryStmt = $db->prepare($categoryQuery);
$categoryStmt->execute();
$categories = $categoryStmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">FAQ管理后台</title>
    <link rel="stylesheet" href="../assets/css/style.css">
</head>
<body>
    <div class="admin-container">
        <h1>FAQ管理后台</h1>
        <!-- 添加FAQ表单 -->
        <div class="add-faq-form">
            <h2>添加新FAQ</h2>
            <form method="POST">
                <input type="hidden" name="action" value="add">
                <div class="form-group">
                    <label>分类:</label>
                    <select name="category_id" required>
                        <?php foreach ($categories as $category): ?>
                            <option value="<?php echo $category['id']; ?>">
                                <?php echo htmlspecialchars($category['name']); ?>
                            </option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="form-group">
                    <label>问题:</label>
                    <input type="text" name="question" required>
                </div>
                <div class="form-group">
                    <label>答案:</label>
                    <textarea name="answer" rows="5" required></textarea>
                </div>
                <div class="form-group">
                    <label>状态:</label>
                    <select name="status">
                        <option value="1">启用</option>
                        <option value="0">禁用</option>
                    </select>
                </div>
                <button type="submit">添加FAQ</button>
            </form>
        </div>
        <!-- FAQ列表 -->
        <div class="faq-table">
            <h2>FAQ列表</h2>
            <table>
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>分类</th>
                        <th>问题</th>
                        <th>状态</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    <?php
                    $query = "SELECT f.*, c.name as category_name 
                              FROM faqs f 
                              LEFT JOIN categories c ON f.category_id = c.id 
                              ORDER BY f.sort_order ASC";
                    $stmt = $db->prepare($query);
                    $stmt->execute();
                    $faqs = $stmt->fetchAll(PDO::FETCH_ASSOC);
                    foreach ($faqs as $faq): ?>
                        <tr>
                            <td><?php echo $faq['id']; ?></td>
                            <td><?php echo htmlspecialchars($faq['category_name']); ?></td>
                            <td><?php echo htmlspecialchars($faq['question']); ?></td>
                            <td><?php echo $faq['status'] ? '启用' : '禁用'; ?></td>
                            <td>
                                <button onclick="editFAQ(<?php echo $faq['id']; ?>)">编辑</button>
                                <form method="POST" style="display:inline">
                                    <input type="hidden" name="action" value="delete">
                                    <input type="hidden" name="id" value="<?php echo $faq['id']; ?>">
                                    <button type="submit" onclick="return confirm('确定删除?')">删除</button>
                                </form>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </div>
    </div>
</body>
</html>

JavaScript交互 (assets/js/main.js)

// 切换答案显示
function toggleAnswer(element) {
    const answer = element.nextElementSibling;
    const arrow = element.querySelector('.arrow');
    if (answer.style.display === 'none') {
        answer.style.display = 'block';
        arrow.innerHTML = '▼';
    } else {
        answer.style.display = 'none';
        arrow.innerHTML = '▶';
    }
}
// 搜索功能
function searchFAQ() {
    const searchTerm = document.getElementById('searchInput').value;
    if (searchTerm.length < 2) {
        // 如果搜索词太短,显示所有内容
        document.querySelectorAll('.faq-item').forEach(item => {
            item.style.display = 'block';
        });
        return;
    }
    fetch(`api/search.php?q=${encodeURIComponent(searchTerm)}`)
        .then(response => response.json())
        .then(data => {
            // 隐藏所有FAQ项
            document.querySelectorAll('.faq-item').forEach(item => {
                item.style.display = 'none';
            });
            // 显示匹配的结果
            data.forEach(faq => {
                const items = document.querySelectorAll('.faq-item');
                // 这里需要根据返回的数据显示匹配的FAQ
            });
        })
        .catch(error => console.error('Error:', error));
}
// 编辑FAQ功能
function editFAQ(id) {
    // 获取FAQ数据并显示编辑表单
    fetch(`api/get_faq.php?id=${id}`)
        .then(response => response.json())
        .then(data => {
            // 填充编辑表单
            document.getElementById('editForm').style.display = 'block';
            document.getElementById('edit_id').value = data.id;
            document.getElementById('edit_question').value = data.question;
            document.getElementById('edit_answer').value = data.answer;
        });
}

CSS样式 (assets/css/style.css)

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
body {
    font-family: 'Microsoft YaHei', Arial, sans-serif;
    background: #f5f5f5;
    color: #333;
}
.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
}
.search-box {
    margin: 20px 0;
    text-align: center;
}
.search-box input {
    width: 60%;
    padding: 12px 20px;
    border: 2px solid #ddd;
    border-radius: 25px;
    font-size: 16px;
    transition: border-color 0.3s;
}
.search-box input:focus {
    outline: none;
    border-color: #007bff;
}
.category {
    background: white;
    border-radius: 8px;
    padding: 20px;
    margin-bottom: 20px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.category h2 {
    color: #007bff;
    border-bottom: 2px solid #007bff;
    padding-bottom: 10px;
    margin-bottom: 15px;
}
.faq-item {
    border: 1px solid #eee;
    border-radius: 5px;
    margin-bottom: 10px;
    overflow: hidden;
}
.faq-question {
    background: #f8f9fa;
    padding: 15px;
    cursor: pointer;
    display: flex;
    justify-content: space-between;
    align-items: center;
    transition: background 0.3s;
}
.faq-question:hover {
    background: #e9ecef;
}
.faq-question .arrow {
    transition: transform 0.3s;
    font-size: 12px;
}
.faq-answer {
    padding: 15px;
    line-height: 1.8;
    background: white;
    border-top: 1px solid #eee;
}
.admin-container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
}
.form-group {
    margin-bottom: 15px;
}
.form-group label {
    display: block;
    margin-bottom: 5px;
    font-weight: bold;
}
.form-group input,
.form-group textarea,
.form-group select {
    width: 100%;
    padding: 8px 12px;
    border: 1px solid #ddd;
    border-radius: 4px;
}
button {
    background: #007bff;
    color: white;
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}
button:hover {
    background: #0056b3;
}
table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 20px;
}
th, td {
    border: 1px solid #ddd;
    padding: 12px;
    text-align: left;
}
th {
    background: #f8f9fa;
}
tr:hover {
    background: #f5f5f5;
}

功能特点

  1. 分类管理:FAQ按分类组织,便于用户查找
  2. 搜索功能:实时搜索,用户快速找到答案
  3. 折叠展开:点击问题展开/关闭答案
  4. 后台管理:完整的CRUD操作
  5. 响应式设计:适配移动端和桌面端
  6. 状态管理:启用/禁用FAQ

安全建议

<?php
// SQL注入防护 - 使用预处理语句
$stmt = $db->prepare("SELECT * FROM faqs WHERE id = :id");
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->execute();
// XSS防护 - 输出过滤
echo htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
// CSRF防护
session_start();
$token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $token;
?>

性能优化建议

  1. 缓存实现:使用memcached或Redis缓存热门FAQ
  2. 数据库索引:为搜索字段添加索引
  3. 懒加载:按需加载FAQ内容
  4. CDN加速:使用CDN托管静态资源

部署步骤

  1. 创建数据库并导入SQL文件
  2. 修改config/database.php中的数据库配置
  3. 设置目录权限(确保uploads目录可写)
  4. 配置Web服务器(Apache/Nginx)
  5. 访问后台地址/admin/login.php进行管理

这个FAQ系统提供了完整的解决方案,您可以根据实际需求进行扩展和修改。

抱歉,评论功能暂时关闭!