Recursion

I’m not going to go into great detail on this, so hopefully the code speaks for itself. But here’s a high level overview.

Imagine you have three types, Product, Product Variation, and Product Variation Value. One instance of a Product could be “Pink Floyd Dark Side of the Moon T-Shirt” , with Variations of “Size”, “Sleeve Length”, and “Color”, and finally some Values such as “Small”, “Medium” and “Large” for Size.

(This is sample data that should be taken as fact. Whether or not you agree with the data model itself, or you could do better, is not germane.)

This could be modeled with the following three PHP classes:

class Product
{
    public string $productName;

    /**
     * @var ProductVariation[]
     */
    public array $variations;

    /**
     * @param string $productName
     * @param ProductVariation[] $variations
     */
    public function __construct(string $productName, array $variations)
    {
        $this->productName = $productName;
        $this->variations = $variations;
    }
}

class ProductVariation
{
    public string $variationName;

    /**
     * @var ProductVariationValue[]
     */
    public array $variationValues;

    /**
     * @param string $variationName
     * @param ProductVariationValue[] $variationValues
     */
    public function __construct(string $variationName, array $variationValues)
    {
        $this->variationName = $variationName;
        $this->variationValues = $variationValues;
    }
}

class ProductVariationValue
{
    public string $valueName;

    public function __construct(string $valueName)
    {
        $this->valueName = $valueName;
    }
}

And a single Product could be modeled as:

$product = new Product(
    'Shirt',
    [
        new ProductVariation(
            'Color',
            [
                new ProductVariationValue('Blue'),
                new ProductVariationValue('Red'),
            ]
        ),
        new ProductVariation(
            'Size',
            [
                new ProductVariationValue('S'),
                new ProductVariationValue('M'),
            ]
        ),
        new ProductVariation(
            'Sleeve Length',
            [
                new ProductVariationValue('Short'),
                new ProductVariationValue('Long'),
            ]
        ),
    ]
);

The question is, how can we make an array of every possible combination of Values. For instance, if you have two sizes, Small and Large and two colors, Blue and Red, you’d have an array of four items: Small/Blue, Small/Red, Large/Blue and Large/Red. Add two Sleeve Sizes, and there’d be either total items. Obviously that array would grow exponentially with each Variation and Value.

Well, here’s the code:

function recurse(Product $product, array $columns = null, array $current = []): array
{
    if (null === $columns) {
        $columns = [];
        foreach ($product->variations as $variation) {
            $columns[] = $variation->variationName;
        }
    }

    $thisColumn = array_shift($columns);

    $new = [];
    foreach ($product->variations as $variation) {
        if ($variation->variationName === $thisColumn) {
            foreach ($variation->variationValues as $value) {
                $x = $current;
                $x[$thisColumn] = $value->valueName;
                $new[] = $x;
            }
        }
    }

    if (count($columns)) {
        $gary = [];
        foreach ($new as $t) {
            $gary = array_merge($gary, recurse($product, $columns, $t));
        }
        $new = $gary;
    }

    return $new;
}

var_dump(recurse($product));

Which, with the provided sample data, produces:

array(8) {
  [0]=>
  array(3) {
    ["Color"]=>
    string(4) "Blue"
    ["Size"]=>
    string(1) "S"
    ["Sleeve Length"]=>
    string(5) "Short"
  }
  [1]=>
  array(3) {
    ["Color"]=>
    string(4) "Blue"
    ["Size"]=>
    string(1) "S"
    ["Sleeve Length"]=>
    string(4) "Long"
  }
  [2]=>
  array(3) {
    ["Color"]=>
    string(4) "Blue"
    ["Size"]=>
    string(1) "M"
    ["Sleeve Length"]=>
    string(5) "Short"
  }
  [3]=>
  array(3) {
    ["Color"]=>
    string(4) "Blue"
    ["Size"]=>
    string(1) "M"
    ["Sleeve Length"]=>
    string(4) "Long"
  }
  [4]=>
  array(3) {
    ["Color"]=>
    string(3) "Red"
    ["Size"]=>
    string(1) "S"
    ["Sleeve Length"]=>
    string(5) "Short"
  }
  [5]=>
  array(3) {
    ["Color"]=>
    string(3) "Red"
    ["Size"]=>
    string(1) "S"
    ["Sleeve Length"]=>
    string(4) "Long"
  }
  [6]=>
  array(3) {
    ["Color"]=>
    string(3) "Red"
    ["Size"]=>
    string(1) "M"
    ["Sleeve Length"]=>
    string(5) "Short"
  }
  [7]=>
  array(3) {
    ["Color"]=>
    string(3) "Red"
    ["Size"]=>
    string(1) "M"
    ["Sleeve Length"]=>
    string(4) "Long"
  }
}