Help with Recursive array loop

Published 3 weeks ago by TuffRivers

I need to get product options out of each product in a product array. Some products might have no product options, some products might have N product options.

How can i recursively loop the product array to get the display_value and display_name for each product_options array from the product array?

I tried this solution to build a tree and return the "branch" but i get a blank array when i execute.

Any ideas?

//convert json to array
//take the product (elements) array, set the parentId to the "id" field for each product
//then use the product options [id] field as the child 
// print out the tree

    $elements = json_decode($product, true);
    
    function buildTree(array $elements, $parentId = 0) {
    $branch = array();

    foreach ($elements as $element) {
        if ($element['id'] == $parentId) {
            $children = buildTree($elements, $element['id']);
            if ($children) {
                $element['product_options']["id"] = $children;
            }
            $branch[] = $element;
        }
    }

    return $branch;
}

$tree = buildTree($elements);

print_r($tree);


//data

$product = '[
    {
        "id": 72,
        "order_id": 64,
        "product_id": 55,
        "order_address_id": 75,
        "name": "Product XXXX",
        "sku": "XXX123",
        "type": "physical",
        "product_options": [],
        "configurable_fields": []
    },
    {
        "id": 74,
        "order_id": 64,
        "product_id": 112,
        "order_address_id": 75,
        "name": "Iron-On Letters",
        "sku": "",
        "product_options": [
            {
                "id": 26,
                "option_id": 34,
                "order_product_id": 74,
                "product_option_id": 15,
                "display_name": "Color",
                "display_value": "Pink",
                "value": "99",
                "type": "Swatch",
                "name": "Color",
                "display_style": ""
            },
            {
                "id": 27,
                "option_id": 38,
                "order_product_id": 74,
                "product_option_id": 22,
                "display_name": "Initials",
                "display_value": "TT",
                "value": "TT",
                "type": "Text field",
                "name": "Initials",
                "display_style": ""
            },
            {
                "id": 28,
                "option_id": 40,
                "order_product_id": 74,
                "product_option_id": 26,
                "display_name": "Style",
                "display_value": "Golf",
                "value": "113",
                "type": "Multiple choice",
                "name": "Style",
                "display_style": "Dropdown"
            }
        ],
        "configurable_fields": []
    },
    {
        "id": 75,
        "order_id": 64,
        "product_id": 12,
        "order_address_id": 75,
        "name": "Iron-On Letters",
        "sku": "",
        "product_options": [
            {
                "id": 29,
                "option_id": 34,
                "order_product_id": 75,
                "product_option_id": 15,
                "display_name": "Color",
                "display_value": "Pink",
                "value": "99",
                "type": "Swatch",
                "name": "Color",
                "display_style": ""
            },
            {
                "id": 30,
                "option_id": 38,
                "order_product_id": 75,
                "product_option_id": 22,
                "display_name": "Initials",
                "display_value": "RR",
                "value": "RR",
                "type": "Text field",
                "name": "Initials",
                "display_style": ""
            },
            {
                "id": 31,
                "option_id": 40,
                "order_product_id": 75,
                "product_option_id": 26,
                "display_name": "Style",
                "display_value": "Golf",
                "value": "113",
                "type": "Multiple choice",
                "name": "Style",
                "display_style": "Dropdown"
            }
        ],
        "configurable_fields": []
    }
]';
Best Answer (As Selected By TuffRivers)
Cronix

Try this:

function normalize_products(array $rawProducts) {
    $products = [];

    foreach ($rawProducts as $rawProduct) {
        $product = [
            'id'              => $rawProduct['id'],
            'product_id'      => $rawProduct['product_id'],
            'name'            => $rawProduct['name'],
            'product_options' => ''
        ];
        
        if (count($rawProduct['product_options'])) {
            $tmp = [];
            foreach ($rawProduct['product_options'] as $option) {
                $tmp[] = $option['display_name'] . ': ' . $option['display_value'];
            }
            $product['product_options'] = implode(', ', $tmp);
        }
        
        $products[] = $product;
    }
}

$products = normalize_products($productsArray);
dd($products);

produces

array:3 [▼
  0 => array:4 [▼
    "id" => 72
    "product_id" => 55
    "name" => "Product XXXX"
    "product_options" => ""
  ]
  1 => array:4 [▼
    "id" => 74
    "product_id" => 112
    "name" => "Iron-On Letters"
    "product_options" => "Color: Pink, Initials: TT, Style: Golf"
  ]
  2 => array:4 [▼
    "id" => 75
    "product_id" => 12
    "name" => "Iron-On Letters"
    "product_options" => "Color: Pink, Initials: RR, Style: Golf"
  ]
]
iyoworks

