th3n3rd/cartesian-product
Memory-efficient Cartesian Product generator for PHP. Uses iterators to yield one tuple at a time, letting you handle very large combinations without big memory usage. Build products via fluent with() calls or CartesianProduct::of(), iterate or toArray().
Installation: Add the package via Composer in your Laravel project:
composer require th3n3rd/cartesian-product
No additional configuration is required—it’s a standalone package with no Laravel-specific dependencies.
First Use Case: Dynamic Query Builder Clauses: Generate all possible combinations of filter conditions for a complex search API.
use Nerd\CartesianProduct\CartesianProduct;
// Define filter options (e.g., from a request or config)
$filters = [
['status' => ['active', 'inactive']],
['category' => ['electronics', 'clothing']],
['price_range' => ['0-50', '50-100']],
];
$cartesianProduct = CartesianProduct::of($filters);
// Stream combinations to build dynamic queries
foreach ($cartesianProduct as $combination) {
$query = Model::query();
foreach ($combination as $field => $value) {
$query->where($field, $value);
}
// Process each unique query (e.g., cache results, log, or execute)
}
Where to Look First:
CartesianProduct::of() and CartesianProduct::empty()->with() methods for chaining.foreach) over toArray() for memory efficiency.Lazy Evaluation with Iterators: Use iterators for large datasets to avoid memory spikes. Example: Generating test cases for API validation.
$testCases = CartesianProduct::of([
['user_type' => ['admin', 'guest']],
['device' => ['mobile', 'desktop']],
['location' => ['us', 'eu']],
]);
foreach ($testCases as $case) {
$response = Http::post('/api/endpoint', $case);
// Assert or log response
}
Chaining with Laravel Collections:
Combine with Laravel’s collect() for fluent operations like filtering or mapping.
$combinations = collect(CartesianProduct::of([
['size' => ['S', 'M', 'L']],
['color' => ['red', 'blue']],
])->toArray()) // Convert to array for collection
->filter(fn ($combo) => $combo['size'] !== 'M') // Skip medium sizes
->map(fn ($combo) => [
'sku' => "{$combo['size']}_{$combo['color']}",
'price' => 10 + (strlen($combo['size']) * 2),
]);
Async Processing with Queues: Offload large Cartesian products to queues for background processing.
// In a Laravel command or controller
$combinations = CartesianProduct::of([/* large arrays */]);
foreach ($combinations as $combination) {
ProcessCombinationJob::dispatch($combination);
}
Dynamic Rule Engines: Generate all possible rule permutations for a workflow engine.
$rules = CartesianProduct::of([
['action' => ['notify', 'block']],
['trigger' => ['login', 'purchase']],
['condition' => ['failed', 'success']],
]);
foreach ($rules as $rule) {
$workflow->addRule($rule);
}
API Filter Generation:
status, category).$filterCombinations = CartesianProduct::of([
['status' => request('status', ['active', 'inactive'])],
['category' => request('category', ['electronics', 'books'])],
]);
// Cache combinations for later use
Cache::put('filter_combinations', iterator_to_array($filterCombinations), now()->addHour());
Recommendation Engines:
color, size, material).recommendations table.$productCombinations = CartesianProduct::of([
['color' => ['red', 'blue']],
['size' => ['S', 'M']],
]);
foreach ($productCombinations as $combo) {
Recommendation::create([
'product_a' => Product::where('color', $combo['color'])->first(),
'product_b' => Product::where('size', $combo['size'])->first(),
'weight' => rand(1, 10),
]);
}
Data Migration:
$legacyData = [
['type' => ['A', 'B']],
['value' => [1, 2, 3]],
];
$migratedData = [];
foreach (CartesianProduct::of($legacyData) as $row) {
$migratedData[] = [
'legacy_type' => $row['type'],
'legacy_value' => $row['value'],
'normalized' => "{$row['type']}_{$row['value']}",
];
}
Laravel Service Container: Bind the CartesianProduct class for dependency injection.
// In AppServiceProvider@boot()
$this->app->bind(CartesianProduct::class, function () {
return new CartesianProduct();
});
Usage in controllers:
public function __construct(private CartesianProduct $cartesianProduct) {}
Collection Macros: Extend Laravel Collections to support Cartesian products natively.
// In AppServiceProvider@boot()
Collect::macro('cartesian', function ($arrays) {
return $this->pipe(fn ($collection) => CartesianProduct::of($arrays));
});
Usage:
$combinations = collect([])->cartesian([
['status' => ['active', 'inactive']],
['priority' => ['high', 'low']],
]);
Eloquent Relationships: Dynamically generate relationship combinations for polymorphic queries.
$relationshipCombinations = CartesianProduct::of([
['model' => [User::class, Product::class]],
['relation' => ['posts', 'reviews']],
]);
foreach ($relationshipCombinations as $combo) {
$query = $combo['model']::query()->with($combo['relation']);
// Execute or cache query
}
Testing: Use the package to generate test data or edge cases.
public function test_combinatorial_logic()
{
$testCases = CartesianProduct::of([
['input' => [1, 2]],
['operation' => ['+', '-']],
]);
foreach ($testCases as $case) {
$result = $case['input'] . $case['operation'] . '1';
$this->assertTrue(eval("return {$result};"));
}
}
Memory Spikes with toArray():
toArray() on large Cartesian products can exhaust memory.foreach) for large datasets. If you must use toArray(), ensure the dataset is small (e.g., <10,000 combinations) or process in chunks.memory_get_usage():
$startMemory = memory_get_usage();
$result = $cartesianProduct->toArray();
Log::warning("Memory used: " . (memory_get_usage() - $startMemory) . " bytes");
Empty Input Handling:
CartesianProduct::of([]) or CartesianProduct::empty() returns an empty iterator, which may not be intuitive.if (empty($arrays)) {
return collect([]); // or throw new InvalidArgumentException("Input arrays cannot be empty.");
}
Iterator State:
$iterator = CartesianProduct::of([/* arrays */]);
foreach ($iterator as $item) { /* first pass */ }
foreach ($iterator as $item) { /* will not yield
How can I help you explore Laravel packages today?