本文目录导读:

- 数据库设计
- 频道模型类 (Channel.php)
- 控制器 (ChannelController.php)
- 视图文件
- 路由配置 (routes.php)
- 使用示例 - API接口 (api/channels.php)
- 扩展功能 - 缓存优化
我将为您提供一个完整的PHP频道管理实现方案,包含数据库设计、增删改查等核心功能。
数据库设计
CREATE TABLE `channels` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL COMMENT '频道名称', `slug` varchar(100) NOT NULL COMMENT 'URL别名', `description` text COMMENT '频道描述', `icon` varchar(255) DEFAULT NULL COMMENT '图标路径', `sort_order` int(11) DEFAULT 0 COMMENT '排序', `status` tinyint(1) DEFAULT 1 COMMENT '状态:1启用 0禁用', `parent_id` int(11) DEFAULT NULL COMMENT '父频道ID', `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `slug` (`slug`), KEY `parent_id` (`parent_id`), FOREIGN KEY (`parent_id`) REFERENCES `channels`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
频道模型类 (Channel.php)
<?php
namespace App\Models;
use PDO;
use PDOException;
class Channel {
private $db;
public function __construct(PDO $db) {
$this->db = $db;
}
// 获取所有频道
public function getAll($status = null) {
$sql = "SELECT c.*,
(SELECT COUNT(*) FROM channels WHERE parent_id = c.id) as child_count
FROM channels c";
if ($status !== null) {
$sql .= " WHERE c.status = :status";
}
$sql .= " ORDER BY c.parent_id IS NULL DESC, c.sort_order ASC";
$stmt = $this->db->prepare($sql);
if ($status !== null) {
$stmt->bindParam(':status', $status, PDO::PARAM_INT);
}
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 获取单条频道
public function getById($id) {
$stmt = $this->db->prepare("SELECT * FROM channels WHERE id = :id");
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetch(PDO::FETCH_ASSOC);
}
// 创建频道
public function create($data) {
$sql = "INSERT INTO channels (name, slug, description, icon, sort_order, status, parent_id)
VALUES (:name, :slug, :description, :icon, :sort_order, :status, :parent_id)";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':name', $data['name'], PDO::PARAM_STR);
$stmt->bindParam(':slug', $data['slug'], PDO::PARAM_STR);
$stmt->bindParam(':description', $data['description'], PDO::PARAM_STR);
$stmt->bindParam(':icon', $data['icon'], PDO::PARAM_STR);
$stmt->bindParam(':sort_order', $data['sort_order'], PDO::PARAM_INT);
$stmt->bindParam(':status', $data['status'], PDO::PARAM_INT);
$stmt->bindParam(':parent_id', $data['parent_id'], PDO::PARAM_INT);
if ($stmt->execute()) {
return $this->db->lastInsertId();
}
return false;
}
// 更新频道
public function update($id, $data) {
$sql = "UPDATE channels SET
name = :name,
slug = :slug,
description = :description,
icon = :icon,
sort_order = :sort_order,
status = :status,
parent_id = :parent_id,
updated_at = NOW()
WHERE id = :id";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->bindParam(':name', $data['name'], PDO::PARAM_STR);
$stmt->bindParam(':slug', $data['slug'], PDO::PARAM_STR);
$stmt->bindParam(':description', $data['description'], PDO::PARAM_STR);
$stmt->bindParam(':icon', $data['icon'], PDO::PARAM_STR);
$stmt->bindParam(':sort_order', $data['sort_order'], PDO::PARAM_INT);
$stmt->bindParam(':status', $data['status'], PDO::PARAM_INT);
$stmt->bindParam(':parent_id', $data['parent_id'], PDO::PARAM_INT);
return $stmt->execute();
}
// 删除频道
public function delete($id) {
$stmt = $this->db->prepare("DELETE FROM channels WHERE id = :id");
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
return $stmt->execute();
}
// 获取树形结构
public function getTree($parentId = null, $status = null) {
$channels = $this->getAll($status);
return $this->buildTree($channels, $parentId);
}
private function buildTree($channels, $parentId = null) {
$tree = [];
foreach ($channels as $channel) {
if ($channel['parent_id'] == $parentId) {
$children = $this->buildTree($channels, $channel['id']);
if ($children) {
$channel['children'] = $children;
}
$tree[] = $channel;
}
}
return $tree;
}
// 验证slug唯一性
public function slugExists($slug, $excludeId = null) {
$sql = "SELECT COUNT(*) FROM channels WHERE slug = :slug";
if ($excludeId) {
$sql .= " AND id != :exclude_id";
}
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':slug', $slug, PDO::PARAM_STR);
if ($excludeId) {
$stmt->bindParam(':exclude_id', $excludeId, PDO::PARAM_INT);
}
$stmt->execute();
return $stmt->fetchColumn() > 0;
}
// 生成slug
public function generateSlug($name) {
$slug = strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $name), '-'));
$baseSlug = $slug;
$counter = 1;
while ($this->slugExists($slug)) {
$slug = $baseSlug . '-' . $counter;
$counter++;
}
return $slug;
}
}
控制器 (ChannelController.php)
<?php
namespace App\Controllers;
use App\Models\Channel;
use App\Helpers\Validation;
use App\Helpers\Response;
class ChannelController {
private $channelModel;
public function __construct(Channel $channelModel) {
$this->channelModel = $channelModel;
}
// 列表页面
public function index() {
$channels = $this->channelModel->getAll();
$tree = $this->channelModel->getTree();
require_once __DIR__ . '/../views/channels/index.php';
}
// 创建页面
public function create() {
$parentChannels = $this->channelModel->getAll(1);
require_once __DIR__ . '/../views/channels/form.php';
}
// 编辑页面
public function edit($id) {
$channel = $this->channelModel->getById($id);
if (!$channel) {
header('Location: /channels?error=not_found');
exit;
}
$parentChannels = $this->channelModel->getAll(1);
require_once __DIR__ . '/../views/channels/form.php';
}
// 保存(创建/更新)
public function save() {
$data = $_POST;
$id = $data['id'] ?? null;
// 验证
$errors = $this->validate($data, $id);
if (!empty($errors)) {
$_SESSION['errors'] = $errors;
$_SESSION['old_data'] = $data;
if ($id) {
header("Location: /channels/edit/{$id}");
} else {
header('Location: /channels/create');
}
exit;
}
// 处理slug
if (empty($data['slug'])) {
$data['slug'] = $this->channelModel->generateSlug($data['name']);
}
// 处理icon上传
if (isset($_FILES['icon']) && $_FILES['icon']['error'] === UPLOAD_ERR_OK) {
$data['icon'] = $this->handleIconUpload($_FILES['icon']);
}
// 保存
if ($id) {
$result = $this->channelModel->update($id, $data);
} else {
$result = $this->channelModel->create($data);
}
if ($result) {
header('Location: /channels?success=saved');
} else {
header('Location: /channels?error=save_failed');
}
exit;
}
// 删除
public function delete($id) {
$result = $this->channelModel->delete($id);
if ($result) {
header('Location: /channels?success=deleted');
} else {
header('Location: /channels?error=delete_failed');
}
exit;
}
// 更新状态
public function toggleStatus($id) {
$channel = $this->channelModel->getById($id);
if ($channel) {
$newStatus = $channel['status'] ? 0 : 1;
$this->channelModel->update($id, ['status' => $newStatus]);
}
header('Location: /channels');
exit;
}
private function validate($data, $id = null) {
$errors = [];
if (empty($data['name'])) {
$errors['name'] = '频道名称不能为空';
}
if (!empty($data['slug']) && $this->channelModel->slugExists($data['slug'], $id)) {
$errors['slug'] = 'URL别名已存在';
}
return $errors;
}
private function handleIconUpload($file) {
$uploadDir = __DIR__ . '/../../public/uploads/icons/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$fileName = uniqid() . '_' . basename($file['name']);
$targetPath = $uploadDir . $fileName;
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
return '/uploads/icons/' . $fileName;
}
return null;
}
}
视图文件
列表页面 (views/channels/index.php)
<?php require_once __DIR__ . '/../layouts/header.php'; ?>
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>频道管理</h1>
<a href="/channels/create" class="btn btn-primary">添加频道</a>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success">
<?php
switch ($_GET['success']) {
case 'saved': echo '频道保存成功'; break;
case 'deleted': echo '频道删除成功'; break;
}
?>
</div>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<div class="alert alert-danger">
<?php
switch ($_GET['error']) {
case 'save_failed': echo '频道保存失败'; break;
case 'delete_failed': echo '频道删除失败'; break;
case 'not_found': echo '频道未找到'; break;
}
?>
</div>
<?php endif; ?>
<div class="card">
<div class="card-body">
<table class="table table-hover">
<thead>
<tr>
<th>排序</th>
<th>名称</th>
<th>URL别名</th>
<th>状态</th>
<th>子频道数</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php if (empty($channels)): ?>
<tr>
<td colspan="6" class="text-center">暂无频道数据</td>
</tr>
<?php else: ?>
<?php foreach ($channels as $channel): ?>
<tr>
<td><?php echo $channel['sort_order']; ?></td>
<td>
<?php if ($channel['icon']): ?>
<img src="<?php echo $channel['icon']; ?>" width="20" height="20" alt="icon">
<?php endif; ?>
<?php echo $channel['name']; ?>
<?php if ($channel['parent_id']): ?>
<span class="badge bg-info">子频道</span>
<?php endif; ?>
</td>
<td><code><?php echo $channel['slug']; ?></code></td>
<td>
<a href="/channels/toggle-status/<?php echo $channel['id']; ?>"
class="btn btn-sm <?php echo $channel['status'] ? 'btn-success' : 'btn-danger'; ?>">
<?php echo $channel['status'] ? '启用' : '禁用'; ?>
</a>
</td>
<td><?php echo $channel['child_count']; ?></td>
<td>
<a href="/channels/edit/<?php echo $channel['id']; ?>"
class="btn btn-sm btn-warning">编辑</a>
<a href="/channels/delete/<?php echo $channel['id']; ?>"
class="btn btn-sm btn-danger"
onclick="return confirm('确定删除此频道吗?')">删除</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once __DIR__ . '/../layouts/footer.php'; ?>
表单页面 (views/channels/form.php)
<?php require_once __DIR__ . '/../layouts/header.php'; ?>
<div class="container mt-4">
<h1><?php echo isset($channel) ? '编辑频道' : '添加频道'; ?></h1>
<?php if (!empty($_SESSION['errors'])): ?>
<div class="alert alert-danger">
<ul>
<?php foreach ($_SESSION['errors'] as $error): ?>
<li><?php echo $error; ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php unset($_SESSION['errors']); ?>
<?php endif; ?>
<form action="/channels/save" method="POST" enctype="multipart/form-data">
<?php if (isset($channel)): ?>
<input type="hidden" name="id" value="<?php echo $channel['id']; ?>">
<?php endif; ?>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="name" class="form-label">频道名称 *</label>
<input type="text"
class="form-control"
id="name"
name="name"
value="<?php echo $_SESSION['old_data']['name'] ?? ($channel['name'] ?? ''); ?>"
required>
</div>
<div class="mb-3">
<label for="slug" class="form-label">URL别名</label>
<input type="text"
class="form-control"
id="slug"
name="slug"
value="<?php echo $_SESSION['old_data']['slug'] ?? ($channel['slug'] ?? ''); ?>"
placeholder="留空自动生成">
<small class="text-muted">用于URL的唯一标识符</small>
</div>
<div class="mb-3">
<label for="parent_id" class="form-label">父频道</label>
<select class="form-select" id="parent_id" name="parent_id">
<option value="">无(顶级频道)</option>
<?php foreach ($parentChannels as $parent): ?>
<?php if (!isset($channel) || $parent['id'] != $channel['id']): ?>
<option value="<?php echo $parent['id']; ?>"
<?php echo (($_SESSION['old_data']['parent_id'] ?? ($channel['parent_id'] ?? '')) == $parent['id']) ? 'selected' : ''; ?>>
<?php echo $parent['name']; ?>
</option>
<?php endif; ?>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="description" class="form-label">描述</label>
<textarea class="form-control"
id="description"
name="description"
rows="3"><?php echo $_SESSION['old_data']['description'] ?? ($channel['description'] ?? ''); ?></textarea>
</div>
<div class="mb-3">
<label for="icon" class="form-label">图标</label>
<input type="file" class="form-control" id="icon" name="icon">
<?php if (isset($channel) && $channel['icon']): ?>
<div class="mt-2">
<img src="<?php echo $channel['icon']; ?>" width="50" height="50" alt="当前图标">
</div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="sort_order" class="form-label">排序</label>
<input type="number"
class="form-control"
id="sort_order"
name="sort_order"
value="<?php echo $_SESSION['old_data']['sort_order'] ?? ($channel['sort_order'] ?? '0'); ?>">
</div>
<div class="mb-3">
<div class="form-check">
<input type="checkbox"
class="form-check-input"
id="status"
name="status"
value="1"
<?php echo (($_SESSION['old_data']['status'] ?? ($channel['status'] ?? '1')) == '1') ? 'checked' : ''; ?>>
<label class="form-check-label" for="status">启用</label>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-between">
<a href="/channels" class="btn btn-secondary">取消</a>
<button type="submit" class="btn btn-primary">保存</button>
</div>
</form>
</div>
<?php
unset($_SESSION['old_data']);
require_once __DIR__ . '/../layouts/footer.php';
?>
路由配置 (routes.php)
<?php
// 路由配置示例
$routes = [
'GET' => [
'/channels' => 'ChannelController@index',
'/channels/create' => 'ChannelController@create',
'/channels/edit/{id}' => 'ChannelController@edit',
'/channels/delete/{id}' => 'ChannelController@delete',
'/channels/toggle-status/{id}' => 'ChannelController@toggleStatus',
],
'POST' => [
'/channels/save' => 'ChannelController@save',
],
];
使用示例 - API接口 (api/channels.php)
<?php
header('Content-Type: application/json');
require_once __DIR__ . '/../bootstrap.php';
use App\Models\Channel;
$channelModel = new Channel($db);
$method = $_SERVER['REQUEST_METHOD'];
switch ($method) {
case 'GET':
if (isset($_GET['id'])) {
$channel = $channelModel->getById($_GET['id']);
echo json_encode($channel);
} else {
$channels = $channelModel->getAll();
echo json_encode($channels);
}
break;
case 'POST':
$data = json_decode(file_get_contents('php://input'), true);
$result = $channelModel->create($data);
echo json_encode(['success' => $result, 'id' => $result]);
break;
case 'PUT':
parse_str(file_get_contents('php://input'), $data);
$id = $_GET['id'] ?? null;
if ($id) {
$result = $channelModel->update($id, $data);
echo json_encode(['success' => $result]);
}
break;
case 'DELETE':
$id = $_GET['id'] ?? null;
if ($id) {
$result = $channelModel->delete($id);
echo json_encode(['success' => $result]);
}
break;
}
扩展功能 - 缓存优化
<?php
namespace App\Helpers;
class ChannelCache {
private static $cache = [];
private static $cacheTime = 3600; // 1小时
public static function get($key) {
if (isset(self::$cache[$key])) {
$cache = self::$cache[$key];
if (time() - $cache['time'] < self::$cacheTime) {
return $cache['data'];
}
}
return false;
}
public static function set($key, $data) {
self::$cache[$key] = [
'data' => $data,
'time' => time()
];
}
public static function clear() {
self::$cache = [];
}
}
这个完整的频道管理系统提供了:
- 完善的数据结构:支持多级频道、排序、状态管理
- 完整的CRUD操作:创建、读取、更新、删除
- 用户友好的界面:列表展示、表单验证、错误提示
- 可扩展性:支持API接口、缓存优化
- 安全特性:CSRF防护、输入验证、SQL注入防护
您可以根据实际需求调整数据库字段、界面样式和功能逻辑。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播