Sortation
BoxPacker (mostly) uses “online” algorithms, that is it packs sequentially, with no regard for what comes next. Therefore the order of items, or the order of boxes are of crucial importance in obtaining good results.
By default, BoxPacker will try to be as smart as possible about this, packing larger/heavier items into the bottom of a box, with smaller/lighter items that might get crushed placed above them. It will also prefer to use smaller boxes where possible, rather than larger ones.
However, BoxPacker also allows you to influence many of these decisions if you prefer.
Items
You may wish to explicitly pack heavier items before larger ones. Or larger ones before heavier ones. Or prefer to keep
items of a similar “group” together (whatever that might mean for your application). The ItemList
class supports
this via two methods.
Supplying a pre-sorted list
If you already have your items in a pre-sorted array (e.g. when using a database ORDER BY
, you can construct an
ItemList
directly from it. You can also use this mechanism if you know that all of your items have identical
dimensions and therefore having BoxPacker sort them before commencing packing would just be a waste of CPU time.
$itemList = ItemList::fromArray($anArrayOfItems, true); // set the 2nd param to true if presorted
Overriding the default algorithm
First, create your own implementation of the ItemSorter
interface implementing your particular requirements:
/**
* A callback to be used with usort(), implementing logic to determine which Item is a higher priority for packing.
*/
YourApplicationItemSorter implements DVDoug\BoxPacker\ItemSorter
{
/**
* Return -1 if $itemA is preferred, 1 if $itemB is preferred or 0 if neither is preferred.
*/
public function compare(Item $itemA, Item $itemB): int
{
// your logic to determine ordering goes here. Remember, that Item is your own object,
// and you have full access to all methods on it, not just the ones from the Item interface
}
}
Then, pass this to the ItemList
constructor
$sorter = new YourApplicationItemSorter();
$itemList = new ItemList($sorter);
Enforcing strict ordering
Regardless of which of the above methods you use, BoxPacker’s normal mode of operation is to respect the sort ordering but not at the expense of packing density. If an item in the list is too large to fit into a particular space, BoxPacker will temporarily skip over it and will try the next item in the list instead.
This typically works well for ecommerce, but in some applications you may want your custom sort to be absolutely
determinative. You can do this by calling beStrictAboutItemOrdering()
.
$packer = new Packer();
$packer->beStrictAboutItemOrdering(true); // or false to turn strict ordering off again
$volumePacker = new VolumePacker(...);
$volumePacker->beStrictAboutItemOrdering(true); // or false to turn strict ordering off again
Box types
BoxPacker’s default algorithm assumes that box size/weight is a proxy for cost and therefore seeks to use the
smallest/lightest type of box possible for a set of items. However in some cases this assumption might not be true,
or you may have alternate reasons for preferring to use one type of box over another. The BoxList
class supports
this kind of application-controlled sorting via two methods.
Supplying a pre-sorted list
If you already have your items in a pre-sorted array (e.g. when using a database ORDER BY
, you can construct an
BoxList
directly from it.
$boxList = BoxList::fromArray($anArrayOfBoxes, true); // set the 2nd param to true if presorted
Overriding the default algorithm
First, create your own implementation of the BoxSorter
interface implementing your particular requirements:
/**
* A callback to be used with usort(), implementing logic to determine which Box is "better".
*/
YourApplicationBoxSorter implements DVDoug\BoxPacker\BoxSorter
{
/**
* Return -1 if $boxA is "best", 1 if $boxB is "best" or 0 if neither is "best".
*/
public function compare(Box $boxA, Box $boxB): int
{
// your logic to determine ordering goes here. Remember, that Box is your own object,
// and you have full access to all methods on it, not just the ones from the Box interface
}
}
Then, pass this to the BoxList
constructor
$sorter = new YourApplicationBoxSorter();
$boxList = new BoxList($sorter);
Choosing between permutations
In a scenario where even the largest box type is not large enough to contain all of the items, BoxPacker needs to decide which is the “best” possible first box, so it can then pack the remaining items into a second box (and so on). If there are two different box types that each hold the same number of items (but different items), which one should be picked? What if one of the boxes can hold an additional item, but is twice as large? Is it better to minimise the number of boxes, or their volume?
By default, BoxPacker will optimise for the largest number of items in a box, with volume acting as a tie-breaker. This can also be changed:
Overriding the default algorithm
First, create your own implementation of the PackedBoxSorter
interface implementing your particular requirements:
/**
* A callback to be used with usort(), implementing logic to determine which PackedBox is "better".
*/
YourApplicationPackedBoxSorter implements DVDoug\BoxPacker\PackedBoxSorter
{
/**
* Return -1 if $boxA is "best", 1 if $boxB is "best" or 0 if neither is "best".
*/
public function compare(PackedBox $boxA, PackedBox $boxB): int
{
// your logic to determine "best" goes here
}
}
Then, pass this to the Packer
$sorter = new YourApplicationPackedBoxSorter();
$packer = new Packer();
$packer->setPackedBoxSorter($sorter);