BoxPacker快速上手:用PHP算出每件商品进哪个箱子的装箱方案
【免费下载链接】BoxPacker4D bin packing / knapsack problem solver项目地址: https://gitcode.com/gh_mirrors/bo/BoxPacker
当仓库堆着上百件待发货商品、你还要手工试纸箱组合时,装箱就成了最费经验的事。BoxPacker 是一个 PHP 的 4D 装箱求解库,输入商品与纸箱的尺寸重量,直接给出每件商品进哪个箱的方案。上百件商品通常在秒级算完,接入只需实现两个接口。
它能帮你做什么
计算每件商品的落箱方案
它的主循环很简单:开一个箱子、尽量装满、再开下一个,直到所有商品放完。入口在 src/Packer.php。
- 返回 PackedBoxList,每个箱子带出装了哪些商品、各自的 x/y/z 坐标
- 同时登记多种纸箱,自动先尝试装得下全部剩余商品的箱子
- 某个商品装不下时,可选择抛异常或把剩余商品留给你处理
平衡多箱之间的重量
装完不是终点。多箱时 src/WeightRedistributor.php 会做第二轮重分配。
- 在箱子之间挪动商品,避免一箱很沉、一箱很轻
- 默认只在箱子数不超过 12 时执行,再多就跳过以节省计算时间
- 重分配时会同时校验每个箱子的最大承重
按业务规则约束摆放
不是所有商品都能任意放。约束通过接口实现,见 src/ConstrainedPlacementItem.php。
- 用 src/Rotation.php 的三个值控制朝向:固定方向、可横转 90 度、任意方向
- 实现 canBePacked() 就能表达"每箱最多几节电池"这类规则
- 用 LimitedSupplyBox 声明某规格纸箱的有限库存
从零到跑通
- 获取代码:
git clone https://gitcode.com/gh_mirrors/bo/BoxPacker,或直接作为依赖composer require dvdoug/boxpacker - 安装依赖:项目目录执行
composer install,要求 PHP 8.2 及以上 - 跑最小示例:下面 12 行代码完成加箱、加商品、求方案、读结果
use DVDoug\BoxPacker\Packer; use DVDoug\BoxPacker\Rotation; use DVDoug\BoxPacker\Test\TestBox; use DVDoug\BoxPacker\Test\TestItem; $packer = new Packer(); $packer->addBox(new TestBox(reference: '小箱', outerWidth: 300, outerLength: 200, outerDepth: 150, emptyWeight: 30, innerWidth: 296, innerLength: 196, innerDepth: 146, maxWeight: 10000)); $packer->addItem(new TestItem(description: '商品A', width: 100, length: 80, depth: 60, weight: 200, allowedRotation: Rotation::BestFit), qty: 3); $packedBoxes = $packer->pack(); echo $packedBoxes->count(), ' 个箱子被打开';跑通后你应该看到:控制台打印"1 个箱子被打开",说明 3 件商品全部装进了小箱。
想要更完整的输入格式,看 tests/ 里的现成实现和 docs/getting-started.rst,不必贴测试代码,照着改就行。
深入用法与关键配置
如何给装箱计算设置超时
适用场景:商品多或纸箱种类多时,计算时间不可控,不能让请求无限等下去。
use DVDoug\BoxPacker\DefaultTimeoutChecker; $packer->setTimeoutChecker(new DefaultTimeoutChecker(timeout: 5.0)); // 单位:秒预期效果:任何一步检查发现超过 5 秒就抛出 TimeoutException,你可以捕获后走降级逻辑,而不是干等。
如何处理塞不进去的商品
适用场景:某件商品比所有纸箱都大,默认行为是抛 NoBoxesAvailableException 中断整个计算。
一行改动:$packer->throwOnUnpackableItem(false)。
预期效果:pack() 正常返回已装好的箱子,没塞进的商品留在 src/Packer.php 的 getUnpackedItems() 里,交给人工或二次装箱。
如何限制商品摆放方向
适用场景:易碎、带"此面向上"标识的商品不能侧放。
在 Item 实现的 getAllowedRotation() 里返回值即可:Rotation::Never只允许定义方向,Rotation::KeepFlat可横转 90 度,Rotation::BestFit任意朝向。
预期效果:侧放方案从搜索结果中直接排除,不会出现在最终箱子里。
它适合谁
电商订单发货
订单确认后,你需要马上知道用几个箱、什么规格。
- 下单时直接算出纸箱规格与数量,替代人工估箱
- 箱子实际重量可直接对接承运商 API,运费预估与实际偏差更小
- 打印的运单数等于返回的箱子数,不缺单也不浪费
仓库出库复核
仓管按方案装箱,复核靠清单核对。
- 每个商品带 x/y/z 坐标,打印出库清单后与实物逐项对照
- 多箱时重量自动再平衡,避免一箱沉一箱轻,托盘码放更稳
- 方案与实物对不上时重跑一次即可,不用手工重排
物流报价与批量规划
批量询价要快速给出箱数和成本。
- 输入商品清单即得总箱数,报价不靠手工试错
- LimitedSupplyBox 声明纸箱库存,方案不会推荐你没有的箱
- 需要对比方案时,packAllPermutations() 能列出全部箱型组合,箱型多时较慢,慎用
内部怎么运作
- Packer:src/Packer.php — 主入口,逐箱试装直到全部放完,最后触发重量平衡
- VolumePacker:src/VolumePacker.php — 负责单箱内部的摆放,逐件落位
- WorkingVolume:src/WorkingVolume.php — 记录箱内剩余可用空间,每放一件更新一次
- OrientatedItemFactory:src/OrientatedItemFactory.php — 按允许朝向展开商品的所有合法摆法并排序
- WeightRedistributor:src/WeightRedistributor.php — 全部装完后跨箱挪动商品、均衡重量
- DefaultTimeoutChecker:src/DefaultTimeoutChecker.php — 每步检查耗时,超限抛 TimeoutException
数据流就一条线:商品和箱子进入 Packer,分发给 VolumePacker 逐箱填充,WorkingVolume 保证每次落位不重叠,最后 WeightRedistributor 匀一遍重量,以 PackedBoxList 交还给你。
上生产前注意什么
- 当商品数量上千或箱型超过十几种时,建议先挂上 TimeoutChecker,用真实订单数据压测一遍再定阈值
- 当有商品装不进任何箱子时,默认抛 NoBoxesAvailableException;想降级就
throwOnUnpackableItem(false),再从 getUnpackedItems() 取回剩余商品 - 当箱子数超过 12(maxBoxesToBalanceWeight 默认值)时,重量平衡会被跳过,需要时调高该值
- 当箱型较多时,不要在生产路径调用 packAllPermutations(),组合数量会随箱型数膨胀
参与与支持
- Issues 入口:功能建议与缺陷直接提交到仓库 Issues,附上最小复现的商品与箱子尺寸
- 完整文档:本地 docs/ 目录覆盖从安装到约束摆放的全部主题,这篇示例的展开版在 docs/getting-started.rst
- 示例代码:tests/ 里有可直接借鉴的 Box 与 Item 实现,以及公开测试用例的回归数据
写在最后
手工试纸箱组合这件事,可以交给代码了。花 5 分钟:装好依赖,把上面 12 行示例跑一遍,再用你自己业务的对象替换 TestBox 和 TestItem。当 pack() 返回的每个箱子都能说清装了什么、多重、放在哪个坐标,它就算正式上岗。MIT 协议的开源项目,接入成本就是实现两个接口。
【免费下载链接】BoxPacker4D bin packing / knapsack problem solver项目地址: https://gitcode.com/gh_mirrors/bo/BoxPacker
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考