1

I'm making a table right now for displaying accounting data, with invoices broken up into license, year, half, and quarter. The invoices come from a query as a 2d array,like this:

array(
    'invoice0' = array(data),
    'invoice2' = array(data),
    'invoice3' = array(data),
    'invoice4' = array(data),
    'invoice5' = array(data),
    'invoice1' = array(data)
)

Currently I'm been using a four-deep nested foreach loop, which seems fairly evil, to create a 5d array structure to organize the rows, with data rows for every level. e.g.:

'License' = array(
  'Year0' = array(
    'Half0' = array(
      'Quarter0' = array(
        'invoice0' = array(data),
        'invoice1' = array(data)
      ),
      'QData' = array(data)
    ),
    'HData' = array(data)
    'Half1' = array(
      'Quarter0' = array(
        'invoice3' = array(data),
        'invoice4' = array(data)
      ),
      'Quarter1' = array(
        'invoice5' = array(data),
        'invoice6' = array(data)
      ),
      'QData' = array(data)
    ),
    'HData' = array(data)
  ),
  'YData' = array(data)
)

And so on. The data for each level needs to include a sum of an amount in the data for each invoice. The end product I'm trying to achieve is simply a bunch of rows of a table (or list items; I'm restricted to table structure for this project) That are expandable. So:

<tr>License info</tr>
<tr>License info</tr>
<tr>License info</tr>
<tr>License info</tr>

And when you click on one:

<tr>License info</tr>
<tr>License info</tr>
<tr>Year1 Info</tr>
<tr>Year2 Info</tr>
<tr>Year3 Info</tr>
<tr>License info</tr>
<tr>License info</tr>

Eventually:

<tr>License info</tr>
<tr>License info</tr>
<tr>Year1 Info</tr>
<tr>Year2 Info</tr>
<tr>Half1 Info</tr>
<tr>Quarter1 Info</tr>
<tr>Quarter2 Info</tr>
<tr>Invoice1 Info</tr>
<tr>Invoice2 Info</tr>
<tr>Invoice3 Info</tr>
<tr>Quarter3 Info</tr>
<tr>Quarter4 Info</tr>
<tr>Half2 Info</tr>
<tr>Year3 Info</tr>
<tr>License info</tr>
<tr>License info</tr>

So, any ideas? Is there any way to avoid such complex data manipulation while still getting me a 5-deep hierarchical dropdown list?

4
  • 1
    The other option is some kind of recursive function, which'd allow for (theoretically, subject to memory/stack limits) unlimited nesting depth. Commented Sep 7, 2012 at 16:18
  • Why not use JS to create the nested table data as the user clicks? This way you avoid computing the entire nested table structure only to have the user get details on one of them. Just have PHP return some JSON that JS can easily parse and turn into table data. Commented Sep 7, 2012 at 16:35
  • @kurtzbot: I was thinking about something like that as an alternative. I couldn't think of anything though. Could a jquery .each() function work to sum up the values of each of the matching options? Ach, but see the hierarchy is determined by a time interval from a certain date (the start of the license). I'm not sure how to combine that kind of date analysis with html manipulation. I'll play around with it a bit though. Commented Sep 7, 2012 at 16:44
  • Just have PHP create a JSON object with all the data you need inside for JS to do the job. Once you have implemented a good "start" you can always cut down on information and improve. I would just worry about testing this implementation idea. Commented Sep 7, 2012 at 17:20

1 Answer 1

1

Okay I didn't understand completely what is it exactly you want, but I did this, kinda confusing, heh:

function get_data($data, $level = 0)
{
    $html = '';

    foreach ($data as $name => $val) {
        if (!is_int($name)) {
            if (!$level)
                $html .= '<tr>';
            $html .= '<ul>';
            $html .= '<li><span class="parent">'.$name.'</span>';
            $html .= get_data($val, $level + 1);
            $html .= '</li></ul>';
            if (!$level)
                $html .= '</tr>';
        } else {
            $html .= '<ul><li>'.$val.'</li></ul>';
        }
    }
    return $html;
}

The HTML should look like this (using jQuery):

<style>
span.parent {
    cursor:pointer;
}
ul ul {
    display:none;
}
</style>
<table>
<?php echo get_data($data) ?>
</table>
<script>
$('span.parent').click(function(){
    if ($(this).hasClass('open')) {
        $(this).parent().children('ul').hide();
        $(this).removeClass('open');
    } else {
        $(this).parent().children().show();
        $(this).addClass('open');
    }
})
</script>

I tried this out with the following array and... well it worked but, again, I don't know if this is what you're looking for.

$data = array(
    'License' => array(
        'Year0' => array(
            'Half0' => array(
                'Quarter0' => array(
                    'invoice0' => array('data'),
                    'invoice1' => array('data')
                ),
                'QData' => array('data')
            ),
            'HData' => array('data'),
            'Half1' => array(
                'Quarter0' => array(
                    'invoice3' => array('data'),
                    'invoice4' => array('data')
                ),
                'Quarter1' => array(
                    'invoice5' => array('data'),
                    'invoice6' => array('data')
                ),
                'QData' => array('data')
            ),
            'HData' => array('data')
        ),
        'YData' => array('data')
    ),

    'License2' => array(
        'Year0' => array(
            'Half0' => array(
                'Quarter0' => array(
                    'invoice0' => array('data'),
                    'invoice1' => array('data'),
                ),
                'QData' => array('data')
            ),
            'HData' => array('data'),
            'Half1' => array(
                'Quarter0' => array(
                    'invoice3' => array('data'),
                    'invoice4' => array('data')
                ),
                'Quarter1' => array(
                    'invoice5' => array('data'),
                    'invoice6' => array('data')
                ),
                'QData' => array('data')
            ),
            'HData' => array('data')
        ),
        'YData' => array('data')
    )
);
Sign up to request clarification or add additional context in comments.

1 Comment

Cool! That looks a lot nicer than my nested foreaches. Technically it's almost the same thing, plus a little extra overhead for calling afunction, but it's a heck of a lot easier to read. Thanks for the input!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.