How can an associative multidimensional (multiple dimensions) PHP array get converted to downloadable CSV? ← (PHP)

I have an associative multidimensional (dynamic length of dimensions) array. It originally comes from JSON data, but I understand that just makes things harder so I convert it using json_decode($original_data, true).

I'm interested to convert it to a clickable CSV file like echo '<a href="data:application/csv, ' . $data . '">Click to download</a>'.

I've tried many code variations, one of which I found online in https://coderwall.com/p/zvzwwa/array-to-comma-separated-string-in-php because its whole purpose is to "convert a multi-dimensional, associative array to CSV data". Alas, its code doesn't seem to be recursive. Unlike other functions I've tried it doesn't call itself recursively if the data isn't is_array.

Your assistance is appreciated.

Sample data:

$array = array(
    'name' => 'Test',
    'average' => 1,
    'fp' => '',
    'dates' => array(
        'isScheduled' => '',
        'startDate' => 1587418137,
        'endDate' => 1587418137,
        'pViewValue' => array(
                    'startDate' => '2020-04-20T18:28:57.000Z',
                    'endDate' => '2020-04-20T18:28:57.000Z',
        )
    )
);
echo '<pre>' . print_r($array, true) . '</pre>';

Array
(
    [name] => Test
    [average] => 1
    [fp] => 
    [dates] => Array
        (
            [isScheduled] => 
            [startDate] => 1587418137
            [endDate] => 1587418137
            [pViewValue] => Array
                (
                    [startDate] => 2020-04-20T18:28:57.000Z
                    [endDate] => 2020-04-20T18:28:57.000Z
                )

        )
)

Expected output:

name    average fp  dates-isScheduled   date-StartDate  date-endDate    date-pViewValue-startDate   date-pViewValue-endDate
test    1                               1587418137      1587418137      2020-04-20T18:28:57.000Z    2020-04-20T18:28:57.000Z

Answer



Solution:

fputcsv()

PHP Manual

Resolved on Stackoverflow

Answer



Solution:

CSV is table-like: columns in rows, seperated by a seperator.

They are unsuitable to use for dynamic length and depth datastructures.

So the short answer is: No, don't.

However, if you happen to have some knowledge about WHAT to expect, you could predefine the 'meaning' of each column and map that to certain places inside your CSV structure. But that is trickery, and only works if you know what goes where.

Answer



Solution:

@nelsonrakson's answer led me to what was a very specific answer to a very general question.

Also, that answer didn't quote the header line, used blank columns for every parent title, used strings instead of arrays and thus was left with extra commas plus unneeded calls (like $csv_data .= "" . ",";), so here it is with arrays, more proper support for parent titles and also what I wanted in the first place - an auto download of the CSV which was converted from an array:

Update: also added support for empty arrays like [something] => array().

<?php
    $array = array(
        'name of' => 'Test',
        'average' => 1,
        'fp' => '',
        'dates' => array(
            'isScheduled' => '',
            'startDate' => 1587418137,
            'endDate' => 1587418137,
            'pViewValue' => array(
                        'startDate' => '2020-04-20T18:28:57.000Z',
                        'endDate' => '2020-04-20T18:28:57.000Z',
            )
        )
    );
    echo '<pre>' . print_r($array, true) . '</pre>';

    $csv_title = array();
    $csv_data = array();
    array2csv($array, $csv_title, $csv_data);
    $csv_title = implode(",", $csv_title);
    $csv_data = implode(",", $csv_data);

    echo $csv_title . "\n<br />" . $csv_data . "\n\n" . '
    <script>
    thevalue = \'' . $csv_title . "\\n\\\n" . $csv_data . '\'
    var blob = new Blob([thevalue], {type: \'attachment/csv\'});
    var blobUrl = URL.createObjectURL(blob);
    var a = document.createElement("a");
    with (a) {
        // href="data:attachment/" + extension + ";charset=utf-8;base64," + utf8_to_b64(thevalue);
        // href="data:text/" + save as + ";charset=\'utf-8\'," + thevalue;
        href=blobUrl
        download = "export.csv";
    }
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    </script>
    ';

function str_wrap($string = '', $char = '"') {
    return str_pad($string, strlen($string) + 2, $char, STR_PAD_BOTH);
}

function array2csv($array, &$csv_title, &$csv_data, $prefix = '') {
    foreach($array as $key => $value) {      
        if (!empty($prefix) || strlen($prefix) > 0)
            $key = $prefix . ' - ' . $key;
        if (is_array($value) && !empty($value))
            array2csv($value, $csv_title, $csv_data, $key);
        else {
            $csv_title[] = str_wrap($key);
            $csv_data[] = !empty($value) ? str_wrap($value) : '';
        }
    }
}
?>

Source