Nesting a flat PHP array to generate an HTML menu and submenus

Asked

Viewed 295 times

0

I have an array like this:

Array
(
    [38] => Array
        (
            [name] => Categoria Raiz 1
            [link] => https://...
            [id_parent] => 0
        )

    [205] => Array
        (
            [name] => Subcategoria 1
            [link] => https://...
            [id_parent] => 38
        )

    [206] => Array
        (
            [name] => Subcategoria 2
            [link] => https://...
            [id_parent] => 38
        )
    [484] => Array
        (
            [name] => Categoria Raiz 2
            [link] => https://...
            [id_parent] => 0
        )
    [485] => Array
        (
            [name] => Subcategoria 3
            [link] => https://...
            [id_parent] => 484
        )

    [39] => Array
        (
            [name] => categoria Raiz 3
            [link] => https://...
            [id_parent] => 0
        )

    [147] => Array
        (
            [name] => Subcategoria 4
            [link] => https://...
            [id_parent] => 39
        )
)

Indexes are the category Ids.

I need to nest the root categories with their subcategories to create a dropdown menu, but I’m missing the logic. I’m using the template engine Smarty, but I’ll write down what I’ve done in PHP pure to facilitate.

<?php
foreach($array as $key => $value) { ?>
    <li data-id_parent="<?php echo $value['id_parent']; ?>">
        <a href="<?php echo $value['link']; ?>"><?php echo $value['name']; ?></a>

    <?php
            if (!isset($id_anterior)) { ?>
                <ul>
    <?php   } else {
                if ($id_anterior != $value['id_parent']) { ?>
                    </ul>
                    <ul>
    <?php       } 
            } 
    ?>
    </li>
    <?php

    $id_anterior = $key;
}
?>

I wish the way out was something like this:

<li data-id_parent="0">
    <a href="https://...">Categoria Raiz 1</a>
    <ul>
        <li data-id_parent="38">
            <a href="https://...">Subcategoria 1</a>
        </li>
        <li data-id_parent="38">
            <a href="https://...">Subcategoria 2</a>
        </li>
    </ul>
</li>
<li data-id_parent="0">
    <a href="https://...">Categoria Raiz 2</a>
    <ul>
        <li data-id_parent="484">
            <a href="https://...">Subcategoria 3</a>
        </li>
    </ul>
</li>
<li data-id_parent="0">
    <a href="https://...">Categoria Raiz 3</a>
    <ul>
        <li data-id_parent="39">
            <a href="https://...">Subcategoria 4</a>
        </li>
    </ul>
</li>

Where am I going wrong?

  • how is the table?

  • @Hananiamizrahi The table consists of columns with the same names as the keys of the array, the column being id primary key and column id_parent foreign key referencing the table itself.

2 answers

0

I suggest first arrange the array...

$menu = [
    38 => [
            'name' => 'Categoria Raiz 1',
            'link' => 'https://...',
            'id_parent' => 0
    ],
    205 => [
            'name' => 'Subcategoria 1',
            'link' => 'https://...',
            'id_parent' => 38
    ],
    206 => [
            'name' => 'Subcategoria 2',
            'link' => 'https://...',
            'id_parent' => 38
    ],
    484 => [
            'name' => 'Categoria Raiz 2',
            'link' => 'https://...',
            'id_parent' => 0
    ],
    485 => [
            'name' => 'Subcategoria 3',
            'link' => 'https://...',
            'id_parent' => 484
    ],
    39 => [
            'name' => 'Categoria Raiz 3',
            'link' => 'https://...',
            'id_parent' => 0
    ],
    147 => [
            'name' => 'Subcategoria 4',
            'link' => 'https://...',
            'id_parent' => 39
    ]
];

function &find($needle, &$haystack) {
    $return = false;

    if (isset($haystack[$needle])) {
        $return = &$haystack[$needle];
    } else {
        foreach ($haystack as &$item) {
            if (isset($item['child'])) {
                $return = &find($needle, $item['child']);
            }
        }
    }

    return $return;
}

function organize($menu) {
    $organized = [];

    foreach ($menu as $index => &$item) {
        $item['child'] = [];

        $ref = &find($item['id_parent'], $organized);

        if ($ref !== false) {
            $ref['child'][$index] = $item;
        } else {
            $organized[$index] = $item;
        }
    }

    return $organized;
}

$organized = organize($menu);

The result of var_dump($organized); will be

