ozdemir/subset-finder 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

ozdemir/subset-finder

Composer 安装命令:

composer require ozdemir/subset-finder

包简介

Find how many complete sets can be built from a pool of item quantities - cart promotions, bundles, inventory allocation.

README 文档

README

Latest Version on Packagist GitHub Tests Action Status License

A dependency-free PHP package for finding subsets within collections based on quantity criteria.

Given a pool of items with quantities, it answers: "How many complete sets can I build, which items go into them, and what is left over?" — useful for bundle pricing, cart discounts ("buy 5 of X and 2 of Y"), and inventory allocation.

▶ See it in action — five practical use cases (cart promotions, gift boxes, assembly, capacity planning, shared stock) with output captured from the actual package. The page is generated by php docs/build.php.

Features

  • Pure arithmetic solver: quantities are never expanded into unit items, so memory stays flat and quantities in the billions solve in milliseconds
  • Overlap aware: subsets sharing the same item ids draw from a shared pool and are never double counted
  • Flexible ordering: allocate cheapest (or any sort order) items first
  • Type safe: PHP 8.2+, strict Subsetable interface
  • Zero dependencies: plain PHP; accepts arrays or any iterable (including Laravel collections)

Installation

composer require ozdemir/subset-finder

Quick Start

use Ozdemir\SubsetFinder\Subset;
use Ozdemir\SubsetFinder\SubsetCollection;
use Ozdemir\SubsetFinder\SubsetFinder;
use Ozdemir\SubsetFinder\SubsetFinderConfig;

// Define your collection and subset criteria.
// Any iterable works: a plain array, a generator, or a Laravel collection.
$collection = [
    new Product(id: 1, quantity: 11, price: 15),
    new Product(id: 2, quantity: 6, price: 5),
    new Product(id: 3, quantity: 6, price: 5),
];

$subsetCollection = new SubsetCollection([
    Subset::of([1, 2])->take(5), // Each set needs 5 items from products 1 and 2
    Subset::of([3])->take(2),    // ...and 2 items from product 3
]);

// Allocate the cheapest items first
$config = new SubsetFinderConfig(sortField: 'price');

$subsetFinder = new SubsetFinder($collection, $subsetCollection, $config);
$subsetFinder->solve();

$subsetFinder->getSubsetQuantity(); // 3 — max number of complete sets
$subsetFinder->getFoundSubsets();   // Subsetable[] — id 2 ×6, id 1 ×9, id 3 ×6 (cheapest first)
$subsetFinder->getRemaining();      // Subsetable[] — id 1 ×2 left over

The Subsetable interface

Collection items must implement Subsetable:

use Ozdemir\SubsetFinder\Subsetable;

class Product implements Subsetable
{
    public function __construct(
        public int|string $id,
        public int $quantity,
        public float $price,
    ) {
    }

    public function getId(): int|string
    {
        return $this->id;
    }

    public function getQuantity(): int
    {
        return $this->quantity;
    }

    public function setQuantity(int $quantity): void
    {
        $this->quantity = $quantity;
    }
}

Item ids and quantities are read through the interface, so your property names don't matter. Only sortField in the config refers to a property of your objects.

Configuration

$config = new SubsetFinderConfig(
    sortField: 'price',    // Property used to order allocation (default: 'id')
    sortDescending: false  // Ascending = cheapest first (default)
);

Using the Trait

Add subset operations to any iterable collection class of your own — for example a Laravel collection:

use Illuminate\Support\Collection;
use Ozdemir\SubsetFinder\Traits\HasSubsetOperations;

class ProductCollection extends Collection
{
    use HasSubsetOperations;
}

$products = new ProductCollection([/* Subsetable items */]);

$subsetFinder = $products->findSubsets($subsetCollection);
$products->canSatisfySubsets($subsetCollection);   // bool
$products->getMaxSubsetQuantity($subsetCollection); // int

Other methods

$subsetFinder->getSubsetItems(10);          // First 10 units in sort order
$subsetFinder->isOptimal();                 // true if nothing is left over
$subsetFinder->getEfficiencyPercentage();   // Used / total quantity
$subsetFinder->getPerformanceMetrics();     // Timing and counts of the last solve()

When it fits

The solver models fungible quantity pools: units of the same id are interchangeable, and an item's quantity can split freely across sets (goods, portions, hours, credits). It does not model per-set distinctness — if each set needs N different individuals (e.g. two distinct people per shift), that's an assignment problem, not a quantity pool.

How it works

  1. Quantities are aggregated per item id; items are sorted by sortField.
  2. The maximum number of complete sets is found by binary search. For each candidate, subsets claim quantities from the shared pool in definition order, consuming items in sort order.
  3. The winning allocation becomes getFoundSubsets(); whatever is left becomes getRemaining().

The solver never materializes individual units, so runtime and memory depend on the number of distinct items, not their quantities.

Error Handling

use Ozdemir\SubsetFinder\Exceptions\InsufficientQuantityException;
use Ozdemir\SubsetFinder\Exceptions\InvalidArgumentException;

try {
    $subsetFinder = new SubsetFinder($collection, $subsetCollection);
    $subsetFinder->solve();
} catch (InvalidArgumentException $e) {
    // Empty collection, or items not implementing Subsetable
} catch (InsufficientQuantityException $e) {
    // Not even one complete set can be built
}

Testing

# Run tests
composer test

# Run tests with coverage
composer test-coverage

# Run static analysis
composer analyse

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

The MIT License (MIT). Please see License File for more information.

ozdemir/subset-finder 适用场景与选型建议

ozdemir/subset-finder 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 8 次下载、GitHub Stars 达 2, 最近一次更新时间为 2024 年 02 月 06 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「collections」 「laravel」 「subset」 「ozdemir」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 ozdemir/subset-finder 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 ozdemir/subset-finder 我们能提供哪些服务?
定制开发 / 二次开发

基于 ozdemir/subset-finder 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 8
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 14
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 2
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2024-02-06