
本文详解如何在 codeigniter 中复用同一份 html 表单,同时支持新增(insert)和编辑(update)操作,避免重复代码,解决因条件包裹导致插入表单不显示、url 路由错位及表单提交目标混乱等常见问题。
本文详解如何在 codeigniter 中复用同一份 html 表单,同时支持新增(insert)和编辑(update)操作,避免重复代码,解决因条件包裹导致插入表单不显示、url 路由错位及表单提交目标混乱等常见问题。
在实际开发中,为“新增”和“编辑”分别维护两套几乎相同的表单不仅增加维护成本,还易引发逻辑不一致问题。理想的方案是:一份视图文件、两个控制器入口(insert_package 和 edit_package)、一个统一的处理方法(如 save_package),并通过数据存在性动态适配行为。
✅ 核心原则:解耦“渲染”与“提交”,统一表单动作逻辑
你当前的问题根源在于:
- 视图中用 <?php if (isset($packages)) { ?> 包裹整个 <form>,导致新增时 $packages 未定义 → 表单完全不渲染;
- 表单 action 硬编码为 update_package/$id,但新增页(insert_package)无 $id,无法生成有效 URL;
- edit_package() 仅负责取数据并加载视图,却未参与表单提交处理,而 update_package() 又依赖 $id,导致新增流程断裂。
✅ 正确做法:统一入口 + 智能表单动作
推荐重构为 单入口保存方法 save_package(),它既能处理新增也能处理更新,且视图无需条件判断表单是否存在:
1. 修改控制器:合并逻辑,统一入口
// Controller: Admin.php
public function insert_package()
{
// 新增页直接调用 save_package(),不传 ID → 视为新增
$this->save_package();
}
public function edit_package($id)
{
// 编辑页也调用 save_package(),传入 ID → 视为更新
$this->save_package($id);
}
private function save_package($id = null)
{
$data['title'] = $id ? 'Update Package' : 'Add Package';
// 加载已有数据(仅当为编辑时)
if ($id) {
$package_model = new Admin_model();
$data['packages'] = $package_model->edit($id);
}
// 表单验证规则(新增/更新共用)
$this->form_validation->set_rules('title', 'Package Name', 'required|trim');
$this->form_validation->set_rules('tour_location', 'Inclusions', 'trim');
$this->form_validation->set_rules('description', 'Description', 'required|trim');
$this->form_validation->set_rules('cost', 'Price', 'required|numeric');
$this->form_validation->set_rules('status', 'Status', 'required|in_list[0,1]');
if ($this->form_validation->run() === TRUE) {
$post = $this->input->post(NULL, TRUE); // 自动过滤 XSS(推荐)
$upload_path = './images/';
// 处理图片上传(新增 or 更新)
$image_file = $_FILES['image']['name'] ?? '';
$upload_image = '';
if (!empty($image_file)) {
$config = [
'upload_path' => $upload_path,
'allowed_types' => 'gif|jpg|png',
'file_name' => time() . '_' . str_replace(' ', '_', $image_file),
];
$this->load->library('upload', $config);
if (!$this->upload->do_upload('image')) {
$this->session->set_flashdata('error', $this->upload->display_errors());
redirect($_SERVER['HTTP_REFERER']);
return;
}
$upload_image = $this->upload->data('file_name');
} elseif ($id && !empty($post['current_image'])) {
// 未上传新图,保留原图
$upload_image = $post['current_image'];
}
// 构建数据数组
$packageData = [
'title' => $post['title'],
'tour_location' => $post['tour_location'],
'description' => $post['description'],
'cost' => $post['cost'],
'status' => $post['status'],
'upload_path' => $upload_image,
];
$model = new Admin_model();
if ($id) {
// 更新
$result = $model->update($packageData, $id);
$msg = $result ? 'Package updated successfully.' : 'Update failed.';
} else {
// 新增
$result = $model->insert($packageData);
$msg = $result ? 'Package inserted successfully.' : 'Insert failed.';
}
$this->session->set_flashdata('status', $msg);
redirect(base_url('admin/packages'));
return;
}
// 验证失败或首次访问:渲染视图
$this->load->view('../admin/template/admin_header');
$this->load->view('../admin/template/admin_topnav');
$this->load->view('../admin/template/admin_sidebar');
$this->load->view('../admin/packages/manage_package', $data);
$this->load->view('../admin/template/admin_footer');
}2. 重构视图:移除条件包裹,智能设置 action
将原视图中 <?php if (isset($packages)) { ?>...<?php } ?> 完全删除,并让 <form> 始终渲染。action 使用空字符串(默认提交到当前 URL),由控制器统一处理:
<div class="card-body">
<small><?php echo validation_errors(); ?></small>
<!-- ✅ 移除 if(isset($packages)) 包裹,表单始终可见 -->
<form method="POST" enctype="multipart/form-data">
<div class="form-group">
<label for="name" class="control-label">Package Name</label>
<input type="text" class="form-control mb-3" name="title"
placeholder="Enter Package Name"
value="<?php echo isset($packages->title) ? htmlspecialchars($packages->title) : ''; ?>">
</div>
<div class="form-group">
<label for="tour_location" class="control-label">Inclusions</label>
<textarea id="compose-textarea" class="form-control" name="tour_location" style="height: 300px;">
<?php echo isset($packages->tour_location) ? htmlspecialchars($packages->tour_location) : ''; ?>
</textarea>
</div>
<div class="form-group">
<label for="description" class="control-label">Description</label>
<textarea id="compose-textarea2" class="form-control" name="description" style="height: 300px;">
<?php echo isset($packages->description) ? htmlspecialchars($packages->description) : ''; ?>
</textarea>
</div>
<div class="form-group">
<label for="cost" class="control-label">Price</label>
<input type="number" step="any" class="form-control" name="cost"
value="<?php echo isset($packages->cost) ? $packages->cost : '0'; ?>" required>
</div>
<div class="form-group">
<label for="status" class="control-label">Status</label>
<select name="status" class="custom-select">
<option value="1" <?php echo isset($packages->status) && $packages->status == 1 ? 'selected' : ''; ?>>Active</option>
<option value="0" <?php echo isset($packages->status) && $packages->status == 0 ? 'selected' : ''; ?>>Inactive</option>
</select>
</div>
<div class="form-group">
<label class="control-label">Images</label>
<div class="custom-file">
<input type="hidden" name="current_image"
value="<?php echo isset($packages->upload_path) ? $packages->upload_path : ''; ?>">
<input type="file" class="custom-file-input" name="image" accept="image/*">
<label class="custom-file-label btn btn-primary text-light">Choose file</label>
<small class="text-danger"><?php echo isset($error) ? $error : ''; ?></small>
</div>
</div>
<div class="card-footer">
<input type="submit" name="packageData" class="btn btn-success" value="Save">
<a class="btn btn-flat btn-default" href="<?php echo base_url('admin/packages'); ?>">Cancel</a>
</div>
</form>
</div>? 安全提示:使用 htmlspecialchars() 输出用户数据,防止 XSS;使用 $this->input->post(NULL, TRUE) 启用全局 XSS 过滤(需在 config/config.php 中启用 global_xss_filtering = TRUE)。
⚠️ 关键注意事项
- 不要在视图中用 if (isset($packages)) 控制表单显示 —— 这是导致新增表单消失的主因;
- <form> 的 action 留空即可,依靠当前 URL(insert_package 或 edit_package/5)自动路由到对应控制器方法;
- 模型方法应保持职责单一:insert() 只插入,update() 只更新,业务逻辑交由控制器协调;
- 图片上传后,旧图清理需手动实现(如更新时删除原图),本例未包含,生产环境建议补充;
- 重定向前务必使用 redirect() 而非 load->view(),避免重复提交(PRG 模式)。
通过以上重构,你将获得一个健壮、可维护、符合 RESTful 风格的单表单双模式解决方案 —— 一套视图、一套验证、一个保存入口,彻底告别表单重复与逻辑割裂。

















