6

I am writing an ecommerce site, and need a good way to calculate product variations. The Site has products, products can have many option groups, option groups can have many options.

So a Tshirt Product has 3 option groups and options:

Size: Small, Medium, Large,

Color: Red, Blue, Yellow, Black,

Material: Cotton, Nylon,

which creates: small red cotton, small red nylon, small blue cotton, small blue nylon, ... so on and so forth

I know that the script below works, but also that it can be optimized. Can anyone furnish a better working example of this? It should be possible using recursion as well... but I'm hitting a mental block.

    if(count($option_groups) > 1)
    {
        // start the variants up
        foreach($option_groups[0]->get_options() as $option)
        {
            $variants[] = array($option);
        }

        // go through every other option group to make combos
        for($x = 1; $x < count($option_groups); $x++)
        {
            $combos = array();

            foreach($variants as $variant)
            {
                $new = array();
                foreach($option_groups[$x]->get_options() as $option)
                {
                    $tmp        = $variant;
                    $tmp[]  = $option;
                    $new[]  = $tmp;
                }
                $combos[] = $new;
            }
            $variants = array();
            foreach($combos as $combo)
            {
                foreach($combo as $tmp)
                {
                    $variants[] = $tmp;
                }
            }
        }
    }

This isn't super time sensitive, but I'd like to have a more maintainable chunk of code, this is pretty gross.

Also does this problem (I feel like it isn't an original problem, many carts do this) have a name? I wasn't pulling anything up on google for this problem.

EDIT This is what I ended up with, its based off of profitphp's solution, but maintains my objects instead of giving me the options per variant concatenated as a string. All thanks to Profitphp!

private function _possible_combos($groups, $prefix = array())
{
    $result = array();
    $group  = array_shift($groups);
    foreach($group->get_options() as $selected)
    {
        if($groups)
        {
            $tmp            = $prefix;
            $tmp[]      = $selected;
          $result = array_merge($result, $this->_possible_combos($groups, $tmp));
        }
        else
        {
            $tmp            = $prefix;
            $tmp[]      = $selected;
          $result[] = $tmp; 
        }
    }

    return $result;
}
Francis Yaconiello
  • 10,829
  • 2
  • 35
  • 54
  • 1
    It's funny, this is a question I give at interviews, near enough. – Orbling Sep 06 '11 at 17:59
  • Don't suppose you've gotten any good (or at least better than what I wrote) answers? – Francis Yaconiello Sep 06 '11 at 18:01
  • As a Haskell evangelist, I can't help but boast that this function is built-in: `sequence [["small", "medium", "large"], ["red", "blue", "yellow", "black"], ["cotton", "nylon"]] = [["small","red","cotton"],["small","red","nylon"],["small","blue","cotton"],["small","blue","nylon"], {- and a bunch more ... -}]` – Daniel Wagner Sep 06 '11 at 20:56
  • 1
    It would be extremely neat if php had a function for this. – Francis Yaconiello Sep 06 '11 at 21:24

2 Answers2

8

This should do the trick:

<?

$data[]=array('shirt');
$data[]=array('red','yellow','black');
$data[]=array('small','medium','large');

$combos=possible_combos($data);

//calculate all the possible comobos creatable from a given choices array
function possible_combos($groups, $prefix='') {
    $result = array();
    $group = array_shift($groups);
    foreach($group as $selected) {
        if($groups) {
            $result = array_merge($result, possible_combos($groups, $prefix . $selected. ' '));
        } else {
            $result[] = $prefix . $selected;
        }
    }
    return $result;
}

echo count($combos) . "\n";
print_r($combos);

Tested: http://www.ideone.com/NZE5S

profitphp
  • 8,104
  • 2
  • 28
  • 21
3

If this is an e-commerce site my guess is your option groups are already in an SQL database so why not just let SQL do the combinations for you.

SELECT Size.Name, Color.Name, Material.Name FROM Size, Color, Material

But what if you had all your options in one table with a foreign key to the group it's in...

SELECT r1.Name, r2.Name, r3.Name 
FROM Options r1, Options r2, Options r3
WHERE r1.GroupID = 1 -- id for Size
    AND r2.GroupID = 2 -- id for Color
    AND r3.GroupID = 3 -- id for Material

Once you have an array containing the group IDs generating the SQL statement above is trivial (just concatenating a few string implodes).

Louis Ricci
  • 20,804
  • 5
  • 48
  • 62
  • you might be on to something, I think you are mistaking my schema though: Product(id_product,...), OptionGroup(id_option_group, id_product, name), Option(id_option, id_option_group, name) - The option groups for a product aren't set +1 for good idea – Francis Yaconiello Sep 07 '11 at 12:39