I'm not quite sure why recursion is necessary. I think the missing step is iterating over the product options. I've renamed variables in hopes of making this an easy read.

$productArray = json_decode($productsJson, true);

function normalize_products(array $rawProducts) {
    $products = [];
    // iterate over all product data
    foreach ($rawProducts as $rawProduct) {
        // create a clean version of the product that has an explicit set of fields
        $product = [
            'id' => $rawProduct['product_id'],
            'name' => $rawProduct['name'],
            'optionValues' => [],
            'options' => []
        ];
        // iterate over all product options
        if (!empty($rawProduct['product_options'])) {
            foreach ($rawProduct['product_options'] as $option) {       
                // add option value to product.optionValues
                    $product['optionValues'][ $option['display_name'] ] = $option['display_value'];
                // add option details to product.options
                $product['options'][] = [
                    'id' => $option['product_option_id'],
                    'display_name' => $option['display_name'],
                    'display_value' => $option['display_value'],
                ];
            }
        }   
        $products[] = $product;
    }

    return $products;
}

Output:

Array
(
    [0] => Array
        (
            [id] => 55
            [name] => Product XXXX
            [optionValues] => Array
                (
                )

            [options] => Array
                (
                )

        )

    [1] => Array
        (
            [id] => 112
            [name] => Iron-On Letters
            [optionValues] => Array
                (
                    [Color] => Pink
                    [Initials] => TT
                    [Style] => Golf
                )

            [options] => Array
                (
                    [0] => Array
                        (
                            [id] => 15
                            [display_name] => Color
                            [display_value] => Pink
                        )

                    [1] => Array
                        (
                            [id] => 22
                            [display_name] => Initials
                            [display_value] => TT
                        )

                    [2] => Array
                        (
                            [id] => 26
                            [display_name] => Style
                            [display_value] => Golf
                        )

                )

        )

    [2] => Array
        (
            [id] => 12
            [name] => Iron-On Letters
            [optionValues] => Array
                (
                    [Color] => Pink
                    [Initials] => RR
                    [Style] => Golf
                )

            [options] => Array
                (
                    [0] => Array
                        (
                            [id] => 15
                            [display_name] => Color
                            [display_value] => Pink
                        )

                    [1] => Array
                        (
                            [id] => 22
                            [display_name] => Initials
                            [display_value] => RR
                        )

                    [2] => Array
                        (
                            [id] => 26
                            [display_name] => Style
                            [display_value] => Golf
                        )

                )

        )

)
TuffRivers

@iyoworks thank you so much for this. I thought recursion was necessary because of the known product options.

This workd perfectly. Thank you so much!

TuffRivers

@iyoworks this workds perfectly but any idea how i can pull the optionValues for each product and concatenate it to an array that looks like

Color: Pink, Initials: KK, Style: Golf

I tried to foreach over the products["options"] array to pull out the key value pairs and implode them then concat to a string like

$productOptions = normalize_products($productsArray);

foreach($productOptions as $item){

  //extract key value pairs implode

}

$color = $product["optionValues"]["color"];
$initials = $product["optionValues"]["initials"];
$style = $product["optionValues"]["style"];

$str = $color.$initials.$style

