Ein altes Navigationsmenu sortieren
Ich habe eine kleine Programmieraufgabe für euch.
Ich habe ein altes Projekt, in dem ich folgende Navigationsstruktur in der Datenbank habe:
menuid | parentid | title | level | sortid |
---|---|---|---|---|
1 | 3 | Wurm 1.1 | 2 | 10 |
2 | 6 | Vogel 2.1 | 2 | 30 |
3 | 0 | Tiger 1 | 1 | 10 |
4 | 6 | Hund 2.2 | 2 | 40 |
5 | 3 | Katze 1.2 | 2 | 11 |
6 | 0 | Pferd 2 | 1 | 20 |
7 | 1 | Baer 1.1.1 | 3 | 0 |
8 | 3 | Schwein 1.3 | 2 | 12 |
9 | 4 | Esel 2.2.1 | 3 | 0 |
Nun möchte ich diese Menüpunkte sortiert ausgeben, und zwar in der folgenden Reihenfolge:
Tiger 1 Wurm 1.1 Baer 1.1.1 Katze 1.2 Schwein 1.3 Pferd 2 Vogel 2.1 Hund 2.2 Esel 2.2.1
Die Sortierreihenfolge muss anhand der menuid, parentid, level und sortid berechnet werden. Eine parentid verweist auf den Elternknoten, sprich er ist darunter einzusortieren. Zwei Einträge mit der selben parentid sind nach der Spalte sortid zu sortieren.
Der Wurm ist ein Kindknoten vom Tiger, der Bär ist ein Kindknoten vom Wurm. Die Katze ist auch ein Kindknoten vom Tiger, hat aber die höhere sortid, muss also nach dem Wurm einsortiert werden.
Es ist ein altes Projekt mit dieser Struktur, und die Frage ist wie man das am einfachsten und schnellsten sortiert?
Geht das ganze mit einem SQL-Query? Das wäre natürlich die beste Lösung, aber mir ist kein solcher Query eingefallen der das Problem lösen könnte.
Also muss es in PHP sortiert werden. Ich habe das ganze in ein PHP-Array gepackt und hier für euch zum Spielen bereitgestellt:
Dort könnt ihr an dem Algorithmus arbeiten, sodass aus dem Ursprungs-Array das Ziel-Array wird. Nachdem ihr „eval()“ gedrückt habt könnt ihr einfach die URL hier in die Kommentare packen, nach jedem Druck auf „eval()“ wird das ganze gespeichert und versioniert.
Ich bin gespannt auf eure Lösungen!
Bei einer Oracle-Datenbank erledigt man das ganz simpel mit connect by sowie level.
http://www.datenbank-sql.de/oracle-hierarchie.htm
Tobias Rohde
11 Mrz 15 at 23:24
Mal davon abgesehen, dass das Level umsonst ist, habe ich dir das mal schnell runter getippt bzw. copy paste von php.net
function array_orderby()
{
$args = func_get_args();
$data = array_shift($args);
foreach ($args as $n => $field) {
$tmp = array();
foreach ($data as $key => $row)
$tmp[$key] = $row[$field];
$args[$n] = $tmp;
}
$args[] = &$data;
call_user_func_array('array_multisort', $args);
return array_pop($args);
}
function byTopId($items,$id) {
$return = array();
foreach($items as $item) {
if($item[1] == $id) {
$return[] = $item;
}
}
if(count($return) > 0) {
$return = array_orderby($return,4);
}
return $return;
}
function sortMenu($menu,$topid,&$return) {
$items = byTopId($menu,$topid);
foreach($items as $item) {
$return[] = $item;
sortMenu($menu,$item[0],$return);
}
}
$return = array();
sortMenu($menu,0,$return);
var_dump($return);
Juan Muerte Duarte Diaz
12 Mrz 15 at 08:02
Im Feld „title“ ist doch eh schon die Sortierreiehnfolge angegeben. Sortier doch einfach danach:
SELECT title FROM tabelle ORDER BY SUBSTRING(title, LENGTH(title) – LOCATE(‚ ‚, REVERSE(title))+2)
😉
HutzenDuddel
12 Mrz 15 at 10:21
Nachtrag: Funktioniert aber nur solange, wie es weniger als 10 Einträge pro Ebene gibt.
HutzenDuddel
12 Mrz 15 at 10:49
Die Idee von HutzenDuddel aufgreifend, aber rein in PHP, dafür funktioniert es auch mit >10 Einträgen pro Ebene:
http://3v4l.org/sSsDC
Daniel
12 Mrz 15 at 16:07
@Tobias: Schöne Lösung! Habe nur leider keine Oracle-Datenbank 😉
@Juan: Deine Lösung funktioniert wunderbar, habe es gerade auch für 4 Level ausprobiert, klappt einwandfrei. Durch die Rekursion sollte es allgemeingültig sein.
@HutzenDuddel + @Daniel: Der Titel darf nicht zur Sortierung verwendet werden. Der enthält hier nur zu Anschauungszwecken die Verschachtelungsstruktur als Zahlen, in den „richtigen“ Menupunkten sind diese Zahlen natürlich nicht vorhanden. Deshalb schrieb ich oben im Blogartikel „Die Sortierreihenfolge muss anhand der menuid, parentid, level und sortid berechnet werden.“
Ganz so einfach ist es leider nicht 😉
Michael Kliewe
14 Mrz 15 at 12:09
Nicht sehr performant, einfach der Weg über die Knotenstruktur …
http://3v4l.org/B3SAD/
Wolfgang
2 Jun 15 at 18:13
Etwas frickelig .. aber bestimmt ganz interessant 🙂
https://3v4l.org/RuFPp
steffen
18 Aug 15 at 16:39
easy up
https://3v4l.org/nvuRW
T
14 Okt 15 at 20:38
Der Wurm ist ein Kindknoten vom Tiger, der Bär ist ein Kindknoten vom Wurm. Die Katze ist auch ein Kindknoten vom Tiger, hat aber die höhere sortid, muss also nach dem Wurm einsortiert werden.
Merkst du eigentlich noch was?
Durchfallman
21 Okt 15 at 17:17
Hier meine Version: https://3v4l.org/qPDZW
Oliver
23 Jan 16 at 07:57
Da kannst du ja gleich alles kommasepariert in ein einziges Datenbankfeld packen…
Bitte die Datenstruktur optimieren und nicht noch mehr schlechten Code herum bauen!
Hier ein Einstieg:
https://stackoverflow.com/questions/4048151/what-are-the-options-for-storing-hierarchical-data-in-a-relational-database#
Max
27 Mai 19 at 13:48