Weave Code
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

Cartesian Product Laravel Package

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().

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require th3n3rd/cartesian-product
    

    No additional configuration is required—Laravel’s autoloader will handle the package automatically.

  2. First Use Case: Dynamic Query Builder Clauses: Generate all possible combinations of Eloquent query scopes or where clauses for a complex search API.

    use Nerd\CartesianProduct\CartesianProduct;
    
    $scopes = [
        ['active' => true],
        ['published' => true],
        ['category_id' => [1, 2, 3]],
    ];
    
    $cartesian = CartesianProduct::of($scopes);
    
    foreach ($cartesian as $combination) {
        $query = User::query();
        foreach ($combination as $key => $value) {
            if (is_array($value)) {
                $query->whereIn($key, $value);
            } else {
                $query->where($key, $value);
            }
        }
        // Process each unique query combination (e.g., log, cache, or execute)
    }
    
  3. Where to Look First:

    • README.md: Focus on the CartesianProduct::of() and CartesianProduct::empty()->with() methods for basic usage.
    • Release Notes: Check for Laravel-specific optimizations or PHP 8+ features (e.g., named arguments).
    • Source Code: Explore src/CartesianProduct.php for iterator logic if extending functionality.

Implementation Patterns

Usage Patterns

  1. Lazy Evaluation (Iterator Pattern): Use iterators for memory efficiency, especially with large datasets (e.g., >1000 combinations).

    $combinations = CartesianProduct::of([
        ['size' => ['S', 'M', 'L']],
        ['color' => ['red', 'blue']],
        ['material' => ['cotton', 'polyester']],
    ]);
    
    foreach ($combinations as $combination) {
        // Process one combination at a time (e.g., generate product variants)
        ProductVariant::create($combination);
    }
    
  2. Chaining for Dynamic Inputs: Build combinations incrementally using the fluent with() method.

    $cartesian = CartesianProduct::empty();
    if ($request->has('filters')) {
        $cartesian->with($request->filters);
    }
    if ($request->has('advanced_filters')) {
        $cartesian->with($request->advanced_filters);
    }
    
  3. Integration with Laravel Collections: Convert to a Collection for further processing (e.g., filtering, mapping).

    $collection = collect(iterator_to_array(CartesianProduct::of($arrays)));
    $filtered = $collection->filter(fn ($item) => $item['price'] > 100);
    
  4. Async Processing with Queues: Offload large Cartesian products to background jobs to avoid timeouts.

    class GenerateCombinationsJob implements ShouldQueue {
        public function handle() {
            $combinations = CartesianProduct::of($this->arrays);
            foreach ($combinations as $combination) {
                // Process or store each combination
            }
        }
    }
    
  5. Streaming API Responses: Stream combinations directly to HTTP responses for memory efficiency.

    return response()->stream(function () use ($combinations) {
        foreach ($combinations as $combination) {
            echo json_encode($combination) . PHP_EOL;
        }
    }, 200, ['Content-Type' => 'application/x-ndjson']);
    

Workflows

  1. Rule Engine: Generate all possible rule permutations for a dynamic workflow system.

    $rules = [
        ['action' => 'notify', 'condition' => 'high_priority'],
        ['action' => 'escalate', 'condition' => 'unresolved'],
    ];
    $ruleCombinations = CartesianProduct::of($rules);
    
  2. Test Data Generation: Create edge-case test inputs for API validation.

    $testInputs = CartesianProduct::of([
        ['user_id' => [1, null]],
        ['role' => ['admin', 'editor']],
        ['status' => ['active', 'suspended']],
    ]);
    
  3. Recommendation Engine: Generate "frequently bought together" product bundles.

    $productPairs = CartesianProduct::of([
        ['product_id' => [101, 102, 103]],
        ['bundle_id' => [201, 202]],
    ]);
    

Integration Tips

  1. Laravel Service Container: Bind the CartesianProduct class for dependency injection.

    $this->app->bind(CartesianProduct::class, function () {
        return new CartesianProduct();
    });
    
  2. Collection Macros: Extend Laravel Collections with Cartesian functionality.

    Collect::macro('cartesian', function ($arrays) {
        return $this->pipe(fn ($collection) => CartesianProduct::of($arrays));
    });
    // Usage:
    $combinations = collect([])->cartesian($arrays);
    
  3. Eloquent Relationships: Dynamically generate relationship combinations for polymorphic queries.

    $relationships = [
        ['type' => 'posts', 'id' => [1, 2]],
        ['type' => 'comments', 'id' => [10, 20]],
    ];
    $query = User::query();
    foreach (CartesianProduct::of($relationships) as $rel) {
        $query->orWhereHas($rel['type'], fn ($q) => $q->where('id', $rel['id']));
    }
    
  4. Caching: Cache toArray() results for repeated computations (e.g., precomputed product bundles).

    $key = 'product_bundles_' . md5(serialize($arrays));
    $bundles = Cache::remember($key, now()->addHours(1), function () use ($arrays) {
        return CartesianProduct::of($arrays)->toArray();
    });
    

Gotchas and Tips

Pitfalls

  1. Memory Spikes with toArray():

    • Issue: Calling toArray() on large datasets can exhaust memory.
    • Fix: Always prefer iterators for large Cartesian products. Document this in your team’s coding guidelines.
      // Avoid for large datasets:
      $result = $cartesian->toArray(); // Risky!
      // Prefer:
      foreach ($cartesian as $item) { ... }
      
  2. Empty Input Handling:

    • Issue: CartesianProduct::of([]) or CartesianProduct::empty() returns an empty iterator, which may not match expectations.
    • Fix: Validate inputs or provide default values.
      $arrays = $request->input('arrays', [['default' => 'value']]);
      $cartesian = CartesianProduct::of($arrays);
      
  3. Non-Array Inputs:

    • Issue: Passing non-array inputs (e.g., strings, objects) may cause silent failures or errors.
    • Fix: Add input validation.
      foreach ($arrays as $array) {
          if (!is_array($array)) {
              throw new \InvalidArgumentException('All inputs must be arrays.');
          }
      }
      
  4. Thread Safety with toArray():

    • Issue: Concurrent calls to toArray() may cause race conditions or memory issues.
    • Fix: Restrict toArray() to single-threaded contexts or use iterators in async environments.
      // Safe for async:
      foreach ($cartesian as $item) { ... }
      // Avoid in async:
      $result = $cartesian->toArray(); // Not thread-safe!
      
  5. Performance Overhead for Small Datasets:

    • Issue: Iterator overhead may be slower than native solutions for small Cartesian products (e.g., <100 items).
    • Fix: Benchmark against alternatives like array_product or nested loops for small use cases.
      if (count($arrays) < 3 && array_reduce($arrays, fn ($carry, $item) => $carry * count($item), 1) < 100) {
          // Use native solution for small datasets
      }
      

Debugging

  1. Iterator Debugging:

    • Use iterator_count() to check the total number of combinations (if supported by your PHP version).
      $count = iterator_count($cartesian);
      
    • Log individual tuples to trace execution:
      foreach ($cartesian as $index => $tuple) {
          Log::debug("Tuple {$index}: " . json_encode($tuple));
      }
      
  2. Memory Profiling:

    • Monitor memory usage during iteration:
      $startMemory = memory_get_usage();
      foreach ($cartesian as $tuple) {
          // Process tuple
      }
      Log::debug("Memory used: " . (memory_get_usage() - $startMemory) . " bytes");
      
    • Use
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.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle