Laravel干预模块中基于已用数量筛选产品的实现方法
Laravel干预管理应用:产品列表动态筛选实现方案
场景与需求
我开发的Laravel应用用于管理干预(interventions)并追踪产品使用情况,涉及三张关联表:
sorties_approuvees:已批准产品出库表(存储产品总数量等基础信息)interventions:干预表(记录干预的基本信息)intervention_sortie_approuvee:中间表,关联干预与已批准出库产品,记录该干预使用的产品数量
核心功能需求:
- 新增干预:产品列表仅展示总数量 > 已用总数量的产品(已耗尽的产品不显示)
- 修改干预:产品列表需包含两类产品:
- 总数量 > 已用总数量的可用产品
- 当前干预已关联的产品(即使该产品已被完全使用)
- 所有表单通过Bootstrap模态框(Modal)弹窗展示
示例场景:
- 电缆总数量2,已用2;灯具总数量5,已用3
- 新增干预时,仅显示灯具
- 修改某使用了2根电缆+3盏灯具的干预时,电缆和灯具都会显示
表结构
-- 已批准产品出库表 CREATE TABLE sorties_approuvees ( id INT PRIMARY KEY AUTO_INCREMENT, designation VARCHAR(255) NOT NULL, -- 产品名称 quantite_total INT NOT NULL, -- 产品总数量 -- 其他业务字段... ); -- 干预表 CREATE TABLE interventions ( id INT PRIMARY KEY AUTO_INCREMENT, libelle VARCHAR(255) NOT NULL, -- 干预名称 -- 其他业务字段... ); -- 关联中间表 CREATE TABLE intervention_sortie_approuvee ( id INT PRIMARY KEY AUTO_INCREMENT, intervention_id INT NOT NULL, sortie_approuvee_id INT NOT NULL, quantite_utilisee INT NOT NULL, -- 该干预使用的产品数量 FOREIGN KEY (intervention_id) REFERENCES interventions(id), FOREIGN KEY (sortie_approuvee_id) REFERENCES sorties_approuvees(id) );
Laravel模型关联
Intervention.php(干预模型)
namespace App\Models; use Illuminate\Database\Eloquent\Model; class Intervention extends Model { protected $fillable = ['libelle']; // 关联已批准出库产品 public function sortieApprouvees() { return $this->belongsToMany(SortieApprouvee::class, 'intervention_sortie_approuvee') ->withPivot('quantite_utilisee'); } }
SortieApprouvee.php(已批准出库产品模型)
namespace App\Models; use Illuminate\Database\Eloquent\Model; class SortieApprouvee extends Model { protected $fillable = ['designation', 'quantite_total']; // 关联干预 public function interventions() { return $this->belongsToMany(Intervention::class, 'intervention_sortie_approuvee') ->withPivot('quantite_utilisee'); } }
控制器逻辑
列表页与新增数据准备(Index方法)
namespace App\Http\Controllers; use App\Models\Intervention; use App\Models\SortieApprouvee; use Illuminate\Http\Request; class InterventionController extends Controller { public function index() { // 新增干预时的可用产品:总数量 > 已用总数量 $productsForCreate = SortieApprouvee::query() ->withSum('interventions', 'quantite_utilisee') ->havingRaw('quantite_total > interventions_sum_quantite_utilisee') ->get(); return view('interventions.index', compact('productsForCreate')); } // 编辑干预时获取产品与干预数据 public function edit(Intervention $intervention) { // 获取当前干预关联的产品ID集合 $associatedProductIds = $intervention->sortieApprouvees()->pluck('sorties_approuvees.id')->toArray(); // 筛选产品:可用产品 或 当前干预关联的产品 $productsForEdit = SortieApprouvee::query() ->withSum('interventions', 'quantite_utilisee') ->where(function ($query) use ($associatedProductIds) { $query->havingRaw('quantite_total > interventions_sum_quantite_utilisee') ->orWhereIn('id', $associatedProductIds); }) ->get(); return response()->json([ 'intervention' => $intervention, 'products' => $productsForEdit, 'associatedProducts' => $intervention->sortieApprouvees->pluck('id')->toArray() ]); } }
视图与前端实现
列表页编辑按钮
<button class="btn btn-sm btn-warning btn-edit" data-id="{{ $intervention->id }}">编辑干预</button>
新增/编辑模态框
<!-- 新增干预模态框 --> <div class="modal fade" id="createInterventionModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">新增干预</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <form action="{{ route('interventions.store') }}" method="POST"> @csrf <div class="mb-3"> <label for="createLibelle" class="form-label">干预名称</label> <input type="text" class="form-control" id="createLibelle" name="libelle" required> </div> <div class="mb-3"> <label class="form-label">选择产品</label> <select name="products[]" multiple class="form-select"> @foreach($productsForCreate as $product) <option value="{{ $product->id }}"> {{ $product->designation }} (剩余: {{ $product->quantite_total - $product->interventions_sum_quantite_utilisee }}) </option> @endforeach </select> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button> <button type="submit" class="btn btn-primary">保存干预</button> </div> </form> </div> </div> </div> </div> <!-- 编辑干预模态框 --> <div class="modal fade" id="editInterventionModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">编辑干预</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <form id="editInterventionForm"> @csrf @method('PUT') <input type="hidden" id="editInterventionId" name="id"> <div class="mb-3"> <label for="editLibelle" class="form-label">干预名称</label> <input type="text" class="form-control" id="editLibelle" name="libelle" required> </div> <div class="mb-3"> <label class="form-label">选择产品</label> <select name="products[]" multiple class="form-select" id="editProductSelect"> <!-- 动态填充 --> </select> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button> <button type="submit" class="btn btn-primary">更新干预</button> </div> </form> </div> </div> </div> </div>
前端交互脚本
// 编辑按钮点击事件 document.querySelectorAll('.btn-edit').forEach(btn => { btn.addEventListener('click', async () => { const interventionId = btn.getAttribute('data-id'); const res = await fetch(`/interventions/${interventionId}/edit`); const data = await res.json(); // 填充干预基础信息 document.getElementById('editInterventionId').value = data.intervention.id; document.getElementById('editLibelle').value = data.intervention.libelle; // 动态填充产品选择列表 const productSelect = document.getElementById('editProductSelect'); productSelect.innerHTML = ''; data.products.forEach(product => { const remaining = product.quantite_total - product.interventions_sum_quantite_utilisee; const option = document.createElement('option'); option.value = product.id; option.textContent = `${product.designation} (剩余: ${remaining >= 0 ? remaining : 0})`; // 标记当前干预已关联的产品为选中 if (data.associatedProducts.includes(product.id)) { option.selected = true; } productSelect.appendChild(option); }); // 打开编辑模态框 new bootstrap.Modal(document.getElementById('editInterventionModal')).show(); }); }); // 处理编辑表单提交 document.getElementById('editInterventionForm').addEventListener('submit', async (e) => { e.preventDefault(); const interventionId = document.getElementById('editInterventionId').value; const formData = new FormData(e.target); await fetch(`/interventions/${interventionId}`, { method: 'PUT', body: formData, headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content } }); // 刷新页面或更新列表 location.reload(); });
功能验证
按照示例场景测试:
- 新增干预时,
productsForCreate仅返回灯具(电缆已耗尽,不符合总数量>已用数量的条件) - 编辑目标干预时,
productsForEdit同时返回电缆和灯具:电缆属于当前干预关联产品,灯具属于可用产品
内容的提问来源于stack exchange,提问作者Abdourahman
相关产品推荐
相关产品推荐

