如何制作一个通用函数来将嵌套对象缩减为 CSV 样式数组?

How can I make a generic function to reduce a nested object into a CSV style array?

我正在尝试弄清楚如何将嵌套的 array/tree 扁平化为 CSV 样式格式。

我有一个来自 elasticsearch 的结果,如下所示(注意 - 每个 'buckets' 属性 可以有多个或 0 个项目):

$data = array(
'reeta.datetime_day' => array(
    'buckets' => array(
        0 => array(
            'key_as_string' => '2018-07-27T00:00:00.000Z',
            'key' => 1532649600000,
            'doc_count' => 4,
            'ticket.employee_name' => array(
                'doc_count_error_upper_bound' => 0,
                'sum_other_doc_count' => 0,
                'buckets' => array(
                    0 => array(
                        'key' => 'Era Swift',
                        'doc_count' => 3,
                        'ticket.order_type' => array(
                            'doc_count_error_upper_bound' => 0,
                            'sum_other_doc_count' => 0,
                            'buckets' => array(
                                0 => array(
                                    'key' => 'Dine In',
                                    'doc_count' => 3,
                                    'ticket.total_guest_count' => array(
                                        'value' => 17,
                                    ) ,
                                    'ticket.total_revenue' => array(
                                        'value' => 273,
                                    ) ,
                                ) ,
                            ) ,
                        ) ,
                    ) ,
                    1 => array(
                        'key' => 'Dorothea Friesen',
                        'doc_count' => 1,
                        'ticket.order_type' => array(
                            'doc_count_error_upper_bound' => 0,
                            'sum_other_doc_count' => 0,
                            'buckets' => array(
                                0 => array(
                                    'key' => 'Take Out',
                                    'doc_count' => 1,
                                    'ticket.total_guest_count' => array(
                                        'value' => 2,
                                    ) ,
                                    'ticket.total_revenue' => array(
                                        'value' => 195,
                                    ) ,
                                ) ,
                            ) ,
                        ) ,
                    ) ,
                ) ,
            ) ,
        )
    )
)

);

我有一个用于获取这些聚合的维度和指标列表,它们各不相同,例如,上面的结果使用以下内容:

$dimensions = ['reeta.datetime_day', 'ticket.employee_name', 'ticket.order_type'];
$metrics = ['ticket.total_guest_count', 'ticket.total_revenue'];

我希望是这样的:

$result = [
    // the first bucket of the deepest dimension (ticket.order_type)
    [
        1532649600000, // the first dimensions key value
        'Era Swift', // the second dimensions key value
        'Dine In', // the thirs dimensions key value
        17, // the first metrics value
        273 // the second metrics value
    ],
    // the second bucket of the deepest dimension (ticket.order_type)
    [
        1532649600000, // the first dimensions key value
        'Dorothea Friesen', // the second dimensions key value
        'Take Out', // the thirs dimensions key value
        2, // the first metrics value
        195 // the second metrics value
    ],
];

