0

Sorry - really bad title but couldn't figure how else to put it!

I am creating an array of a folder structure like this:

Each entry in the table has three fields:

| parent id | route | label      |
|-----------|-------|------------|
| 1         |       | account    |
| 2         | 1     | section 1  |
| 3         | 1     | section 2  |
| 4         | 1/2   | s1 options |
| 5         | 1/3   | s2 options |
| 6         | 1/2/4 | settings   |
| 7         | 1/3/5 | language   |

This would look like this:

account / 
account / section 1 / 
account / section 2 / 
account / section 1 / s1 options / 
account / section 2 / s2 options / 
account / section 1 / s1 options / settings
account / section 2 / s2 options / language

To create this the route is parsed using the parent id and its label. All straightforward so far.

As I'm sure you'll appreciate, I want to list it as:

account / 
account / section 1 / 
account / section 1 / s1 options / 
account / section 1 / s1 options / settings
account / section 2 / 
account / section 2 / s2 options / 
account / section 2 / s2 options / language

This is where I'm having problems! I've tried different sort options both in the sql query and various array options but I'm getting stuck.

I have a page where a user can create new folders by selecting the parent folder first and then adding in the new folder name, so I don't have control over how the folder structure is built or in what order.

At the moment I'm trying to avoid anything too long and complex because I'm sure there must be a way of doing it simply...I just can't find it.

Suggestions/solutions valued! :)

SQL -

$q1 = $dbp->prepare("SELECT * FROM `structure`");
$q1->execute();
while ($d1 = $q1->fetch()) {
    $structure_parent[] = $d1['parent'];
    $structure_route[] = $d1['route'];
    $structure_label[] = $d1['label'];
    $structure[$d1['parent']] = $d1['label'];
}

SELECT -

<select id="parent" name="parent">
<?php
$p = 0;
while($p < count($structure_parent)) {
    // build route map
    $route = explode("/", $structure_route[$p]);
    $s = 0;
    while($s < count($route)) {
        $route_mapping[] = $structure[$route[$s]];
        $s++;
    }
    if($structure_route[$p] > "") $routemap = implode(" / ", $route_mapping)." / ";
    else $routemap = "";
    unset($route_mapping);
    ?>
    <option value="<?php echo $structure_parent[$p]; ?>"><?php echo " / ".$routemap.$structure_label[$p]; ?></option>
    <?php
    $p++;
}
?>
</select>
  • It would have been useful to see the query that you are currently using. – RiggsFolly Feb 06 '19 at 12:16
  • @RiggsFolly sql and select added. Not sure it deserved a downvote though? – Steve Weatherill Feb 06 '19 at 12:24
  • @RiggsFolly Sorry. For some reason I was telling myself it was irrelevant because it wasn't really doing anything helpful towards the solution! My bad :) – Steve Weatherill Feb 06 '19 at 12:33
  • Once you've built your array, you can sort it using a custom call-back to `usort` / `uasort`, similar to the multi-dimensional array example in the linked duplicate. You just need to define a function that, given two entries, says which should come first in the final list. – IMSoP Feb 06 '19 at 15:28
  • @IMSoP So, out of the vast 'array' of answers (pun intended!), which would you have chosen? TBH I don't think this should have been marked as a duplicate to that one. – Steve Weatherill Feb 06 '19 at 15:48
  • @StephenWeatherill Look at the one headed "Sorting by multiple fields". In your case, the "fields" are the parts separated by '/'. – IMSoP Feb 06 '19 at 16:00
  • @IMSoP OK, thank you. Will look at that in more depth when I have the space for it. :) – Steve Weatherill Feb 06 '19 at 16:10

1 Answers1

1

You can do that simply using array_combine and array_column to create label map and strtr (documentation) to swap the route.

Consider the following example (I skip the DB extraction and just init array with the data):

$arr = [];
$arr[] = array("id" => 1, "route" => "", "label" => "account");
$arr[] = array("id" => 2, "route" => "1", "label" => "section 1");
$arr[] = array("id" => 3, "route" => "1", "label" => "section 2");
$arr[] = array("id" => 4, "route" => "1/2", "label" => "s1 options");
$arr[] = array("id" => 5, "route" => "1/3", "label" => "s2 options");
$arr[] = array("id" => 6, "route" => "1/2/4", "label" => "settings");
$arr[] = array("id" => 7, "route" => "1/3/5", "label" => "language");

$labels = array_combine(array_column($arr, "id"), array_column($arr, "label")); // labels will be mapping between id to label
foreach($arr as $e) {
    $ans[] = strtr($e["route"], $labels) . "/" . $e["label"]; //build path according to route and add your label
}
sort($ans);

Now $ans will have you desire output.

dWinder
  • 11,597
  • 3
  • 24
  • 39
  • Thanks. Unfortunately this gives me the same as I have to start with, not the desired order. I did a direct copy/paste to see it working first. :( – Steve Weatherill Feb 06 '19 at 14:34
  • In my query above I show the output twice. The first one is what I currently get, but the second one show the desired output. In your answer it shows the output in the same order as the array but it needs to be sorted more logically. I hope that makes more sense. :) – Steve Weatherill Feb 06 '19 at 14:53
  • @StephenWeatherill I was missing `sort($ans);` - updated my answer -please check the order now – dWinder Feb 06 '19 at 15:05
  • AWESOME - does the trick. Thank you! I'm now just trying to work out how to keep the original `id` because it seems to get lost somewhere. I did a quick test by adding it onto the end and then splitting the result into key/value but it does something a bit strange! I need it for the – Steve Weatherill Feb 06 '19 at 15:52
  • 1
    @StephenWeatherill I would insert the path to `$ans` by id as key so you can get the key then you loop on the array. If this post helped you may mark it as accepted – dWinder Feb 06 '19 at 16:01
  • It does now - thank you! :) – Steve Weatherill Feb 06 '19 at 16:04