but cant seem to figure out the correct logic :(

Cronix
Cronix
3 weeks ago (783,370 XP)

any idea how i can pull the optionValues for each product and concatenate it to an array

And what about the products that don't have options? Not all of them do.

Cronix
Cronix
3 weeks ago (783,370 XP)

But it would be

$products = normalize_products($productsArray);

foreach ($products as $product){
    $tmp = [];

    // are there any options for this?
    if (count($product['optionValues'])) {
        foreach ($product['optionValues'] as $key => $value) {
            $tmp[] = "{$key}: {$value}";
        }
        $string = implode(', ', $tmp);
        echo $string . '<br>';
    } else {
        // no options for this product?
    }
}
TuffRivers

@Cronix

Yes that works!

Is this a normal amount of code required to obtain this information ? (Not trying to knock yours and @iyoworks code but asking just in general).

I hope it doesnt get much more complicated than this lol i feel like im drowning at this point

Cronix
Cronix
3 weeks ago (783,370 XP)

I don't know all you're trying to do. I was just answering the last question.

It really would be best to post an example of what you want your final array to look like.

You want to feed a function the $product json array you defined in your first post, and you want to get an array back that looks like... (show example of the actual array).

You probably don't need to be doing all of these separate steps with all of this code to get what you're wanting, but you're not defining exactly what you're needing very well.

given this product, what do you want the php array for it to look like?

{
        "id": 75,
        "order_id": 64,
        "product_id": 12,
        "order_address_id": 75,
        "name": "Iron-On Letters",
        "sku": "",
        "product_options": [
            {
                "id": 29,
                "option_id": 34,
                "order_product_id": 75,
                "product_option_id": 15,
                "display_name": "Color",
                "display_value": "Pink",
                "value": "99",
                "type": "Swatch",
                "name": "Color",
                "display_style": ""
            },
            {
                "id": 30,
                "option_id": 38,
                "order_product_id": 75,
                "product_option_id": 22,
                "display_name": "Initials",
                "display_value": "RR",
                "value": "RR",
                "type": "Text field",
                "name": "Initials",
                "display_style": ""
            },
            {
                "id": 31,
                "option_id": 40,
                "order_product_id": 75,
                "product_option_id": 26,
                "display_name": "Style",
                "display_value": "Golf",
                "value": "113",
                "type": "Multiple choice",
                "name": "Style",
                "display_style": "Dropdown"
            }
        ],
        "configurable_fields": []
    }

right now it's coming back as

[2] => Array
        (
            [id] => 12
            [name] => Iron-On Letters
            [optionValues] => Array
                (
                    [Color] => Pink
                    [Initials] => RR
                    [Style] => Golf
                )

            [options] => Array
                (
                    [0] => Array
                        (
                            [id] => 15
                            [display_name] => Color
                            [display_value] => Pink
                        )

                    [1] => Array
                        (
                            [id] => 22
                            [display_name] => Initials
                            [display_value] => RR
                        )

                    [2] => Array
                        (
                            [id] => 26
                            [display_name] => Style
                            [display_value] => Golf
                        )

                )

        )

How should it be?

TuffRivers

@Cronix

Sorry for the confusion, i find that hardest part actually formulating my questions (still learning the vocabulary to express myself).

i have to write a line to a csv file containing product data for each product returned from an API call (example json above is an api response for all products in a specific order).

there is a header the csv file for each product called "product variations" which is the display name and display value of each array under product_options for each product.

So i dont really mind what the final array looks like, i just need to get into (if it exists) the product_variations array, and obtain the display_name and display_value of each array and print it out as seen below. If it doesnt exist, continue.

Let me know if that clarifies

//So from my json example
//headers
id, product_id, name, product_options

//line for each product in order
72, 55,Product XXXX,""
74, 64, Iron-On Letters, "Color: Pink, Initials: RR, Style: Golf"
75, 12,Iron-On Letters, "Color: Pink, Initials: TT, Style: Golf"
Cronix
Cronix
3 weeks ago (783,370 XP)

Try this:

function normalize_products(array $rawProducts) {
    $products = [];

    foreach ($rawProducts as $rawProduct) {
        $product = [
            'id'              => $rawProduct['id'],
            'product_id'      => $rawProduct['product_id'],
            'name'            => $rawProduct['name'],
            'product_options' => ''
        ];
        
        if (count($rawProduct['product_options'])) {
            $tmp = [];
            foreach ($rawProduct['product_options'] as $option) {
                $tmp[] = $option['display_name'] . ': ' . $option['display_value'];
            }
            $product['product_options'] = implode(', ', $tmp);
        }
        
        $products[] = $product;
    }
}

$products = normalize_products($productsArray);
dd($products);

produces

array:3 [▼
  0 => array:4 [▼
    "id" => 72
    "product_id" => 55
    "name" => "Product XXXX"
    "product_options" => ""
  ]
  1 => array:4 [▼
    "id" => 74
    "product_id" => 112
    "name" => "Iron-On Letters"
    "product_options" => "Color: Pink, Initials: TT, Style: Golf"
  ]
  2 => array:4 [▼
    "id" => 75
    "product_id" => 12
    "name" => "Iron-On Letters"
    "product_options" => "Color: Pink, Initials: RR, Style: Golf"
  ]
]

Please sign in or create an account to participate in this conversation.