为了更好地解释我的用例,我创建了一个 API,它接受许多维度和指标,然后查询 ES 以获得结果。然后,我将获取这些结果并使用它们为 Google 图表 (https://developers.google.com/chart/interactive/docs/datatables_dataviews).

创建数据表

我尝试编写许多递归函数,array_walk_recursive 等,并尝试使用谷歌搜索任何类似的示例,但找不到任何地方。

我尝试遍历并检查:

$array[$dimension]['buckets']

要知道当索引不存在时我处于最深层次

但我总是得到不正确的结果。我只是无法简单地展平数组,因为我需要为每个最深层次的桶提供一个数组项。

希望这能说得通,但我很乐意在这里澄清并提供更大的数据集: https://pastebin.com/mU2xkQGB

如果有人能指出我正确的方向或任何类似的示例,我将不胜感激,我确实有一堆嵌套的 foreach 块在某个时候工作,但一旦我改变维度的数量当然没用。

我确实设法找到了一种解决方案,它绝对不是最好的,但它通常适用于任意数量的维度和指标。

本质上,我使用 https://github.com/nicmart/Tree 构建了一个树对象,因此可以遍历节点,但是我想要,所以每次我必须添加指标时,我都可以遍历回到它的父节点并创建一个 table 行。

查看下面的工作代码:

composer require nicmart/tree

这是文件

<?php

require 'vendor/autoload.php';

$array = json_decode('{"reeta.datetime_day":{"buckets":[{"key_as_string":"2018-07-27T00:00:00.000Z","key":1532649600000,"doc_count":4,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Era Swift","doc_count":3,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":3,"ticket.total_guest_count":{"value":17},"ticket.total_revenue":{"value":273}}]}},{"key":"Dorothea Friesen","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":2},"ticket.total_revenue":{"value":195}}]}}]}},{"key_as_string":"2018-07-28T00:00:00.000Z","key":1532736000000,"doc_count":6,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Era Swift","doc_count":3,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":2,"ticket.total_guest_count":{"value":9},"ticket.total_revenue":{"value":212}},{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":8},"ticket.total_revenue":{"value":112}}]}},{"key":"Dorothea Friesen","doc_count":2,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":2,"ticket.total_guest_count":{"value":8},"ticket.total_revenue":{"value":379.97999572753906}}]}},{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":8},"ticket.total_revenue":{"value":226.35000610351562}}]}}]}},{"key_as_string":"2018-07-29T00:00:00.000Z","key":1532822400000,"doc_count":0,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[]}},{"key_as_string":"2018-07-30T00:00:00.000Z","key":1532908800000,"doc_count":2,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Delbert Abernathy","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":3},"ticket.total_revenue":{"value":105.3499984741211}}]}},{"key":"Era Swift","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":1},"ticket.total_revenue":{"value":160.35000610351562}}]}}]}},{"key_as_string":"2018-07-31T00:00:00.000Z","key":1532995200000,"doc_count":2,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Era Swift","doc_count":2,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":2,"ticket.total_guest_count":{"value":8},"ticket.total_revenue":{"value":234}}]}}]}},{"key_as_string":"2018-08-01T00:00:00.000Z","key":1533081600000,"doc_count":1,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Era Swift","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":2},"ticket.total_revenue":{"value":151.35000610351562}}]}}]}},{"key_as_string":"2018-08-02T00:00:00.000Z","key":1533168000000,"doc_count":5,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Conor Gerlach","doc_count":2,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":6},"ticket.total_revenue":{"value":5}},{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":9},"ticket.total_revenue":{"value":140}}]}},{"key":"Dorothea Friesen","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":6},"ticket.total_revenue":{"value":241}}]}},{"key":"Era Swift","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":3},"ticket.total_revenue":{"value":157}}]}},{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":4},"ticket.total_revenue":{"value":240}}]}}]}},{"key_as_string":"2018-08-03T00:00:00.000Z","key":1533254400000,"doc_count":3,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Era Swift","doc_count":2,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":2,"ticket.total_guest_count":{"value":12},"ticket.total_revenue":{"value":169}}]}},{"key":"Conor Gerlach","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":3},"ticket.total_revenue":{"value":260.3500061035156}}]}}]}},{"key_as_string":"2018-08-04T00:00:00.000Z","key":1533340800000,"doc_count":3,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Foster Bashirian","doc_count":2,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":2,"ticket.total_guest_count":{"value":16},"ticket.total_revenue":{"value":277}}]}},{"key":"Era Swift","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":2},"ticket.total_revenue":{"value":118}}]}}]}},{"key_as_string":"2018-08-05T00:00:00.000Z","key":1533427200000,"doc_count":3,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Delbert Abernathy","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":6},"ticket.total_revenue":{"value":68}}]}},{"key":"Era Swift","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":6},"ticket.total_revenue":{"value":274}}]}},{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":5},"ticket.total_revenue":{"value":180}}]}}]}},{"key_as_string":"2018-08-06T00:00:00.000Z","key":1533513600000,"doc_count":4,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Conor Gerlach","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":2},"ticket.total_revenue":{"value":172}}]}},{"key":"Delbert Abernathy","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":8},"ticket.total_revenue":{"value":119}}]}},{"key":"Dorothea Friesen","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":4},"ticket.total_revenue":{"value":120}}]}},{"key":"Era Swift","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":2},"ticket.total_revenue":{"value":37}}]}}]}},{"key_as_string":"2018-08-07T00:00:00.000Z","key":1533600000000,"doc_count":2,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Conor Gerlach","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":6},"ticket.total_revenue":{"value":134}}]}},{"key":"Dorothea Friesen","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":4},"ticket.total_revenue":{"value":71}}]}}]}},{"key_as_string":"2018-08-08T00:00:00.000Z","key":1533686400000,"doc_count":2,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dorothea Friesen","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":9},"ticket.total_revenue":{"value":252}}]}},{"key":"Era Swift","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":2},"ticket.total_revenue":{"value":244.35000610351562}}]}}]}},{"key_as_string":"2018-08-09T00:00:00.000Z","key":1533772800000,"doc_count":3,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Conor Gerlach","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":9},"ticket.total_revenue":{"value":246}}]}},{"key":"Dorothea Friesen","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":9},"ticket.total_revenue":{"value":23}}]}},{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":8},"ticket.total_revenue":{"value":34}}]}}]}},{"key_as_string":"2018-08-10T00:00:00.000Z","key":1533859200000,"doc_count":4,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Conor Gerlach","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":6},"ticket.total_revenue":{"value":77.3499984741211}}]}},{"key":"Delbert Abernathy","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":2},"ticket.total_revenue":{"value":195}}]}},{"key":"Era Swift","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":6},"ticket.total_revenue":{"value":85}}]}},{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":2},"ticket.total_revenue":{"value":161}}]}}]}},{"key_as_string":"2018-08-11T00:00:00.000Z","key":1533945600000,"doc_count":3,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dorothea Friesen","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":2},"ticket.total_revenue":{"value":38}}]}},{"key":"Era Swift","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":5},"ticket.total_revenue":{"value":25}}]}},{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":5},"ticket.total_revenue":{"value":100}}]}}]}},{"key_as_string":"2018-08-12T00:00:00.000Z","key":1534032000000,"doc_count":1,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Era Swift","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":1},"ticket.total_revenue":{"value":201}}]}}]}},{"key_as_string":"2018-08-13T00:00:00.000Z","key":1534118400000,"doc_count":4,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dorothea Friesen","doc_count":2,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":2,"ticket.total_guest_count":{"value":15},"ticket.total_revenue":{"value":183.3499984741211}}]}},{"key":"Conor Gerlach","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":6},"ticket.total_revenue":{"value":116}}]}},{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":5},"ticket.total_revenue":{"value":117}}]}}]}},{"key_as_string":"2018-08-14T00:00:00.000Z","key":1534204800000,"doc_count":4,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Era Swift","doc_count":2,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":2,"ticket.total_guest_count":{"value":11},"ticket.total_revenue":{"value":255}}]}},{"key":"Conor Gerlach","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":3},"ticket.total_revenue":{"value":73.3499984741211}}]}},{"key":"Dorothea Friesen","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":9},"ticket.total_revenue":{"value":7}}]}}]}},{"key_as_string":"2018-08-15T00:00:00.000Z","key":1534291200000,"doc_count":2,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Delbert Abernathy","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":8},"ticket.total_revenue":{"value":101}}]}},{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":10},"ticket.total_revenue":{"value":31}}]}}]}},{"key_as_string":"2018-08-16T00:00:00.000Z","key":1534377600000,"doc_count":0,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[]}},{"key_as_string":"2018-08-17T00:00:00.000Z","key":1534464000000,"doc_count":1,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dorothea Friesen","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":4},"ticket.total_revenue":{"value":155}}]}}]}},{"key_as_string":"2018-08-18T00:00:00.000Z","key":1534550400000,"doc_count":2,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Delbert Abernathy","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":9},"ticket.total_revenue":{"value":130.35000610351562}}]}},{"key":"Era Swift","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":5},"ticket.total_revenue":{"value":255.97999572753906}}]}}]}},{"key_as_string":"2018-08-19T00:00:00.000Z","key":1534636800000,"doc_count":1,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":4},"ticket.total_revenue":{"value":176}}]}}]}},{"key_as_string":"2018-08-20T00:00:00.000Z","key":1534723200000,"doc_count":3,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Conor Gerlach","doc_count":2,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":8},"ticket.total_revenue":{"value":290.3500061035156}},{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":9},"ticket.total_revenue":{"value":88}}]}},{"key":"Dorothea Friesen","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":1},"ticket.total_revenue":{"value":88}}]}}]}},{"key_as_string":"2018-08-21T00:00:00.000Z","key":1534809600000,"doc_count":1,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":6},"ticket.total_revenue":{"value":64}}]}}]}},{"key_as_string":"2018-08-22T00:00:00.000Z","key":1534896000000,"doc_count":1,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":8},"ticket.total_revenue":{"value":171.35000610351562}}]}}]}},{"key_as_string":"2018-08-23T00:00:00.000Z","key":1534982400000,"doc_count":5,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Delbert Abernathy","doc_count":2,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":2,"ticket.total_guest_count":{"value":11},"ticket.total_revenue":{"value":112}}]}},{"key":"Conor Gerlach","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":6},"ticket.total_revenue":{"value":120}}]}},{"key":"Dorothea Friesen","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":9},"ticket.total_revenue":{"value":73.3499984741211}}]}},{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":1},"ticket.total_revenue":{"value":149}}]}}]}},{"key_as_string":"2018-08-24T00:00:00.000Z","key":1535068800000,"doc_count":1,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Delbert Abernathy","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":10},"ticket.total_revenue":{"value":56}}]}}]}},{"key_as_string":"2018-08-25T00:00:00.000Z","key":1535155200000,"doc_count":6,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Conor Gerlach","doc_count":2,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":2,"ticket.total_guest_count":{"value":14},"ticket.total_revenue":{"value":228.35000038146973}}]}},{"key":"Era Swift","doc_count":2,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":8},"ticket.total_revenue":{"value":27}},{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":9},"ticket.total_revenue":{"value":154}}]}},{"key":"Delbert Abernathy","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":5},"ticket.total_revenue":{"value":114}}]}},{"key":"Foster Bashirian","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Take Out","doc_count":1,"ticket.total_guest_count":{"value":3},"ticket.total_revenue":{"value":13.350000381469727}}]}}]}},{"key_as_string":"2018-08-26T00:00:00.000Z","key":1535241600000,"doc_count":1,"ticket.employee_name":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Era Swift","doc_count":1,"ticket.order_type":{"doc_count_error_upper_bound":0,"sum_other_doc_count":0,"buckets":[{"key":"Dine In","doc_count":1,"ticket.total_guest_count":{"value":4},"ticket.total_revenue":{"value":244}}]}}]}}]}}', true);

$dimensions = ['reeta.datetime_day', 'ticket.employee_name', 'ticket.order_type'];
$metrics = ['ticket.total_guest_count', 'ticket.total_revenue'];

use Tree\Node\Node;

class TableFormatter
{
    public static function formatDataTable($aggregationResults, array $dimensions, array $metrics) {

        // initialize main data to return
        $data = [];
        $headerRow = [];

        // now put in each metric we have
        foreach($dimensions as $dimension) {
            array_push($headerRow, $dimension);
        }

        // now put in each metric we have
        foreach($metrics as $metric) {
            array_push($headerRow, $metric);
        }

        $data[] = $headerRow;

        $aggs = self::convertToTree($aggregationResults, $dimensions, $metrics);

        return array_merge($data, $aggs);
    }

    private static function convertToTree(array $aggregationResults, array $dimensions, array $metrics, $tree = null, & $data = []) {

        // initialize a tree if there isn't one already
        if (!$tree) {
            $tree = new Node('root');
        }

        // if there are dimensions then continue
        if (!empty($dimensions)) {

            // get the next dimension
            $dimension = array_shift($dimensions);

            // ensure the data is set for that dimension
            if (isset($aggregationResults[$dimension]['buckets'])) {


                // loop through each buckt
                foreach ($aggregationResults[$dimension]['buckets'] as $bucket) {

                    // make child node to store the value and add to the parent node
                    $dimensionValue = new Node($bucket['key']);
                    $tree->addChild($dimensionValue);

                    // recursively call function to traverse through each bucket
                    self::convertToTree($bucket, $dimensions, $metrics, $dimensionValue, $data);
                }
            } else {
                throw new Exception("No aggregation data for [".$dimension."]", Error::ERROR_INTERNAL_ERROR);
            }
        } else {
            self::formatMetricData($aggregationResults, $metrics, $tree, $data);
        }

        return $data;
    }

    private static function formatMetricData(array $bucket, array $metrics, $tree, & $data) {

        if(empty($bucket)) {
            return $tree;
        }

        // setup the array for the table row
        $item = [];

        // now add each metric
        foreach($metrics as $metric) {

            $metricValue = new Node($bucket[$metric]['value']);
            // add in the metrics
            $item[] = $metricValue->getValue();

            $tree->addChild($metricValue);
        }

        // metrics are the wrong way round at this point so reverse them
        $item = array_reverse($item);

        // add in the key
        $item[] = $bucket['key'];

        // now we can traverse the tree all the way up and get the parents
        $node = $tree->getParent();
        while ($node->getValue() != 'root') {
            $item[] = $node->getValue();
            $node = $node->getParent();
        }

        // reverse here as it's in the wrong order
        $data[] = array_reverse($item);

        return $tree;
    }
}

$result = TableFormatter::formatDataTable($array, $dimensions, $metrics);

var_dump($result);

如前所述,这绝对不是目前为止最有效的解决方案,但它确实有效,并且花了很多时间四处寻找类似的东西,所以希望这可以帮助解除封锁,直到出现更好的解决方案。

谢谢!