Bobby: Schlangencode / Spaghetticode verhindern

Moin

Mal ne grundsätzliche Frage. Mir passiert es immer wieder das ich zu komplexe Vorgänge zu tief verschachtelt programmiere. Ein Beispiel aus PHP

ich hab ein Array das folgendermaßen aussieht:

$menuarray=  
array('Menuid'=>  
    array('Menuname'=>  
        array('level'=>  
            array('menupunktid'=>  
                array('name',  
                      'url',  
                      'published'  
                     )  
                 )  
             )  
         )  
    )

Um dies auszulesen und auszugeben durchlaufe ich dies mittels verschachtelter foreachschleifen, denn ich will ja Menuname einzeln ausgeben. Ich will auf die Level einzeln zugreifen können und auf die MenupunktIDs sowie auf die Daten der Menupunkte.

Ich weiß, das Beispiel ist etwas blöd, da dies mit Nested Sets besser zu lösen wäre. Es geht aber ums Grundsätzliche. Wie könnte man den Zugriff auf eine solche Struktur vereinfachen ohne tief verschachtelte Schleifen? Odre sollte ich mir um die Datenstruktur an sich Gedanken machen?

Für konstruktive Hinweise wär ich sehr dankbar.

Gruß Bobby

--
-> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <-
### Henry L. Mencken ###
-> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <-
## Viktor Frankl ###
ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)
  1. Odre sollte ich mir um die Datenstruktur an sich Gedanken machen?

    Ja - "id" und "menpunktid" sind redundant zueinander, level ist völlig überflüssig, menüname und name sind redundant und eine "parent"-Feld fehlt (wenn es flach sein soll).

    Wenn du es als baum abbilden willst, hilft dir ggf. das hier weiter:
    http://suit.rebell.at/fileadmin/a-26/source.tar.gz

    Das ist zwar jetzt für ein Filesystem, aber im Grunde ist das auf jedes beliebige Menü anwendbar.

  2. Hi!

    Ich weiß, das Beispiel ist etwas blöd, da dies mit Nested Sets besser zu lösen wäre.

    Jein. Nested Sets sind dann angebracht, wenn man eine an sich verschachtelte Struktur in einem flachen Medium speichern will. Datensätze in einem RDBMS bringen von sich aus keine Information zu Eltern und Kindern mit. Das übernimmt das Nested-Sets-Model. In einem verschachtelten Array ist die Eltern-Kind-Beziehungsinformation bereits enthalten, beziehungsweise gut abbildbar.

    Es geht aber ums Grundsätzliche. Wie könnte man den Zugriff auf eine solche Struktur vereinfachen ohne tief verschachtelte Schleifen? Odre sollte ich mir um die Datenstruktur an sich Gedanken machen?

    Ja.

    Für konstruktive Hinweise wär ich sehr dankbar.

    Ich weiß ja nicht, welchen Sinn du in deiner derzeitigen Verschachtlung siehst, was damit konkret abgebildet werden soll. Üblicher-/einfacherweise nimmt man ein Array of Menuitems. Jedes Menuitem hat diverse Eigenschaften, die man so braucht. Eine davon ist ein Array, welches weitere Menuitems als Kinder enthalten kann.

    Wenn du mehrere Menüs hast, dann kannst du da noch ein weiteres Array erstellen, das die Menüs als Elemente hat.

    Lo!

  3. Hallo,

    Mal ne grundsätzliche Frage. Mir passiert es immer wieder das ich zu komplexe Vorgänge zu tief verschachtelt programmiere. Ein Beispiel aus PHP

    ich hab ein Array das folgendermaßen aussieht:

    $menuarray=

    array('Menuid'=>
        array('Menuname'=>
            array('level'=>
                array('menupunktid'=>
                    array('name',
                          'url',
                          'published'
                         )
                     )
                 )
             )
        )

      
    das verstehe ich nicht - und das ist ernst gemeint. Es sieht nach einer Struktur für ein Menü aus.  
      
    Wie wäre es mit Kommentaren, \*was\* Du mit dieser Struktur abbilden willst. Vermutlich möchtest Du eine bessere Struktur verwenden :-)  
      
    Ein verschachteltes Menü lässt sich wunderbar als Baum darstellen. Bäume lassen sich sehr gut mit PHP-Arrays umsetzen. Wichtig ist es, dass Knoten identischen Aufbau haben.  
      
    
    > Um dies auszulesen und auszugeben durchlaufe ich dies mittels verschachtelter foreachschleifen, denn ich will ja Menuname einzeln ausgeben. Ich will auf die Level einzeln zugreifen können und auf die MenupunktIDs sowie auf die Daten der Menupunkte.  
      
    Das ist überhaupt keine gute Idee. Verfahren zum Durchlaufen von Bäumen gibts nicht so viele. Für Deinen Zweck wäre vermutlich pre-order-traversal günstig (entsprechende Datenstruktur vorausgesetzt), siehe z.B. [dieser Archivthread](/archiv/2011/3/t204031/#m1380942) mit rekursivem Durchlaufen mit einer weniger als 10 Zeilen langen Funktion, d.h. alles andere als Spaghetti-Code.  
      
    Wie Du dort sehen kannst, ergibt sich das Level aus der Verschachtelungstiefe und sollte daher nicht redundant in der Datenstruktur mitgeführt (und mitgepflegt) werden.  
      
      
    Freundliche Grüße  
      
    Vinzenz
    
  4. Moin

    OK, werden wir mal konkreter.

    ich habe eine Datenbanktabelle für ein Menu und bekomme folgendes Array raus:

    [menuid] => 12
    [menutitle] => Menutitel
    [id] => 88
    [name] => Menupunktname
    [link] => eineurl.html
    [type] => artikel
    [published] => 1
    [parent] => 0
    [sublevel] => 0
    [ordering] => 32
    [checked_out] => 0

    Wie sollte der Aufbau des Zielarrays sein? Und wie komm ich da hin?

    Gruß Bobby

    --
    -> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <-
    ### Henry L. Mencken ###
    -> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <-
    ## Viktor Frankl ###
    ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)
    1. Hi!

      Wie sollte der Aufbau des Zielarrays sein? Und wie komm ich da hin?

      Er soll so sein, dass du damit (effektiv und effizient) arbeiten kannst. Ich kenne deine Anforderungen nciht und kann meine Meinung nur allgemein geben, so wie ich ein Menü ohne Spezialanforderungen bauen würde. Da du ebenfalls nicht gesagt hast, welche Bedeutung die Felder haben, fang ich mal an zu raten.

      [menuid] => 12

      Ist die Bedeutung, dass es mehrere Menüs geben kann und dies eine Zuordnung zu einem Menü angibt? Dann Brauchst du das Feld in der Anwendung nicht. Hauptsache, du legst eine Struktur (ein Array) an, in der mehrere Menüs Platz finden, wenn du mehrere gleichzeitig aus dem DBMS holst. Wenn du immer nur ein Menü verwendest, dann brauchst du das Feld sicher nicht und kannst dir dessen Abfrage sparen.

      [id] => 88

      Wozu dient diese ID? Wenn du sie nicht brauchst, frag sie nicht ab.

      [published] => 1

      Wenn der Eintrag nicht angezeigt werden soll, kannst du ihn auch im DBMS lassen.

      [parent] => 0

      Diese Information ist wichtig, damit die Menüpunkte einem übergeordneten zugeordnet werden können. Nachdem du die flache Liste aus dem DBMS gelesen hast, sortierst du die Einträge unter den jeweiligen Elterneintrag und kannst diese Information verwerfen.

      [sublevel] => 0

      Vermutlich überflüssig.

      [ordering] => 32

      Die Sortierung kann das DBMS anhand dieses Feldes vornehmen. Wenn du geradeaus durch die Ergebnismenge gehst, werden die Daten auch in der Reihenfolge weiterverarbeitet / in Arrays gehängt / ... Dabei bleibt die Sortierung bestehen und du kannst dieses Feld aus der Ergebnismenge der Query entfernen.

      Die anderen Punkte transportieren anscheinend Nutzdaten, so dass aus ihnen der Menüpunkt besteht. Hinzu kommt ein Feld, dass in einem weiteren Array alle Kinder des Menüpunktes aufnimmt.

      Lo!

      1. Mahlzeit dedlfix,

        [published] => 1

        Wenn der Eintrag nicht angezeigt werden soll, kannst du ihn auch im DBMS lassen.

        Wenn der Eintrag nur für bestimmte Benutzer nicht angezeigt werden soll, für Editoren/Admins o.ä. aber doch, dann kann darüber vielleicht einfach die Art und Weise der Darstellung gesteuert werden.

        MfG,
        EKKi

        --
        sh:( fo:| ch:? rl:( br:> n4:~ ie:% mo:} va:) de:] zu:) fl:{ ss:) ls:& js:|
        1. Hi!

          [published] => 1
          Wenn der Eintrag nicht angezeigt werden soll, kannst du ihn auch im DBMS lassen.
          Wenn der Eintrag nur für bestimmte Benutzer nicht angezeigt werden soll, für Editoren/Admins o.ä. aber doch, dann kann darüber vielleicht einfach die Art und Weise der Darstellung gesteuert werden.

          Man kann ihn auch benutzerspezifisch aus dem DBMS holen oder eben nicht.

          Lo!

          1. Moin

            [published] => 1
            Wenn der Eintrag nicht angezeigt werden soll, kannst du ihn auch im DBMS lassen.
            Wenn der Eintrag nur für bestimmte Benutzer nicht angezeigt werden soll, für Editoren/Admins o.ä. aber doch, dann kann darüber vielleicht einfach die Art und Weise der Darstellung gesteuert werden.

            Man kann ihn auch benutzerspezifisch aus dem DBMS holen oder eben nicht.

            Es geht um den Status für die administrative Bearbeitung.

            Danke erstmal für die Tipps. Habe mich etwas in Nested Sets eingelesen und etwas über den =& Operator gelernt und das ganze nun mit wenigen Zeilen Code erledigt.

            Die Tips haben mir zumindest gezeigt das es so, wie ich es angegangen bin nur schwerlich funktioniert. Nun denk ich bin ich auf dem richtigen Weg.

            Gruß Bobby

            --
            -> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <-
            ### Henry L. Mencken ###
            -> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <-
            ## Viktor Frankl ###
            ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)
    2. Moin,

      OK, werden wir mal konkreter.

      Ja. Interessantes Thema!

      ich habe eine Datenbanktabelle für ein Menu und bekomme folgendes Array raus: [..]

      Wie sollte der Aufbau des Zielarrays sein? Und wie komm ich da hin?

      Bei mir existiert ein Baum nur in der Darstellung. D.h., eine Zwischenstufe in Form einer baumartigen Datenstruktur gibt es nicht. Ich habe Objekte mit OIDs wobei in der OID (Objekt-ID) der Zweig steckt:

      /
      /map/
      /map/apps/
      /map/apps/ipcalc

      usw. Die OID ist gleich dem URL der requested werden darf. Die Datenstruktur ist flach und sieht so aus (Perl):

        
       $objects{OID}{ATTR}; # OID == URL, ATTR => Title, Descr, Body etc.  
      
      

      Bei einem Request wird aus dem REQUEST_URI das Objekt erstellt (oder auch nicht, gibt dann Status: 404).

      Der Speicherort für meine Objekte ist wahlweise eine Datei oder MySQL mit einem selbst aufgesetzten Minimal-ORM. Dafür gibt es zwei Tie-Klassen, die das ganze redundant machen:

        
      my %pbin = ();  
      tie(%pbin, 'ORM', %opts) || tie(%pbin, 'Objects', $cfg->{path}->{contbin}, 'lock');  
      # ORM: Klasse zum Binden der Objekte an eine DB-Tabelle  
      # Objects: Klasse zum Binden der Objekte an eine Datei  
      
      

      Ist die DB mal weg, geht nur die Suchfunktion nicht, ansonsten ist alles da was zur Seite gehört.

      Soll ein Baum erzeugt werden, tut dies eine Rekursion über die OIDs bzw. URLs in der Liste der Objekte. Speicheroptimierung: Die Liste enthält nur die OIDs und die Namen der Attribute. Das Attribut selbst, z.B. der Body wird nur in den RAM geladen, wenn der URL selbst angefordert wird. Zur Ausgabe einer <ul> werden nur die Title-Attribute geladen und ggf. die Beschreibung (descr für den Meta-Tag).

      Hotti