Am I creating variations properly?



I’m selling digital products - photos. My site is a bit like a photo stock site. The prices of my products depend on 3 attributes:

  • size (sm, md, lg, xl)
  • quality (basic, normal, premium)
  • aspect ratio (standard, panoramic)

So, at first glance, it looks like I need to create 3 variations. But it’s not that simple as it seems because:

  • All products have all available sizes
  • Each product has just one quality attribute
  • Each product has just one aspect ratio attribute (it can be either panoramic o standard).

For example:

Product A: is panoramic photo that has a normal quality.
Product B: is a photo that has a standard ratio and its quality is premium

A user can by a Product A or a product B choosing a size of: sm, md, lg or xl, but he can’t select a quality or an aspect ratio of these products - they are properties of these products defined by the store owner.

So I definitely don’t want to create 3 variations for all products. Only the size parameter seem to be a real variation… but not really because I need to assign different modifiers to the size variation options depending on the quality and aspect ratio property of each product.

So in fact, I need 6 variations for all possible combinations of quality and aspect ratio parameters:

  • basic_standard
  • basic_panoramic
  • normal_standard
  • normal_panoramic
  • premium_standard
  • premium_panoramic

This is the code I ended up with:

namespace MyApp\Moltin;

class Setup
    // quality: basic, ratio: standard
    const BASIC_STANDARD = [
        'sm' => 2,
        'md' => 5,
        'lg' => 12,
        'xl' => 25,

    // quality: basic, ratio: panoramic
    const BASIC_PANORAMIC = [
        'sm' => 2,
        'md' => 5,
        'lg' => 12,
        'xl' => 49,

    // quality: normal, ratio: standard
    const NORMAL_STANDARD = [
        'sm' => 5,
        'md' => 12,
        'lg' => 25,
        'xl' => 49,

    // quality: normal, ratio: panoramic
    const NORMAL_PANORAMIC = [
        'sm' => 5,
        'md' => 12,
        'lg' => 25,
        'xl' => 99,

    // quality: premium, ratio: standard
    const PREMIUM_STANDARD = [
        'sm' => 9,
        'md' => 25,
        'lg' => 49,
        'xl' => 99,

    // quality: premium, ratio: panoramic
        'sm' => 9,
        'md' => 25,
        'lg' => 49,
        'xl' => 149,

    const LABELS = [
        'sm' => [
            'name' => 'sm',
            'desc' => 'Small size',
        'md' => [
            'name' => 'md',
            'desc' => 'Medium size',
        'lg' => [
            'name' => 'lg',
            'desc' => 'Large size',
        'xl' => [
            'name' => 'xl',
            'desc' => 'Extra large size',

     * This method is supposed to be executed only once when a new Moltin instance is set up.
     * It performs the initialization task. It's not supposed to be run during normal application lifecycle.
     * @throws \Exception
    public static function buildVariations()
        $moltin = ClientFactory::getClient();

        $variationsWithPrices = [
            'basic_standard' => self::BASIC_STANDARD,
            'basic_panoramic' => self::BASIC_PANORAMIC,
            'normal_standard' => self::NORMAL_STANDARD,
            'normal_panoramic' => self::NORMAL_PANORAMIC,
            'premium_standard' => self::PREMIUM_STANDARD,
            'premium_panoramic' => self::PREMIUM_PANORAMIC,

        foreach ($variationsWithPrices as $variationName => $sizesAndPrices) {

            // POST: /variations/
            $variation = $moltin->addVariation($variationName);

            $variationId = $variation->toArray()['id'];

            foreach ($sizesAndPrices as $size => $price) {
                $name = self::LABELS[$size]['name'];
                $desc = self::LABELS[$size]['desc'];

                // POST:/variations/${variationId}/options/
                $moltin->addVariationOptions($variationId, $name, $desc);

            // GET: /variations/${variationId}/options
            $variationOptions = $moltin->getVariationOptions($variationId)->toArray();

            foreach ($variationOptions as $variationOption) {
                $size = $variationOption['name'];
                $optionId = $variationOption['id'];

                // POST: /variations/${variationId}/options/${optionId}/modifiers/
                $moltin->addVariationModifier($variationId, $optionId, 'slug_builder', [
                    'seek' => 'SLUG',
                    'set' => $size,
                $moltin->addVariationModifier($variationId, $optionId, 'sku_builder', [
                    'seek' => 'SKU',
                    'set' => $size,
                $moltin->addVariationModifier($variationId, $optionId, 'name_append', " (${size})");
                $moltin->addVariationModifier($variationId, $optionId, 'price_equals', [
                        'currency' => 'USD',
                        'amount' => $sizesAndPrices[$size],
                        'includes_tax' => false,


It all works fine, but I have a feeling that I’m not doing it right. I have introduced my own abstraction over Moltin variations, so in fact some part of variations functionality is implemented on my app’s end and some part of variations handling is implemented in a Moltin way.

So I wonder does it even make sense to use Moltin’s variations. Maybe it would be better if I got rid of variations completely in favour of setting the prices individually for each product? At the moment I’m doing a similar thing - once a product is added to my DB, I push it to Moltin, assign a variation to it and create child products. If I got rid of Moltin’s variations I would do a similar amount of work - instead of creating child products I would create 4 products (one for each size) and set an appropriate price on each product.

What do you think?


Hi Wube,

Variations are perfect for your size choice but the others are not really a choice - they are set.

I would create the size variation only.

As other 2 properties are not a choice the user can make then I’d either create a flow (some supplemental material that might help) for these or simply define them in the description of the product.

Hope that make sense…




Variations are perfect for your size choice but the others are not really a choice - they are set.

Correct. I reached the same conclusion.

As other 2 properties are not a choice the user can make then I’d either create a flow (some supplemental material that might help) for these or simply define them in the description of the product.

I think you missed one, important information - these parameters are not display-only parameters, prices of products depend on these parameters, that’s why I ended up with 6 variations, all of them have 4 size options (sm, md, lg, xl) but different price modifiers that depend on orientation and aspect ratio

  • basic_standard (sm, md, lg, xl)
  • basic_panoramic (sm, md, lg, xl)
  • normal_standard (sm, md, lg, xl)
  • normal_panoramic (sm, md, lg, xl)
  • premium_standard (sm, md, lg, xl)
  • premium_panoramic (sm, md, lg, xl)


So…the approach I would advocate.

Create a variation for size and add the options and modifiers.

Then each product you create you can specify its aspect ratio and quality either passively in description or with a flow (these will be particular to that product) and then attach the size variation you created previously - a variation can be applied to as many products as you wish.

You can then set the base price for that particular product with only the size then varying. This way you don’t conflate the choices that don’t really exist.

If the size has a consistent affect on the price e.g. large is always a price increment of $10 then you should find this route very simple to manage.

The aspect ratio and quality are not something the user can pick so trying to manage with variations is most likely going to involve more jumping through hoops to manage effectively.

An example of this:

Variation: Size

  • small (+$2)
  • medium (+$4)
  • large (+$7)
  • x-large (+$11)

Image A

  • orientation: standard
  • quality: normal
  • base price $19

Image B

  • orientation: panoramic
  • quality: premium
  • base price: $38

Applying the variation to these 2 products would yield

Image A std, normal sml $21
Image A std, normal med $23
Image A std, normal lrg $26
Image A std, normal xl $30

Image B panoramic, premium small $40
Image B panoramic, premium small $42
Image B panoramic, premium small $45
Image B panoramic, premium small $51

I would strongly advise you avoid using variations for things that are not choices for the user.

I suspect from what you are asking though is that the aspect ratio and the quality have a differing effect on the cost when choosing a size.

For example:

panoramic premium image large is +$10 and xl is +$15
standard normal image large is +$6 and xl is +$9

In this instance I would create a size variation for each scenario.

Based on:

  • basic_standard_size (sm, md, lg, xl)
  • basic_panoramic_size (sm, md, lg, xl)
  • normal_standard_size (sm, md, lg, xl)
  • normal_panoramic_size (sm, md, lg, xl)
  • premium_standard_size (sm, md, lg, xl)
  • premium_panoramic_size (sm, md, lg, xl)

That is 6 variations each with 4 options and you can modify the price according to each. You can then apply the relevant variation to each product.

Hope this helps.


I don’t need to attach these properties to a product in Moltin, I already store these informations in my DB. Based on that data I attach proper variation to a product once a product is created in Moltin.

The code I posted in my first comment describes the price change clearly - have a look at constants defined at the top of the class. The price depends on all 3 parameters: size, ratio and quality. There’s no simple rule saying that every parameter is responsible for a constant price increase/decrease -that’s why I ended up with 6 variations.

That’s what I do currently (look at the PHP code I posted). I just wanted a confirmation that I’m not doing something stupid. I thought that maybe there’s some kind of a Moltin-way that I’m not aware of, but it looks like there’s none and the solution I came up with is OK.

Thanks for your time.


I’ll confess I didn’t scroll through the code you posted…

Based in that I think you have this…