Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Arr Laravel Package

houdunwang/arr

A lightweight PHP array helper library from houdunwang, offering convenient utilities for manipulating, converting, filtering, and querying arrays. Useful for Laravel and standalone PHP projects that need cleaner array handling without extra dependencies.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require houdunwang/arr

There is no Laravel service provider or auto-discovered facade — this is a plain static utility class. Import it directly:

use Houdunwang\Arr\Arr;

Where to look first

  1. src/Arr.php (or src/ArrFacade.php in some releases) — the entire surface area is a handful of public static methods. Skim the method list once; that is the documentation.
  2. composer.json — confirm the PHP constraint before installing on an older runtime. Older tags target PHP 5.6/7.x.
  3. The README is Chinese-only and thin. Treat the source as canonical: method signatures, default argument order, and the key names used for children (_child, children, or similar) differ between versions. Read the actual tree()/cat() signature in the file you installed before writing code against it.

First use case: flat category table → nested tree

This is what the package exists for. The typical starting point is an adjacency-list table (id, pid/parent_id, name) that you've already loaded:

$rows = Category::orderBy('sort')->get()->toArray();

$tree = Arr::tree($rows, 0, 'id', 'pid', 'name');
// -> nested array, each node carrying a children key

Once you can produce that, children(), parent(), and the level-flattening helpers all fall out of the same data shape, so building the tree correctly is 80% of the work.

Implementation Patterns

Category tree, cached

tree() is pure array work and does an O(n) pass, but you still don't want to run it on every request against a full table.

$tree = Cache::rememberForever('cat.tree', function () {
    return Arr::tree(Category::orderBy('sort')->get()->toArray(), 0, 'id', 'pid', 'name');
});

Invalidate on any write to the category table — a model observer with Cache::forget('cat.tree') on saved/deleted is enough.

Flattened tree for <select> dropdowns

Trees are for rendering; dropdowns need an indented flat list. Combine tree() with your recursive flatten, or use the package's level helper if your version has one (it usually stamps a level / _level field onto each node):

$flat = [];
$walk = function ($nodes, $depth) use (&$walk, &$flat) {
    foreach ($nodes as $n) {
        $flat[] = ['id' => $n['id'], 'label' => str_repeat(' ', $depth) . $n['name']];
        if (!empty($n['_child'])) {
            $walk($n['_child'], $depth + 1);
        }
    }
};
$walk($tree, 0);

Key detail: the children key name is version-specific. Dump one node with dd($tree[0]) on day one and hardcode the right key in a single helper rather than scattering the literal across views.

Breadcrumb / ancestor chain

Use the parent-lookup helper to walk up from a node id, or build it yourself from a keyed map — the map approach is often clearer and lets you skip a DB hit per level:

$byId = Category::pluck('name', 'id'); // id => name
$chain = [];
for ($id = $current; $id && isset($byId[$id]); $id = $parents[$id] ?? 0) {
    array_unshift($chain, $byId[$id]);
}

If you use Arr::parent(), it typically returns an array of ancestor rows, ordered root-first or leaf-first depending on version — assert the order in a test before wiring it into a view.

SKU generation via Cartesian product

The other headline feature. Given per-attribute option lists, produce every combination:

$specs = [
    ['红色', '蓝色'],
    ['S', 'M', 'L'],
    ['棉', '涤纶'],
];

$combos = Arr::cartesian($specs);
// 12 combinations; each is an array of one value per attribute

Pattern that works well: keep the Cartesian call as the only thing this package does in your SKU service, and map its output to your own DTO immediately. Then the storage layer (one product_skus row per combination, keyed by a hash of the option ids) never depends on the package's return shape.

foreach ($combos as $combo) {
    Sku::firstOrCreate(
        ['product_id' => $product->id, 'signature' => implode('|', $combo)],
        ['stock' => 0]
    );
}

firstOrCreate keyed on a signature means editing a product's spec list is idempotent — existing SKUs survive, new ones appear, and you soft-delete orphans rather than truncating the table and losing stock counts.

Bridging Eloquent output

The package works on plain arrays. Always ->toArray() an Eloquent collection or ->get()->toArray() a builder result first; passing a Collection or a Model will either fatal or silently produce garbage. Likewise, results come back as arrays — if you need models again, re-query by the ids you got, don't try to reconstruct models.

Gotchas and Tips

  • Static-only API. No constructor, no container binding, no injection. You cannot mock it in tests and you cannot swap the implementation. If that matters, wrap it in a thin TreeBuilder / SkuGenerator class of your own and call the package from inside — then mock your class.
  • Argument order is the #1 source of bugs. tree($data, $parentId, $idField, $pidField, $nameField) is the common shape, but forks and tags shuffle the middle parameters. Read the installed source, not the README, not a blog post.
  • Field names are matched literally. 'parent_id' vs 'pid' vs 'parentId' — no normalization, no type coercion. A string '0' parent id from a MySQL driver vs an int 0 from a seeder will silently drop rows out of the tree. Cast ids to int before treeing, or at least verify the root level populates.
  • The root parent value matters. Many implementations assume 0. If your schema uses NULL for roots, either COALESCE it in SQL or normalize in PHP first.
  • Cartesian product blows up fast. 5 attributes × 6 options = 7,776 rows before you've added a single variant. Guard the input: cap total combinations (e.g. reject > 1,000) at the controller/service layer and return a validation error. Also cap the number of attributes. This is a DoS vector disguised as a product form.
  • Empty arrays behave differently than you expect. cartesian([]) and cartesian([[]]) are not the same. Test both if specs are optional.
  • Memory on large trees. tree() on 50k categories is fine occasionally, terrible per-request. Cache aggressively; the cache key is the whole table version.
  • Mutation vs return. Some versions mutate the input array by reference, others return a new one. Don't assume the input is untouched — pass a copy if you reuse $rows afterward.
  • Chinese-only docs and error messages. Expect preg/type errors without English context. var_dump at the call site beats searching issues.
  • Low adoption (0 dependents). No community momentum, no guarantees on bug fixes. For a Laravel app, evaluate whether the battle-tested alternatives cover your case first: kalnoy/nestedset (materialized-path trees, writes stay fast) and staudenmeir/laravel-adjacency-list (recursive CTE queries, no full-table load). Reach for houdunwang/arr when you specifically want the Cartesian-product helper or you're already in the Houdunwang ecosystem.
  • Extension points are essentially nil. There's no config file, no interface, no events. If you need custom behavior, subclass or wrap — don't try to hook it.
  • Vendor it if you depend on it heavily. With zero stars and zero dependents, a composer update can remove or rewrite the repo. Fork it into your own namespace, or pin an exact version, for anything load-bearing.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource