2

I'm trying to create a "plugin" like script for adding to a "menu array".... I'll go straight in..

Lets say I initiate a nav item like so:

$sections->add_section('dashboard');
$DashBoardSection = $sections->load_section('dashboard');
$DashBoardSection->set_role(NULL);
$DashBoardSection->set_nav_item(array(
    'identifier' => 'dashboard',
    'text' => 'Dashboard',
    'url' => NULL,
    'position' => NULL
));

starts off by creating a new section and the getting the instance of.

We are then setting a "role" in which we will test against to see if the user is authenticated as being verified to view.

The set nav item simply stores the array. identifier is a reference to the item ( its for when we want to add sub items), all standard apart from "position" which states where it is to sit in the nav i.e. NULL is top level, array('topnav','subnav') will be topnav->subnav->dashboard.

as a test, this could be stored as follows:

Array
(
    [0] => Array
        (
            [identifier] => dashboard
            [text] => Dashboard
            [url] => 
            [position] => 
        )
[1] => Array
    (
        [identifier] => dashboard2
        [text] => Dashboard2
        [url] => 
        [position] => Array
            (
                [0] => dashboard
            )

    )

)

my question being how I turn that into the following structure:

Array
(
    [0] => Array
    (
        [identifier] => dashboard
        [text] => Dashboard
        [url] => 
        [position] => 
        [children] => Array
            (
                [0] => Array
                    (
                        [identifier] => dashboard2
                        [text] => Dashboard2
                        [url] => 
                    )
            )
    )
)

pulling my hair out over this one, any help would be very much appreciated.

regards

I currently have

public function build_navigation( $role ){
    $role = (int)$role;
    $nav  = array();
    foreach( $this->sections as $section ) {
        if( $section->get_role() === NULL || $section->get_role() === $role ) {
            $nav_array = $section->get_nav();
            foreach( $nav_array as $key => $nav_item ) {
                if( $nav_item['position'] === NULL ) {
                    $nav[$nav_item['identifier']] = $nav_item;
                }elseif( is_array( $nav_item['position'] ) ){
                    #...#
                }
            }
        }
    }
    return $nav;
}

EDIT

Imagine this is the array given (it can be in any order)

Array
(
    [0] => Array
        (
            [identifier] => dashboard_child2
            [text] => Dashboard Child 2
            [url] => 
            [position] => Array
                (
                    [0] => dashboard
                )

        )

    [1] => Array
        (
            [identifier] => dashboard_child_child_1
            [text] => Dashboard Child Child 1
            [url] => 
            [position] => Array
                (
                    [0] => dashboard
                    [1] => dashboard_child1
                )

        )



[2] => Array
    (
        [identifier] => dashboard_child1
        [text] => Dashboard Child 1
        [url] => 
        [position] => Array
            (
                [0] => dashboard
            )

    )

[3] => Array
    (
        [identifier] => dashboard
        [text] => Dashboard
        [url] => 
        [position] => 
    )

[4] => Array
    (
        [identifier] => dashboard2
        [text] => Dashboard2
        [url] => 
        [position] => Array
            (
                [0] => dashboard
            )

    )

)

Which needs to be formatted as:

Array
(
    [dashboard] => Array
        (
            [text] => Dashboard
            [url] => 
            [children] => Array
                (
                    [dashboard_child2] => Array
                        (
                            [text] => Dashboard Child 2
                            [url] => 
                        )
                    [dashboard_child1] => Array
                        (
                            [text] => Dashboard Child 1
                            [url] => 
                            [children] => Array
                                (
                                    [dashboard_child_child_1] => Array
                                        (
                                            [text] => Dashboard Child Child 1
                                            [url] => 
                                        )
                                )
                        )
                    [dashboard2] => Array
                        (
                            [text] => Dashboard2
                            [url] =>
                        )
                )
        )
) 
Phil Jackson
  • 10,238
  • 23
  • 96
  • 130
  • possible duplicate of [Nested array. Third level is disappearing](http://stackoverflow.com/questions/7673044/nested-array-third-level-is-disappearing) – hakre Aug 16 '12 at 20:55

2 Answers2

2

You need a temporary array that maps the identifier to the children array of it so that you can add it there.

If I see that right, you have something like that already here when you add the parent:

$nav[$nav_item['identifier']] = $nav_item;

Edit: Adding the parent needs some caution as pointed out in the comment:

$node = &$nav[$nav_item['identifier']];
$children = isset($node['children']) ? $node['children'] : array();
$node = $nav_item;
$node['children'] = $children;
unset($node);

Just add to the children then:

foreach($nav_item['position'] as $identifier)
{
    $nav[$identifier]['children'][] = $nav_item;
}

Also you could use objects, not arrays, so that you do not duplicate data that much (or you can change it later), however, just thinking, this must not be necessary to solve your problem so probably something for later.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • Thats my problem... If it's set as a plugin scope/environment, there's no reason why the section with position `array('foo','bar','foobar')` is read before identifier 'foo' or 'bar'... so the key wouldnt be set in the temp array... – Phil Jackson Aug 16 '12 at 19:09
  • I know you have already spent time on the above but I cant see how this would work... Let me create an array of data that i am seeing in my head that will cause the problem... – Phil Jackson Aug 16 '12 at 19:16
  • @PhilJackson: The simple method for that is outline here: [Converting an array from one to multi-dimensional based on parent ID values](http://stackoverflow.com/questions/7767961/converting-an-array-from-one-to-multi-dimensional-based-on-parent-id-values) or [Nested array. Third level is disappearing](http://stackoverflow.com/questions/7673044/nested-array-third-level-is-disappearing/7673415)- I didn't know you were looking for a tree. – hakre Aug 16 '12 at 20:50
2

Here's my take on the problem, solved with recursion.

You can use multiple positions (i guess this is why it's an array), it will ignore missing positions if at least on of the positions is found, but will complain if every position is missing.

function translate($in) {

    $out = array();

    // first pass, move root nodes to output
    foreach ($in as $k => $row) {
        if (!$row['position']) {
            $out[$row['identifier']] = $row;
            unset($in[$k]);
        }
    }

    // while we have input
    do {
        $elements_placed = 0;
        // step trough input
        foreach ($in as $row_index => $row) {
            foreach ($row['position'] as $pos) {
                // build context for the node placing
                $data = array(
                    'row' => $row,
                    'in'  => &$in,
                    'row_index' => $row_index,
                    'elements_placed' => &$elements_placed,
                    'pos' => $pos,
                );
                // kick of recursion
                walker($out, $data);
            }
        }
    } while ($elements_placed != 0);
    if (count($in)) {
        trigger_error("Error in user data, can't place every item");
    }
    return $out;
}

function walker(&$out, $data) {
    foreach ($out as &$row) {
        // it looks like a node array
        if (is_array($row) && isset($row['identifier'])) {
            // if it has children recurse in there too
            if (isset($row['children'])) {
                walker($row['children'], $data);
            }

            // it looks like a node array that we are looking for place the row
            if ($row['identifier'] == $data['pos']) {
                if (!isset($row['children'])) {
                    $row['children'] = array($data['row']['identifier'] => $data['row']);
                } else {
                    $row['children'][$data['row']['identifier']] = $data['row'];
                }
                // report back to the solver that we found a place
                ++$data['elements_placed'];
                // remove the row from the $in array
                unset($data['in'][$data['row_index']]);
            }
        }
    }
}

$in = array (
    array (
        'identifier' => 'secondlevelchild2',
        'text' => 'secondlevelchild2',
        'url' => '',
        'position' => array (
            'dashboard2',
        ),
    ),
    array (
        'identifier' => 'secondlevelchild',
        'text' => 'secondlevelchild',
        'url' => '',
        'position' => array (
            'dashboard2',
        ),
    ),
    array (
        'identifier' => 'dashboard',
        'text' => 'Dashboard',
        'url' => '',
        'position' => '',
    ),
    array (
        'identifier' => 'dashboard2',
        'text' => 'Dashboard2',
        'url' => '',
        'position' => array (
            'dashboard', 'home',
        ),
    ),
    array (
        'identifier' => 'thirdlevelchild',
        'text' => 'thirdlevelchild',
        'url' => '',
        'position' => array (
            'secondlevelchild2',
        ),
    ),
);

$out = translate($in);
var_export($out);

In it's current form it doesn't remove the identifier or position keys from the node arrays once they are placed.

complex857
  • 20,425
  • 6
  • 51
  • 54
  • Can't seem to get this to work correctly, keeps putting everything as a direct child of dashboard... http://pastebin.com/uWuX0QeU – Phil Jackson Aug 16 '12 at 20:26
  • Oh, I've misinterpreted the meaning of the `position` array (based on the original question). With this version you don't have to list every parent along the way only the closest one (in my example data the `thirdlevelchild` has only a `secondlevelchild2` position and get's placed correctly). However with the full path available it can be done more easily too (-: – complex857 Aug 16 '12 at 20:30
  • Ah, I see yes. The original "position" mapped the path in array values but your way adds to more than one... think this maybe better! AHA! genius. Thank you both!! – Phil Jackson Aug 16 '12 at 20:32