Mavichow
Mavichow

Reputation: 1233

PHP – Format dynamic string pattern into nested array

I've following data in my Database :

| meta_key                          | meta_value |
|-----------------------------------|------------|
| foo                               | bar        |
| hello                             | world      |
| testimonials_0_category           | category0  |
| testimonials_0_comments_0_message | message0   |
| testimonials_0_comments_1_message | message1   |

Some of the meta_key will be string and some of the meta_key will be nested array base on the _{N}_ pattern.

I've successfully extract & render the data as I wanted with eval(); method, but i don't think eval is a good solution for this.

I'm just wondering do we have alternative solution on this (eg : using recursive or other kind of array loop method?)

You may refer below for my sample codes:

<?php
$database = array();
$database[] = array('meta_key' => 'foo','meta_value'=>'bar');
$database[] = array('meta_key' => 'hello','meta_value'=>'world');
$database[] = array('meta_key' => 'testimonials_0_category','meta_value'=>'category0');
$database[] = array('meta_key' => 'testimonials_0_comments_0_message','meta_value'=>'message0');
$database[] = array('meta_key' => 'testimonials_0_comments_1_message','meta_value'=>'message1');

$output = array();
// echo'<pre>';print_r($database);die;

foreach($database as $d)
{
    if (preg_match('(_\d+_)',$d['meta_key']))
    {
        //nested array
        preg_match_all('(_\d+_)',$d['meta_key']."_9999_x",$matches);
        $splits = preg_split('(_[0-9]_)', $d['meta_key']);
        $str    = '';

        foreach($matches[0] as $j => $match)
        {
            $i = filter_var($match,FILTER_SANITIZE_NUMBER_INT);
            $str.= "['{$splits[$j]}'][{$i}]";
        }
        $str = Str_replaceLast('[9999]','',$str);
        eval('$output'.$str." = '{$d['meta_value']}';");
    }else{
        //string
        $output[$d['meta_key']] = $d['meta_value'];
    }
}

echo'<pre>';print_r($output);

//Helper
function Str_replaceLast($search, $replace, $subject)
{
    $position = strrpos($subject, $search);

    if ($position !== false) {
        return substr_replace($subject, $replace, $position, strlen($search));
    }

    return $subject;
}
?>

The result/output I want would be like below format:

Array
(
    [foo] => bar
    [hello] => world
    [testimonials] => Array
        (
            [0] => Array
                (
                    [category] => category0
                    [comments] => Array
                        (
                            [0] => Array
                                (
                                    [message] => message0
                                )

                            [1] => Array
                                (
                                    [message] => message1
                                )

                        )

                )

        )

)

Upvotes: 0

Views: 67

Answers (1)

Nick
Nick

Reputation: 147216

This recursive function will give you the results you want. It splits the meta_key value into three parts around and including the number and uses that to create a new entry in the output array. If there is more than 1 part of the key remaining, the function recurses to the next level on the part of the key after the digit, otherwise it assigns the meta_value to that part of the key:

function split_word($word, $value, $output) {
    $parts = preg_split('/_(\d)+_/', $word, 2, PREG_SPLIT_DELIM_CAPTURE);
    if (count($parts) > 1) {
        if (isset($output[$parts[0]][$parts[1]])) {
            $output[$parts[0]][$parts[1]] = array_merge($output[$parts[0]][$parts[1]], split_word($parts[2], $value, $output[$parts[0]][$parts[1]]));
        }
        else {
            $output[$parts[0]][$parts[1]] = split_word($parts[2], $value, array());
        }
    }
    else {
        $output = array_merge($output, array($word => $value));
    }
    return $output;
}

$output = array();
foreach ($database as $d) {
    $output = array_merge($output, split_word($d['meta_key'], $d['meta_value'], $output));
}
print_r($output);

Output:

Array
(
    [foo] => bar
    [hello] => world
    [testimonials] => Array
        (
            [0] => Array
                (
                    [category] => category0
                    [comments] => Array
                        (
                            [0] => Array
                                (
                                    [message] => message0
                                )
                            [1] => Array
                                (
                                    [message] => message1
                                )
                        )
                )
        )
)

Demo on 3v4l.org

Upvotes: 1

Related Questions