array(3) {
  [38]=>
  array(4) {
    ["name"]=>
    string(16) "Categoria Raiz 1"
    ["link"]=>
    string(11) "https://..."
    ["id_parent"]=>
    int(0)
    ["child"]=>
    array(2) {
      [205]=>
      array(3) {
        ["name"]=>
        string(14) "Subcategoria 1"
        ["link"]=>
        string(11) "https://..."
        ["id_parent"]=>
        int(38)
      }
      [206]=>
      array(3) {
        ["name"]=>
        string(14) "Subcategoria 2"
        ["link"]=>
        string(11) "https://..."
        ["id_parent"]=>
        int(38)
      }
    }
  }
  [484]=>
  array(4) {
    ["name"]=>
    string(16) "Categoria Raiz 2"
    ["link"]=>
    string(11) "https://..."
    ["id_parent"]=>
    int(0)
    ["child"]=>
    array(1) {
      [485]=>
      array(3) {
        ["name"]=>
        string(14) "Subcategoria 3"
        ["link"]=>
        string(11) "https://..."
        ["id_parent"]=>
        int(484)
      }
    }
  }
  [39]=>
  array(4) {
    ["name"]=>
    string(16) "Categoria Raiz 3"
    ["link"]=>
    string(11) "https://..."
    ["id_parent"]=>
    int(0)
    ["child"]=>
    array(1) {
      [147]=>
      array(3) {
        ["name"]=>
        string(14) "Subcategoria 4"
        ["link"]=>
        string(11) "https://..."
        ["id_parent"]=>
        int(39)
      }
    }
  }
}

And then write the list, for example, with a recursive function:

function echoList($array) {
    echo '<ul>';

    foreach($array as $item) {
        echo '<li data-id_parent="'.$item['id_parent'].'"><a href="'.$item['link'].'">'.$item['name'].'</a>';

        if (isset($item['child']) && !empty($item['child'])) {
            echoList($item['child']);
        }

        echo '</li>';
    }

    echo '</ul>';
}

echoList($organized);

The resulting HTML:

<ul>
    <li data-id_parent="0">
        <a href="https://...">Categoria Raiz 1</a>
        <ul>
            <li data-id_parent="38"><a href="https://...">Subcategoria 1</a></li>
            <li data-id_parent="38"><a href="https://...">Subcategoria 2</a></li>
        </ul>
    </li>
    <li data-id_parent="0">
        <a href="https://...">Categoria Raiz 2</a>
        <ul>
            <li data-id_parent="484"><a href="https://...">Subcategoria 3</a></li>
        </ul>
    </li>
    <li data-id_parent="0">
        <a href="https://...">Categoria Raiz 3</a>
        <ul>
            <li data-id_parent="39"><a href="https://...">Subcategoria 4</a></li>
        </ul>
    </li>
</ul>

Functions are recursive and allow as many lists, sublists and sublists within sublists as needed

0


Your structure is recursive, so why don’t you solve it using recursiveness? And you don’t need to restructure your array, just filter the items you want to display from the value of id_parent.

function menu($items, $parent = 0)
{
    $links = array_filter($items, function ($it) use ($parent) { 
        return $it['id_parent'] === $parent; 
    });

    if (count($links) === 0) {
        return '';
    }

    $menu = "<ul>";

    foreach ($links as $id => $link) {
        $menu .= "<li data-id_parent='{$parent}'>";
        $menu .= sprintf("<a href='%s'>%s</a>", $link['link'], $link['name']);
        $menu .= menu($items, $id);
        $menu .= "</li>";
    }

    $menu .= "</ul>";

    return $menu;
}

Thus staying:

$items = [
    38 => [
            'name' => 'Categoria Raiz 1',
            'link' => 'https://...',
            'id_parent' => 0
    ],
    205 => [
            'name' => 'Subcategoria 1',
            'link' => 'https://...',
            'id_parent' => 38
    ],
    206 => [
            'name' => 'Subcategoria 2',
            'link' => 'https://...',
            'id_parent' => 38
    ],
    484 => [
            'name' => 'Categoria Raiz 2',
            'link' => 'https://...',
            'id_parent' => 0
    ],
    485 => [
            'name' => 'Subcategoria 3',
            'link' => 'https://...',
            'id_parent' => 484
    ],
    39 => [
            'name' => 'Categoria Raiz 3',
            'link' => 'https://...',
            'id_parent' => 0
    ],
    147 => [
            'name' => 'Subcategoria 4',
            'link' => 'https://...',
            'id_parent' => 39
    ]
];


echo menu($items);

Result (after formatted):

<ul>
  <li data-id_parent='0'>
    <a href='https://...'>Categoria Raiz 1</a>
    <ul>
      <li data-id_parent='38'><a href='https://...'>Subcategoria 1</a></li>
      <li data-id_parent='38'><a href='https://...'>Subcategoria 2</a></li>
    </ul>
  </li>
  <li data-id_parent='0'>
    <a href='https://...'>Categoria Raiz 2</a>
    <ul>
      <li data-id_parent='484'><a href='https://...'>Subcategoria 3</a></li>
    </ul>
  </li>
  <li data-id_parent='0'>
    <a href='https://...'>Categoria Raiz 3</a>
    <ul>
      <li data-id_parent='39'><a href='https://...'>Subcategoria 4</a></li>
    </ul>
  </li>
</ul>

Browser other questions tagged

You are not signed in. Login or sign up in order to post.