本文目录导读:

在PHP项目中实现商品上下架功能,核心是设计数据库状态字段 + 后端逻辑控制 + 前端交互,下面是完整实现方案:
数据库设计
在商品表中添加状态字段:
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
price DECIMAL(10,2),
stock INT DEFAULT 0,
status TINYINT DEFAULT 1 COMMENT '1=上架 0=下架',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
状态字段设计要点:
- 建议用
TINYINT或INT类型 - 值含义清晰:
1上架,0下架 - 后续扩展可加
2=待审核等状态
后端实现
1 商品模型层
class Product {
private $db;
public function __construct($db) {
$this->db = $db;
}
// 切换商品状态
public function toggleStatus($productId) {
$sql = "UPDATE products SET status = !status WHERE id = ?";
$stmt = $this->db->prepare($sql);
return $stmt->execute([$productId]);
}
// 设置指定状态
public function setStatus($productId, $status) {
$allowedStatus = [0, 1];
if (!in_array($status, $allowedStatus)) {
return false;
}
$sql = "UPDATE products SET status = ? WHERE id = ?";
$stmt = $this->db->prepare($sql);
return $stmt->execute([$status, $productId]);
}
// 批量操作
public function batchSetStatus($productIds, $status) {
if (empty($productIds)) return false;
$placeholders = implode(',', array_fill(0, count($productIds), '?'));
$sql = "UPDATE products SET status = ? WHERE id IN ($placeholders)";
$params = array_merge([$status], $productIds);
$stmt = $this->db->prepare($sql);
return $stmt->execute($params);
}
// 获取在售商品
public function getActiveProducts() {
$sql = "SELECT * FROM products WHERE status = 1";
return $this->db->query($sql)->fetchAll();
}
}
2 控制器层
class ProductController {
private $productModel;
public function __construct($productModel) {
$this->productModel = $productModel;
}
// 切换状态接口
public function toggleStatus($request) {
$productId = $request['id'] ?? 0;
// 验证商品存在
if (!$this->productExists($productId)) {
return ['code' => 404, 'msg' => '商品不存在'];
}
$result = $this->productModel->toggleStatus($productId);
if ($result) {
return ['code' => 200, 'msg' => '状态切换成功'];
} else {
return ['code' => 500, 'msg' => '操作失败'];
}
}
// 上下架操作接口
public function updateStatus($request) {
$productId = $request['id'] ?? 0;
$status = $request['status'] ?? 0;
// 权限验证
if (!$this->checkPermission()) {
return ['code' => 403, 'msg' => '无权限'];
}
$result = $this->productModel->setStatus($productId, $status);
if ($result) {
$action = $status == 1 ? '上架' : '下架';
// 记录操作日志
$this->logOperation($productId, $action);
return ['code' => 200, 'msg' => "商品{$action}成功"];
}
return ['code' => 500, 'msg' => '操作失败'];
}
}
前端实现
1 使用 AJAX 实现
<!-- 商品列表页面 -->
<div id="product-list">
<table>
<thead>
<tr>
<th><input type="checkbox" id="select-all"></th>
<th>商品名称</th>
<th>价格</th>
<th>库存</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($products as $product): ?>
<tr data-id="<?= $product['id'] ?>">
<td><input type="checkbox" class="product-checkbox"></td>
<td><?= htmlspecialchars($product['name']) ?></td>
<td>¥<?= $product['price'] ?></td>
<td><?= $product['stock'] ?></td>
<td>
<span class="status-badge <?= $product['status'] ? 'active' : 'inactive' ?>">
<?= $product['status'] ? '上架' : '下架' ?>
</span>
</td>
<td>
<button class="btn-toggle-status" data-id="<?= $product['id'] ?>">
<?= $product['status'] ? '下架' : '上架' ?>
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- 批量操作按钮 -->
<div class="batch-actions">
<button id="batch-on">批量上架</button>
<button id="batch-off">批量下架</button>
</div>
2 JavaScript 交互
$(document).ready(function() {
// 单个商品上架/下架切换
$('.btn-toggle-status').click(function() {
const productId = $(this).data('id');
const $button = $(this);
// 显示加载状态
$button.prop('disabled', true).text('处理中...');
$.ajax({
url: '/api/product/toggle-status',
method: 'POST',
data: { id: productId },
dataType: 'json',
success: function(response) {
if (response.code === 200) {
// 更新UI
const $row = $button.closest('tr');
const $badge = $row.find('.status-badge');
const $toggleBtn = $row.find('.btn-toggle-status');
// 切换状态显示
if ($badge.hasClass('active')) {
$badge.removeClass('active').addClass('inactive');
$badge.text('下架');
$toggleBtn.text('上架');
} else {
$badge.removeClass('inactive').addClass('active');
$badge.text('上架');
$toggleBtn.text('下架');
}
showToast('操作成功', 'success');
} else {
showToast(response.msg || '操作失败', 'error');
}
},
error: function() {
showToast('网络错误,请重试', 'error');
},
complete: function() {
$button.prop('disabled', false);
}
});
});
// 批量操作
$('#batch-on, #batch-off').click(function() {
const status = $(this).attr('id') === 'batch-on' ? 1 : 0;
const selectedIds = [];
// 收集选中的商品ID
$('.product-checkbox:checked').each(function() {
selectedIds.push($(this).closest('tr').data('id'));
});
if (selectedIds.length === 0) {
showToast('请先选择商品', 'warning');
return;
}
// 确认对话框
if (!confirm(`确定要${status ? '上架' : '下架'}选中的${selectedIds.length}个商品吗?`)) {
return;
}
$.ajax({
url: '/api/product/batch-status',
method: 'POST',
data: {
ids: selectedIds,
status: status
},
dataType: 'json',
success: function(response) {
if (response.code === 200) {
location.reload();
} else {
showToast(response.msg, 'error');
}
}
});
});
});
安全考虑
1 权限验证
// 中间件示例
class AuthMiddleware {
public function handle($request) {
// 只有管理员可以操作上架/下架
if ($_SESSION['user_role'] !== 'admin') {
header('HTTP/1.1 403 Forbidden');
echo json_encode(['code' => 403, 'msg' => '无权限']);
exit;
}
}
}
2 防止 SQL 注入
- 始终使用预处理语句
- 对输入进行验证和过滤
// 安全的查询
$stmt = $pdo->prepare("UPDATE products SET status = ? WHERE id = ?");
$stmt->execute([intval($status), intval($id)]);
高级功能扩展
1 定时上下架
// 创建定时任务表
CREATE TABLE schedule_products (
id INT PRIMARY KEY AUTO_INCREMENT,
product_id INT,
action TINYINT COMMENT '1=上架 0=下架',
execute_time DATETIME,
status TINYINT DEFAULT 0 COMMENT '0=待执行 1=已执行',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
// 定时任务脚本
class ScheduleTask {
public function execute() {
$sql = "SELECT * FROM schedule_products
WHERE status = 0 AND execute_time <= NOW()";
$tasks = $this->db->query($sql)->fetchAll();
foreach ($tasks as $task) {
// 执行上下架操作
$this->updateProductStatus($task['product_id'], $task['action']);
// 更新任务状态
$updateSql = "UPDATE schedule_products SET status = 1 WHERE id = ?";
$this->db->prepare($updateSql)->execute([$task['id']]);
}
}
}
2 日志记录
CREATE TABLE product_logs (
id INT PRIMARY KEY AUTO_INCREMENT,
product_id INT,
action VARCHAR(50),
operator INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_product_id (product_id)
);
最佳实践建议
- 使用事务:批量操作时使用事务确保数据一致性
- 缓存处理:频繁查询的商品状态可加入 Redis 缓存
- SEO 处理:下架商品返回 404 状态码,避免搜索引擎索引
- 前端优化:使用防抖处理,避免频繁点击导致多次请求
- 状态机模式:复杂状态流转建议使用状态机模式
这个实现方案适合大多数 PHP 项目,可以根据具体需求进行调整和扩展。