Melvin Cowznofski: Ausgabe einer Liste mit Kategorien und Unterkategorien

Hallo!

Gegeben sei eine DB mit 2 Tabellen. In der 1. Tabelle befinden sich Kategorienamen, wobei jede Kategorie eine unique ID hat, in der 2. Tabelle befinden sich Unterkategorienamen, wobei jede Unterkategorie eine unique ID sowie als INT die ID jener Kategorie hat, zu der sie gehört:

Tabelle 'kategorien':

id | kategoriename
------------------
1  | Politik
2  | Musik
3  | Gesellschaft
4  | Kulinarik
5  | Geographie
6  | Automobiles

u.s.w. [...]

Tabelle 'unterkategorien':

id | kategorie | unterkategoriename
-----------------------------------
1  | 1         | Parteien
2  | 6         | Autohersteller
3  | 2         | Bands
4  | 6         | Autoforen
5  | 1         | Ministerien
6  | 4         | Kochrezepte
7  | 1         | Politikforen

u.s.w. [...]

Die Daten in den Tabellen ändern sich laufend, werden teilweise auch gelöscht, also steht keine lückenlose Durchnummerierung bei den IDs zur Verfügung!  Nun soll eine Liste mit den momentan gespeicherten Kategorien sowie den momentanen Unterkategorien als jeweilige Unterliste ausgegeben werden:

* Politik
     o Parteien
     o Ministerien
     o Politikforen
* Musik
     o Bands
* Gesellschaft
* Kulinarik
     o Kochrezepte
* Geographie
* Automobiles
     o Autohersteller
     o Autoforen

u.s.w. [...]

Ich habe das nun in 3 Schritten folgendermaßen gelöst:

Schritt 1: Zuerst werden in 2 DB-Abfragen die Daten der beiden Tabellen geholt und jeweils in einem Array gespeichert.

Schritt 2: In jedem Fall werden nun in einer foreach() Schleife 2 neue Arrays gebildet. Im Array "$array_kategorien" speichere ich die Namen der Kategorien, wobei die Indizes die jeweiligen Kategorie-IDs sind. Und im Array "$array_kategorien_IDs" speichere ich einfach systematisch jede vorhandene Kategorien-ID.

Schritt 3: Für jede Kategorie wird nun ein Array gebildet, dessen Variablenname dynamisch zusammengesetzt wird und die ID der Kategorie enthält. Es entsteht also die Arrays "array_unterkategorien_1", "array_unterkategorien_2", "array_unterkategorien_3", u.s.w., wobei in jedem dieser Arrays anschließend die jeweiligen Unterkategorienamen gespeichert werden.

Nun kann es mit folgender Systematik zur Ausgabe der Liste mit den Unterlisten kommen:

foreach($array_kategorien_IDs AS $id)  
   {  
      // Ausgabe der Kategorien:  
      echo "<li>".$array_kategorien[$id]."</li>";  
      // Ausgabe der Unterkategorien als Unterliste:  
      if(count(${'array_unterkategorien_'.$id})>0)  
         {  
            echo"<ul>";  
            foreach(${'array_unterkategorien_'.$id} AS $unterkategorie)  
               {  
                  echo"<li>".$unterkategorie."</li>";  
               }  
            echo"</ul>";  
         }  
   }

Das funktioniert wunderbar und liefert genau das gewünschte Ergebnis. Meine Frage an Euch ist nun, ob das so gut gelöst ist oder ob es da einen einfacheren Weg mit kürzerem Quellcode gäbe bzw. wie Ihr das lösen würdet.

Mit lieben Grüßen

Melvin Cowznofski

--

Melvin Cowznofski
What – me worry?
  1. Om nah hoo pez nyeetz, Melvin Cowznofski!

    Nur eine Bemerkung:

    Die Daten in den Tabellen ändern sich laufend, werden teilweise auch gelöscht, also steht keine lückenlose Durchnummerierung bei den IDs zur Verfügung!

    IDs sind ausschließlich dafür gedacht, einen Datensatz zu identifizieren. Sie sind _niemals_ geeignet, etwa eine Reihenfolge zu bestimmen. Auch wenn man die IDs nicht automatisiert vergibt und man die volle Kontrolle über sie hat, sollte man sie nicht als Kriteritum für Vergleiche o.ä. missbrauchen.

    Matthias

    --
    Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Volt und Voltaire.

    1. Hallo Matthias!

      Nur eine Bemerkung: IDs sind ausschließlich dafür gedacht, einen Datensatz zu identifizieren. Sie sind _niemals_ geeignet, etwa eine Reihenfolge zu bestimmen.

      Völlig richtig! Ich hoffe, meine Formulierungen haben nicht den Anschein erweckt, dass ich das anders sehe!

      Und was sagst Du zu der Lösung, mit der ich mein Ziel erreicht habe?

      Mit lieben Grüßen

      Melvin Cowznofski

      --

      Melvin Cowznofski
      What – me worry?
  2. Moin,

    Das funktioniert wunderbar und liefert genau das gewünschte Ergebnis. Meine Frage an Euch ist nun, ob das so gut gelöst ist oder ob es da einen einfacheren Weg mit kürzerem Quellcode gäbe bzw. wie Ihr das lösen würdet.

    Dafür würde sich doch ein assoziatives, verschachteltes Array anbieten. Die Abfrage würde ca. so lauten:

    SELECT kategoriename, unterkategoriename FROM kategorien  
    LEFT JOIN unterkategorien ON unterkategorien.kategorie = kategorien.id  
    ORDER BY 1, 2
    

    Dann würde ich einfach das Array aufbauen:

    while($result = mysql_fetch_assoc($query)) {  
      $kategorien[$result['kategoriename']][] = $result['unterkategoriename'];  
    }
    

    Dann kannst du mit array_keys() auf die Kategorien zugreifen und damit dann auf die Unterkategorien.

    (alles ungetestet)

    Grüße Marco

    --
    Ich spreche Spaghetticode - fließend.
    1. Hallo Marco!

      Danke für den Input aber das finde ich _noch_ komplizierter als meine Lösung. Jedenfalls um nichts einfacher oder kürzer. Abgesehen davon wäre das ja nur der Weg bis vor die Listenausgabe. Wie Du dann aus dem entstandenen Array die Liste mit den Unterlisten erstellst, fehlt ja noch.

      Mit lieben Grüßen

      Melvin Cowznofski

      --

      Melvin Cowznofski
      What – me worry?
  3. hi,

    nur interessehalber, wenn Du Dich eh an eine DB stark bindest, warum nicht gleich das Mengenmodell nested sets?

    Ansonsten: Wenn Du eindeutige Schlüssel hast, reicht ein zusätzliches Feld (parent) um eine Hierarchie abbilden zu können. Das kannst Du mit einem Durchlauf rumdrehen zu einer child-relation und mit einer Rekursion ausgeben. Kommt vom Code her auf dasselbe raus, ist jedoch dann von der Art und Weise der Datenhaltung abstrahiert.

    Hotti

    --
    Meditoxin: Und Sie husten nie wieder!
    1. Hallo Hotti!

      Ich würde Dir Deine Frage gerne beantworten und Deinem Hinweis nachgehen, leider beschränkt sich mein Wissen und meine Erfahrungen mit MySQL wirklich nur auf die wichtigsten Basics. Ich habe deshalb nichts von Deinem Posting verstanden.

      Mit lieben Grüßen

      Melvin Cowznofski

      --

      Melvin Cowznofski
      What – me worry?
  4. Hallo!

    Also die bisherigen Beiträge habe ich ja leider nicht wirklich nachvollziehen können, allerdings habe ich in der Zwischenzeit meine eigene Lösung deutlich verkürzt und vereinfacht:

    Mit 2 DB Abfragen werden die Daten der beiden Tabellen in einem assoziativen Array gespeichert ('$rows_kategorien' und '$rows_unterkategorien') und ohne sonst noch was zu tun kann es sofort zur Ausgabe kommen, die nun so aussieht:

    echo "<ul>";  
       foreach($rows_kategorien AS $kategorie)  
          {  
             echo "<li>".$kategorie['name']."</li>";  
             foreach($rows_unterkategorien AS $unterkategorie)  
                {  
                   if($unterkategorie['category'] == $kategorie['id'])  
                      {  
                         echo "<ul>";  
                         echo "<li>".$unterkategorie['name']."</li>";  
                         echo "</ul>";  
                      }  
                }  
          }  
    echo "</ul>";
    

    Der Unterschied zu meiner ursprünglichen Lösung ist nun, dass für jeden Listenpunkt der "Hauptliste", also für jede Kategorie, das _komplette_ Array mit den Unterkategorien durchgearbeitet wird, um passende Einträge für eine (evt.) Unterliste zu finden.

    Das ist zwar performancelastiger, aber im Code um ein Vielfaches kürzer und simpler. Und da es sich in meinem Fall um maximal 10-30 Kategorien mit jeweils maximal bis zu 10-20 Unterkategorien handelt, ist, was die Geschwindigkeit der Skriptabarbeitung und somit des Seitenaufbaus bezrifft, bis dato kein Delay bemerkbar.

    Vereinfachteren Code kann ich es mir jetzt nicht mehr vorstellen, aber ich bin natürlich für jeden Vorschlag weiter offen wie man das, was ich hier realisiere, noch eleganter lösen könnte.

    Mit lieben Grüßen

    Melvin Cowznofski

    --

    Melvin Cowznofski
    What – me worry?
    1. Tach!

      Also die bisherigen Beiträge habe ich ja leider nicht wirklich nachvollziehen können,

      Dass der Inhalt von IDs bedeutungslos sein sollte, hast du sicher verstanden. Das bringt dich im eigentlichen Problem nicht weiter, aber wenn man sich dies beherzigt, muss man sich keinerlei Gedanken mehr um über die Lückenhaftigkeit machen.

      Nested Sets ist ein Fachbegriff für eine Abbildungsart hierarchischer Daten in einer flachen Datentabelle. Sie bringen einige Vorteile, wenn man Teilmengen abfragen möchte (besonders, wenn man mehr als zwei Ebenen hat), sind aber deutlich komplexer bei Änderungen. Wenn du keine Teilbäume sondern stets den gesamten Kategorienbaum benötigst, bringt dir Nested Sets keinen wirklichen Vorteil. Das  Abfrageergebnis ist in jedem Fall flach, und wenn man daraus ein verschachteltes Array haben möchte, ist das in beiden Fällen gleich aufwendig.

      allerdings habe ich in der Zwischenzeit meine eigene Lösung deutlich verkürzt und vereinfacht:

      Bei der alten war auch nicht schön, dass du mit variablen Variablen hantiert hast. Als Faustregel: Wenn du beim Programmieren noch nicht weißt, welche Namen zur Laufzeit entstehen, dann willst du lieber ein Array statt einzelner Variablen. Einzelne Variablen bringen dann keinen Vorteil, eher Nachteile, zum Beispiel beim Iterieren.

      Mit 2 DB Abfragen werden die Daten der beiden Tabellen

      Warum sind es überhaupt zwei Tabellen? Sind die Daten von Haupt- und Unterkategorie so verschieden? Wenn sie gleich sind, dann nähme ich eine Tabelle im Stile deiner jetzigen Unterkategorie-Tabelle und die Hauptkategorien bekommen einen Parent-Zeiger mit dem Wert 0 (oder NULL).

      in einem assoziativen Array gespeichert ('$rows_kategorien' und '$rows_unterkategorien')

      Also in _je_ einem. Spricht war gegen ein verschachteltes, bei dem die Unterkategorien zur jeweilgen Hauptkategorie zugeordnet sind? Das sieht dann am Ende so aus:

      $categories = array(
        1 => array(
          'name' => 'Name Kat 1'
          'subcategories' => array(
            2 => 'Name UnterKat 2',
            3 => 'Name UnterKat 3',
          ),
        ),
        8 => array(
          'name' => 'Name Kat 8'
          'subcategories' => array(
            23 => 'Name UnterKat 23',
            42 => 'Name UnterKat 42',
          ),
        ),
      );

      Damit komtm noch eine weitere Array-Ebene ins Spiel, aber irgendwie muss man ja die Hauptkategoriedaten zusammenhalten. Das Ergebnis dieser Struktur ist, dass du nun gezielt über die Unterkategorien iterieren kannst und dabei nicht die Parent-ID überprüfen musst. Es ist nur ein wenig aufwendiger diese Struktur zu erstellen. Sortiere die Datensätze so, dass die mit Parent = 0/NULL zuerst kommen. Dann kannst du zuerst die Hauptkategorien erstellen und die nachfolgenden Unterkategorien einhängen.

      Das ist zwar performancelastiger, aber im Code um ein Vielfaches kürzer und simpler. Und da es sich in meinem Fall um maximal 10-30 Kategorien mit jeweils maximal bis zu 10-20 Unterkategorien handelt, ist, was die Geschwindigkeit der Skriptabarbeitung und somit des Seitenaufbaus bezrifft, bis dato kein Delay bemerkbar.

      Performance spielt bei solch einer kleinen Datenmenge üblicherweise noch keine Rolle.

      dedlfix.

      1. Hallo dedlfix!

        Warum sind es überhaupt zwei Tabellen?

        Na wie sonst? Ich brauche ja pro Kategorie eine _eindeutige_ ID, damit ich etwas habe, worauf sich die Unterkategorie beziehen kann. Sonst weiß ich ja nicht, zu welcher Hauptkategorie sie gehört.

        Wenn ich Beides in eine Tabelle schmeisse, dann nummeriert mir die ID einfach die Einträge durch. Völlig sinnlos. Ich brauche IDs für meine Hauptkategorien und IDs für meine Unterkategorien.

        Sind die Daten von Haupt- und Unterkategorie so verschieden?

        Es handelt sich um eine Link-Sammlung, also eine Lesezeichenverwaltung von URLs. (Siehe die "Tabelleninhalte" in meinem Ursprungsposting.) Die Hauptkategorien sind Themenüberschriften - unter denen sich die Unterkategorien aufteilen. Diese sind wieder Themen, unter denen sich dann die Links aufteilen. Es gibt dann also noch eine 3. Tabelle mit allen URLs und 2 INT Feldern, die sich auf die IDs von Kategorie und Unterkategorie beziehen. Bei dem in meinem Ausgangsposting zugrunde liegenden Fall geht es aber jetzt gar nicht um die Lnks, sondern rein um die Ausgabe einer Liste mit allen Kategorien und Unterkategorien. (In der Folge brauche ich das zuer Erstellung einer Navigation.)

        Wenn sie gleich sind, dann nähme ich eine Tabelle im Stile deiner jetzigen Unterkategorie-Tabelle und die Hauptkategorien bekommen einen Parent-Zeiger mit dem Wert 0 (oder NULL).

        Wie gesagt - das würde mir nichts bringen, weil wenn eine neue Unterkategorie hinzukommt, muss ja festgelegt werden, zu welcher Hauptkategorie sie gehört. Und somit brauche ich Hauptkategorien mit eindeutigen IDs.

        in einem assoziativen Array gespeichert ('$rows_kategorien' und '$rows_unterkategorien')
        Also in _je_ einem. Spricht war gegen ein verschachteltes, bei dem die Unterkategorien zur jeweilgen Hauptkategorie zugeordnet sind?

        Wieso so kompliziert? Ob ich jetzt 1 mehrdimensionales Array oder 2 eindimensionale Arrays habe, ist mir völlig egal. Aber abgesehen davon: Schön und gut ... und wie stellst Du Dir dann vor, aus diesem mehrdimensionalen Array die Liste wie von mir gewünscht auszugeben? Das wird ja dann _noch_ komplizierter. Also bisher hat mir niemand aufzeigen können, dass es einen simpleren/unkomplizierteren/kürzeren Weg gibt als meine letzte Lösung.

        Damit komtm noch eine weitere Array-Ebene ins Spiel, aber irgendwie muss man ja die Hauptkategoriedaten zusammenhalten. Das Ergebnis dieser Struktur ist, dass du nun gezielt über die Unterkategorien iterieren kannst und dabei nicht die Parent-ID überprüfen musst. Es ist nur ein wenig aufwendiger diese Struktur zu erstellen. Sortiere die Datensätze so, dass die mit Parent = 0/NULL zuerst kommen. Dann kannst du zuerst die Hauptkategorien erstellen und die nachfolgenden Unterkategorien einhängen.

        Wie gesagt, ich wolte wissen, ob es noch simpler/kürzer/einfacher geht. Das wäre hier sicher nicht der Fall.

        Mit lieben Grüßen

        Melvin Cowznofski

        --

        Melvin Cowznofski
        What – me worry?
        1. Moin,

          Wieso so kompliziert?

          Es ist nicht kompliziert, sondern IMHO einfacher, als dein Weg.

          und wie stellst Du Dir dann vor, aus diesem mehrdimensionalen Array die Liste wie von mir gewünscht auszugeben? Das wird ja dann _noch_ komplizierter.

          zum Beispiel so:

          foreach(array_keys($arr) as $kategorie) { // durch die Kategorien gehen  
             print '<li>'.$kategorie;  
             if(count($arr[$kategorie])>0)print '<ul>'; // nur, wenn Unterkategorien vorhanden sind eine Unterliste  
             foreach($arr[$kategorie] as $unterkategorie) {  
                print '<li>'.$unterkategorie.'</li>';  
             }  
             if(count($arr[$kategorie])>0)print '</ul>';  
             print '</li>';  
          }
          

          (ungetestet)

          Wie gesagt, ich wolte wissen, ob es noch simpler/kürzer/einfacher geht. Das wäre hier sicher nicht der Fall.

          Ich denke schon. Du kannst mit einer Abfrage und 2 Schleifen dein Ziel erreichen. Siehe auch nochmal meine erste Antwort.

          Grüße Marco

          --
          Ich spreche Spaghetticode - fließend.
        2. Tach!

          Warum sind es überhaupt zwei Tabellen?
          Na wie sonst? Ich brauche ja pro Kategorie eine _eindeutige_ ID, damit ich etwas habe, worauf sich die Unterkategorie beziehen kann. Sonst weiß ich ja nicht, zu welcher Hauptkategorie sie gehört.

          Ja, und das geht.

          Wenn ich Beides in eine Tabelle schmeisse, dann nummeriert mir die ID einfach die Einträge durch. Völlig sinnlos. Ich brauche IDs für meine Hauptkategorien und IDs für meine Unterkategorien.

          Die Inhalte der ID sind belanglos. Sie müssen nur eindeutig sein. Und das ist auch mit einer Tabelle gewährleistet.

          ID | ParentID | Name
          1  | 0        | Hauptkategorie
          2  | 1        | Unterkategorie
          3  | 0        | zweite Haupt
          4  | 0        | dritte Haupt
          5  | 3        | Unterkat zur zweiten

          Verschachtelte Strukturen lassen sich problemlos in einer Tabelle unterbringen.

          Es handelt sich um eine Link-Sammlung, also eine Lesezeichenverwaltung von URLs. (Siehe die "Tabelleninhalte" in meinem Ursprungsposting.) Die Hauptkategorien sind Themenüberschriften - unter denen sich die Unterkategorien aufteilen. Diese sind wieder Themen, unter denen sich dann die Links aufteilen. Es gibt dann also noch eine 3. Tabelle mit allen URLs und 2 INT Feldern, die sich auf die IDs von Kategorie und Unterkategorie beziehen.

          Und die ist vermutlich auch unnötig. Die Links können ebenfalls mit in der einen Tabelle als weiteres Feld stehen.

          Wenn sie gleich sind, dann nähme ich eine Tabelle im Stile deiner jetzigen Unterkategorie-Tabelle und die Hauptkategorien bekommen einen Parent-Zeiger mit dem Wert 0 (oder NULL).
          Wie gesagt - das würde mir nichts bringen, weil wenn eine neue Unterkategorie hinzukommt, muss ja festgelegt werden, zu welcher Hauptkategorie sie gehört. Und somit brauche ich Hauptkategorien mit eindeutigen IDs.

          Doch doch, das geht alles mit einer Tabelle. Es gibt dabei keine Probleme mit eindeutigen IDs.

          Spricht was gegen ein verschachteltes, bei dem die Unterkategorien zur jeweilgen Hauptkategorie zugeordnet sind?
          Wieso so kompliziert? Ob ich jetzt 1 mehrdimensionales Array oder 2 eindimensionale Arrays habe, ist mir völlig egal.

          Das eine mag komplexer aussehen, hat aber den Vorteil, dass es beim Verarbeiten einfacher zugeht, weil die Beziehungen zueinander bereits da sind und nicht jedes Mal überprüft werden müssen.

          Aber abgesehen davon: Schön und gut ... und wie stellst Du Dir dann vor, aus diesem mehrdimensionalen Array die Liste wie von mir gewünscht auszugeben? Das wird ja dann _noch_ komplizierter. Also bisher hat mir niemand aufzeigen können, dass es einen simpleren/unkomplizierteren/kürzeren Weg gibt als meine letzte Lösung.

          foreach ($categories as $category) {  
            echo "<li>", htmlspecialchars($category['name']);  
            if ($category['subcategories']) {  
              echo "<ul>";  
              foreach ($category['subcategories'] as $subcategory) {  
                echo "<li>".$unterkategorie['name']."</li>";  
              }  
              echo "</ul>";  
            }  
            echo "</li>";  
          }
          

          Dein Code hat auch noch einen HTML-ul-li-Verschachtlungsfehler enthalten. Mein COde hat jetzt immer noch die gleiche Anzahl an foreach (2) und if (1), aber das if würdest du auch benötigen, wenn du richtig schachtelst und für leere Kategorien kein unnützes <ul></ul> im Ergebnis haben möchtest. Somit hätte deine Lösung dann 2 if und wäre komplexer.

          Wie gesagt, ich wolte wissen, ob es noch simpler/kürzer/einfacher geht. Das wäre hier sicher nicht der Fall.

          Es kommt nicht immer auf die Kürze beim Code an. Das Abbilden der logischen Zusammenhänge in _einer_ Datenstruktur kann einem zu schnellerem Erkenntnisgewinn verhelfen, als wenn man sich die Beziehungen zueinander über die im Code verstreuten if zusammensuchen muss. Code/Strukturen für Computer lesbar zu schreiben/entwerfen, ist kein Problem. Code/Strukturen für Menschen lesbar zu schreiben/entwerfen, ist die hohe Kunst.

          dedlfix.

          1. Tach!

            foreach ($categories as $category) {

            echo "<li>", htmlspecialchars($category['name']);
              if ($category['subcategories']) {
                echo "<ul>";
                foreach ($category['subcategories'] as $subcategory) {
                  echo "<li>".$unterkategorie['name']."</li>";
                }
                echo "</ul>";
              }
              echo "</li>";
            }

              
            Die "innerste" Zeile muss natürlich so aussehen:  
              
              echo "<li>", htmlspecialchars($subcategory), "</li>";  
              
              
            dedlfix.
            
          2. Hallo dedlfix!

            Ja, und das geht.

            Das mag schon sein, was daran alerdings besser ist, entzieht sich meiner Logik.

            Die Inhalte der ID sind belanglos.

            Was genau meinst Du bitte damit? Der "Inhalt der ID"? Verstehe ich Dich richtig, dass Du damit sagen willst, jede Kategorie braucht eine eindeutige ID und jede Unterkategorie braucht eine eindeutige ID, aber es muss nicht jede Zahl als ID in jeder Gruppe stehen? Analog dazu: Es gibt keine getrennten Kinokarten für Männer und Frauen, beide bekommen die selben, trotzdem hat jede einzelne Kinokarte als "unique ID" einen eindeutigen, nicht doppelt vorkommenden Sitzplatz. War es das, was Du mit dem "Die Inhalte der ID sind belanglos" sagen wolltest?

            Ich frage mich weiterhin, wieso das besser sein soll. Jedenfaslls war das auch schon das Einzige, was ich an Deinem Beitrag glaube, verstanden zu haben.

            Es gibt dann also noch eine 3. Tabelle mit allen URLs
            Und die ist vermutlich auch unnötig. Die Links können ebenfalls mit in der einen Tabelle als weiteres Feld stehen.

            Also bei allem Respekt, das ist Nonsens. Ich fasse in Tabellen Daten zusammen, die auch in Sinn/Inhalt einen Zusammenhang haben. Ich lasse mir theoretisch ja noch einreden, dass das in meinem Fall bei Kategorien und Unterkategorien der Fall ist, weil beides thematische Beschreibungen für die URLs sind. Die URLs selbst sind dann aber eine _völlig_ andere Art von Information. Zu sagen, man soll doch alles in eine Tabelle packen, widerspricht dem Ziel der Normalisierung. Zumindest macht es jemanden, der Redundanzen vermeiden möchte, das Leben nicht gerade einfacher.

            Um eine weitere Analogie aufzuzeigen: Mit der selben Logik würde man jemenden, der eine DB für einen Autovermieter erstellt und eine Tabelle mit den Fahrzeugkategorien sowie eine mit den Fahrzeugen füllt, sagen, er soll doch die Autos in die selbe Tabelle wie die Fahrzeugkategorien schmeissen. Auch hier stünden dann 2 _völlig_ verschiedene Informationen in eienr Tabelle.

            Dein Code hat auch noch einen HTML-ul-li-Verschachtlungsfehler enthalten.

            Stimmt, das habe ich tatsächlich nicht bedacht, dass eine <ul> in einer anderen <ul> nur zwischen 2 <li> valide ist. Dabei <http://de.selfhtml.org/html/text/listen.htm#verschachteln@title=steht das in der Doku> extra drin.

            Nur verstehe ich Dein PHP nicht. Wo kommt das "$category['subcategories']" plötzlich her? Das hat weder was mit meiner 2. Lösung, noch mit der von Dir vorgeschlagenen Tabelle, in der Kategorien und Unterkategorien gemeinsam stehen, zu tun. Abgesehen davon:

            ID | ParentID | Name
            1  | 0        | Hauptkategorie
            2  | 1        | Unterkategorie
            3  | 0        | zweite Haupt
            4  | 0        | dritte Haupt
            5  | 3        | Unterkat zur zweiten

            Wenn diese Daten in einem Array landen, wie soll dann via PHP eine Liste mit den zusammengehörenden Unterkategorien als Unterliste entstehen, ohne dass auch hier für jede Zeile das komplette Array durchlaufen werden muss, wenn Kategorien und zusammengehörende Unterkategorien völlig durcheinender in der Tabelle gespeichert sind?

            Generell: Bei Deiner Tabelle ende ich mit einem mehrdimensionalen Array (Und ja, ich weiß, dass _Du_ das _nicht_ so nennst in PHP). Wenn ich das jetzt mit einem foreach($rows AS $row) durchlaufen lasse, habe ich pro Zeile 3 Informationen zur Verfügung: $row['id'], $row['ParentID'] und $row['Name']. Und wie soll da jetzt eine Liste entstehen, wo Kategorien und Unterkategorien logisch richtig angeordnet sind?

            Das eine mag komplexer aussehen, hat aber den Vorteil, dass es beim Verarbeiten einfacher zugeht, weil die Beziehungen zueinander bereits da sind und nicht jedes Mal überprüft werden müssen.

            Also was da einfacher sein soll als meine 2. Lösung, sehe ich nicht.

            Es kommt nicht immer auf die Kürze beim Code an. Das Abbilden der logischen Zusammenhänge in _einer_ Datenstruktur kann einem zu schnellerem Erkenntnisgewinn verhelfen, als wenn man sich die Beziehungen zueinander über die im Code verstreuten if zusammensuchen muss. Code/Strukturen für Computer lesbar zu schreiben/entwerfen, ist kein Problem. Code/Strukturen für Menschen lesbar zu schreiben/entwerfen, ist die hohe Kunst.

            Ich habe viele Links. Jeder Link ist einer Unterkategorie zugeordnet. Und mehrere Unterkategorien sind thematisch zusammengehörend und einer Kategorie zugeordnet. Punkt. Ende. Um diesen Zusammenhang zu verstehen, brauche ich doch nicht alles noch komplizierter machen, als es ohnehin schon ist.

            Apropos "Code/Strukturen für Menschen lesbar zu schreiben/entwerfen, ist die hohe Kunst." und weil mir das immer wieder so auf der Zunge liegt: _Genau deshalb_ steigen mir bei der gängigen Art, wie viele Programmierer in PHP bei 'if' oder 'foreach' die geschweiften Klammern setzen, die Grausbirnen auf. Schon bei einem Miniaturbeispiel ist doch

            foo bar foo  
              {  
                bar foo foo  
              }  
            bar bar foo  
            baar foo bar  
              {  
                foo bar  
                  {  
                    bar bar  
                  }  
              }
            

            auf den ersten Blick viel schneller und einfacher zu erfassen als würde man das Ganze als

            foo bar foo{  
                bar foo foo  
                }  
            bar bar foo  
            baar foo bar{  
                foo bar{  
                    bar bar  
                    }  
                }
            

            schreiben. Sobald mehr als 1 Klammer gleichzeitig offen ist, geht bei der herkömmlichen Schreibweise das Gefühl, was da jetzt womit zusammengehört und welcher Block zu welcher Bedingung gehört, verloren. Und wenn statt so kurzem sinnfreien Inhalt dann auch noch mehrzeiliger Code steht, ist ein schnelles Verstehen der logischen Zusammenhänge völlig unmöglich. Aber das sehe ich offenbar als _einziger_ Mensch so, weil ob im Manual, in Tutorials oder hier im Forum - es wird immer die 2. Schreibweise verwendet.

            Eine letzte Frage noch: In diesem speziellen Fall schreibe ich das Ganz nur für mich persönlich. Da ich nur URLs kompiere und in einem Formulatr einfüge, weiß ich ja, dass hier keine html Tags landen werden. Würdest Du da _trotzdem_ mit htmlspecialchars() arbeiten und wenn ja, wieso?

            Mit lieben Grüßen

            Melvin Cowznofski

            --

            Melvin Cowznofski
            What – me worry?
            1. Om nah hoo pez nyeetz, Melvin Cowznofski!

              Die Inhalte der ID sind belanglos.

              Was genau meinst Du bitte damit? Der "Inhalt der ID"? Verstehe ich Dich richtig, dass Du damit sagen willst, jede Kategorie braucht eine eindeutige ID und jede Unterkategorie braucht eine eindeutige ID, aber es muss nicht jede Zahl als ID in jeder Gruppe stehen?

              Die IDs müssen nur eindeutig sein, es könnten sogar Zufallszahlen sein.

              Es gibt dann also noch eine 3. Tabelle mit allen URLs
              Und die ist vermutlich auch unnötig. Die Links können ebenfalls mit in der einen Tabelle als weiteres Feld stehen.

              Wenn es eine Tabelle "Kategorien" und "Unterkategorien" gibt, dann lässt sich zwischen denen eine 1:n-Beziehung herstellen. Da es beides Kategorien sind, lässt sich die 1:n-Beziehung auch zur selben Tabelle herstellen.

              Ebenso bräuchtest du keine eigenen Tabellen für "Frauen" und "Männer", nur weil du den Status des Verheiratetseins abbilden willst (unabhängig davon, dass es auch gleichgeschlechtliche Partner gibt)

              Aus meiner Sicht wäre folgende Tabelle sinnvoll

              ID | KNr | KName | Parent | Linktext | Linkziel

              In der Spalte Parent steht die KNr. der Elternkategorie bzw. 0, wenn es kein Elternteil gibt.

              Ich sehe nicht, wo du da Redundanzen erzeugst.

              Um eine weitere Analogie aufzuzeigen: Mit der selben Logik würde man jemenden, der eine DB für einen Autovermieter erstellt und eine Tabelle mit den Fahrzeugkategorien sowie eine mit den Fahrzeugen füllt, sagen, er soll doch die Autos in die selbe Tabelle wie die Fahrzeugkategorien schmeissen. Auch hier stünden dann 2 _völlig_ verschiedene Informationen in eienr Tabelle.

              In dem Fall ist eine Kategorie tatsächlich etwas anderes als ein Auto.

              auf den ersten Blick viel schneller und einfacher zu erfassen als würde man das Ganze als

              foo bar foo{

              bar foo foo
                  }
              bar bar foo
              baar foo bar{
                  foo bar{
                      bar bar
                      }
                  }

              
              >   
                
              Man sollte auch "vorne" wieder ankommen mit der schließenden Klammer und Leerzeilen sind auch nicht unwichtig.  
                
              ~~~php
              foo bar foo {  
                  bar foo foo  
              }  
                
              bar bar foo  
                
              baar foo bar {  
                  foo bar {  
                      bar bar  
                  }  
              }
              

              Das Beispiel ist auch dumm gewählt, weil ja in echt nicht bar foo drinsteht. sondern

              for ($i = 1; $i < 10; $i++ ) {  
                  bar foo foo  
              }  
                
              bar bar foo  
                
              foreach ($cats as $value) {  
                  if ($value != 0) {  
                      bar bar  
                  }  
              }
              

              Ich habe mich übrigens mit der alternativen Schreibweise von Kontrollstrukturen in PHP angefreundet, das macht es mMn. übersichtlicher wenn man html-Ausgaben im PHP-Code braucht.

              for ($i = 1; $i < 10; $i++ ) :  
                  bar foo foo  
              endfor;  
                
              bar bar foo  
                
              foreach ($cats as $value) :  
                  if ($value != 0) :  
                      bar bar  
                  endif;  
              endforeach;
              

              Man darf die beiden Schreibweisen allerdings nicht mischen. (Und unser Syntaxhighlighter kennt die nicht.)

              Eine letzte Frage noch: In diesem speziellen Fall schreibe ich das Ganz nur für mich persönlich. Da ich nur URLs kompiere und in einem Formulatr einfüge, weiß ich ja, dass hier keine html Tags landen werden.

              Wenn es URIs sind die du "in schön" ausgeben möchtest, verwende urldecode.

              http://example.com/wiki/überflüssige Füßlinge

              http%3A%2F%2Fexample.com%2Fwiki%2F%C3%BCberfl%C3%BCssige%20F%C3%BC%C3%9Flinge

              Matthias

              --
              Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Hang und Hangar.

              1. Hallo Matthias!

                Danke für die ausführliche Antwort!

                Die IDs müssen nur eindeutig sein, es könnten sogar Zufallszahlen sein.

                Ich habe in der Zwischenzeit verstanden, wie das gemeint war.

                Ebenso bräuchtest du keine eigenen Tabellen für "Frauen" und "Männer", nur weil du den Status des Verheiratetseins abbilden willst (unabhängig davon, dass es auch gleichgeschlechtliche Partner gibt)

                Sorry, ich bin kein politisch korrekter Mensch und verweigere den so modernen Begriff des "gleichgeschlechtlichen Partners". Sowas gibt es nicht für mich. Es mag Fetische und sexuelle Vorlieben geben. Und das ist auch gut so und zu respektieren. Aber eine Partnerschaft/Ehe gibt es in meinen Augen nur zwischen Mann und Frau. Auch wenn mir irgendwelche Genderorganisationen, linke Gutmenschen oder die Gesellschaft im Allgemeinen zum 50. Millionsten Mal das Gegenteil einreden wollen. Aber lassen wir das Thema, gehört eh nicht hier her.

                Aus meiner Sicht wäre folgende Tabelle sinnvoll
                ID | KNr | KName | Parent | Linktext | Linkziel
                In der Spalte Parent steht die KNr. der Elternkategorie bzw. 0, wenn es kein Elternteil gibt.
                Ich sehe nicht, wo du da Redundanzen erzeugst.

                Also auf Wikipedia steht: "Die Normalisierung hat den Zweck, Redundanzen (mehrfaches Festhalten des gleichen Sachverhalts) zu verringern und dadurch verursachte Anomalien (z. B. infolge Änderung an nicht allen Stellen) zu verhindern, um so die Aktualisierung einer Datenbank zu vereinfachen (wegen Änderung an lediglich einer Stelle) sowie die Konsistenz der Daten zu gewährleisten."

                Wenn ich bei Deinem Beispiel den Namen einer Kategorie verändern möchte, kann ich das nicht an einer einzigen Stelle tun, sondern muss es bei jedem Link-Datensatz, der dieser Kategorie zugeordnet ist, ändern. Das ist doch wohl ein exemplarischer Fall, der nach Normalisierung geradezu schreit.

                Um eine weitere Analogie aufzuzeigen: Mit der selben Logik würde man jemenden, der eine DB für einen Autovermieter erstellt und eine Tabelle mit den Fahrzeugkategorien sowie eine mit den Fahrzeugen füllt, sagen, er soll doch die Autos in die selbe Tabelle wie die Fahrzeugkategorien schmeissen. Auch hier stünden dann 2 _völlig_ verschiedene Informationen in eienr Tabelle.

                Man sollte auch "vorne" wieder ankommen mit der schließenden Klammer und Leerzeilen sind auch nicht unwichtig.

                Ich persönlich finde die Art und Weise im von mir gezeigten ersten Beispielfall immer noch am übersichtlichsten. Bei dem Stil erkenne _ich_ am schnellsten die Zusammenhänge. Aber ich denke, wir sind uns darüber einig, dass es jeder so handhaben soll, wie er den Codezusammenhang am leichtesten mental verarbeiten kann.

                Ich habe mich übrigens mit der alternativen Schreibweise von Kontrollstrukturen in PHP angefreundet, das macht es mMn. übersichtlicher wenn man html-Ausgaben im PHP-Code braucht.
                Man darf die beiden Schreibweisen allerdings nicht mischen. (Und unser Syntaxhighlighter kennt die nicht.)

                Auch eine Möglichkeit.

                Wenn es URIs sind die du "in schön" ausgeben möchtest, verwende urldecode.

                Die URL wird von mir im Browser markiert, dann mittels rechter Maustaste kopiert und dann in einem Formular wieder mittels rechter Maustaste eingefügt. Die URL wird dann samt http:// und einem evt. www so, wie sie gerade noch in der Adresszeile des Browser gestanden ist, 1:1 utf-8 codiert gespeichert. Bei der Ausgabe einer Linkliste steht die Adresse dann sowieso nur als href-Ziel in einem Link. Den sichtbaren Verweisnamen am Bildschirm bildet der "Link-Name", den ich gemeinsam mit der URL speichere.

                Mit lieben Grüßen

                Melvin Cowznofski

                --

                Melvin Cowznofski
                What – me worry?
                1. Om nah hoo pez nyeetz, Melvin Cowznofski!

                  Aus meiner Sicht wäre folgende Tabelle sinnvoll
                  ID | KNr | KName | Parent | Linktext | Linkziel
                  In der Spalte Parent steht die KNr. der Elternkategorie bzw. 0, wenn es kein Elternteil gibt.
                  Ich sehe nicht, wo du da Redundanzen erzeugst.

                  Wenn ich bei Deinem Beispiel den Namen einer Kategorie verändern möchte, kann ich das nicht an einer einzigen Stelle tun, sondern muss es bei jedem Link-Datensatz, der dieser Kategorie zugeordnet ist, ändern. Das ist doch wohl ein exemplarischer Fall, der nach Normalisierung geradezu schreit.

                  Nein. Der Name einer ganz konkreten Kategorie steht nur ein einziges Mal in der Tabelle. „KNr“ habe ich nur hinzugefügt, falls du irgendwie ein eigenes Ordnungssystem haben möchtest. Die Zuordnung Kategorie → Unterkategorie erfolgt entweder über die KNr oder die ID, wenn sich die Zuordnung ändert, musst du auch nur für die Kategorien, die eine neue Elternkategorie bekommen sollen, aktiv werden.

                  Du kannst mit dieser Tabelle Bäume beliebiger Tiefe abbilden (1:n-Beziehung). Die Struktur wieder herauszulesen ist ein anderes Problem. Was allerdings nicht geht, ist eine Kategorie mehreren Elternkategorien zuzuordnen (m:n).

                  Matthias

                  --
                  Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Brie und Brieföffner.

                  1. Hallo Matthias!

                    Wenn Du die Links in die selbe Tabelle packst wie die Kategorien (was per se schon ein Unsinn wäre), dann stünde auch in jeder Zeile, nämlich für jeden Link der Name für kategorie und Unterkategorie. Und wenn nicht, wo in der Tabelle stehen dann die Namen ausgeschrieben?

                    Aber egal, meine Frage ist beantwortet.

                    Mit lieben Grüßen

                    Melvin Cowznofski

                    --

                    Melvin Cowznofski
                    What – me worry?
                    1. Om nah hoo pez nyeetz, Melvin Cowznofski!

                      Wenn Du die Links in die selbe Tabelle packst wie die Kategorien (was per se schon ein Unsinn wäre), dann stünde auch in jeder Zeile, nämlich für jeden Link der Name für kategorie und Unterkategorie. Und wenn nicht, wo in der Tabelle stehen dann die Namen ausgeschrieben?

                      Ich hatte hier eine wie auch immer geartete Navigation vor Augen. Wenn das natürlich was völlig anderes ist. passt meine vorgeschlagene Struktur möglicherweise nicht.

                      Matthias

                      --
                      Der Unterschied zwischen Java und JavaScript ist größer als der zwischen HAL und Halsbandarassari.

            2. Tach!

              Die Inhalte der ID sind belanglos.

              Was genau meinst Du bitte damit? Der "Inhalt der ID"? Verstehe ich Dich richtig, dass Du damit sagen willst, jede Kategorie braucht eine eindeutige ID und jede Unterkategorie braucht eine eindeutige ID, aber es muss nicht jede Zahl als ID in jeder Gruppe stehen? Analog dazu: Es gibt keine getrennten Kinokarten für Männer und Frauen, beide bekommen die selben, trotzdem hat jede einzelne Kinokarte als "unique ID" einen eindeutigen, nicht doppelt vorkommenden Sitzplatz. War es das, was Du mit dem "Die Inhalte der ID sind belanglos" sagen wolltest?

              Genau das meinte ich.

              Ich frage mich weiterhin, wieso das besser sein soll.

              Weniger Tabellen bedeuten auch weniger Wartungsaufwand.

              Es gibt dann also noch eine 3. Tabelle mit allen URLs
              Und die ist vermutlich auch unnötig. Die Links können ebenfalls mit in der einen Tabelle als weiteres Feld stehen.
              Also bei allem Respekt, das ist Nonsens. Ich fasse in Tabellen Daten zusammen, die auch in Sinn/Inhalt einen Zusammenhang haben.

              In deinem Fall ging ich davon aus, dass deine Daten am Ende eine Art Menü darstellen. Da wäre es nur begrenzt sinnvoll sie auseinanderzunehmen. Aber ich überlas "Es handelt sich um eine Link-Sammlung, also eine Lesezeichenverwaltung von URLs." Dann ist der Fall klar und anders (als gedacht). Dann gehören die URLs in eine eigene Tabelle, schon weil du nun 1:n oder m:n vorliegen hast.

              Ich lasse mir theoretisch ja noch einreden, dass das in meinem Fall bei Kategorien und Unterkategorien der Fall ist, weil beides thematische Beschreibungen für die URLs sind.

              Wenn du Kategorien so aufteilst, dann kommst du bei mehr Ebenen gleich mal auf (mindestens) drei Tabellen. Hauptkategorien mit Kindern, Unterkategorien mit Eltern und Kindern (in beliebiger Anzahl an Ebenen - oder noch extremer für jede Ebene eine Tabelle), und Endkategorien nur mit Eltern ohne Kinder. Wenn sich nicht gravierende Unterschiede in Daten, die noch zu einer Kategorie dieser "Arten" abgelegt werden sollen, ergeben, so ist das Ergebnis eigentlich nur eine unnötig hohe Zahl an Tabellen und eine ziemliche Verzettelung. Das wäre dann totnormalisiert.

              Nur verstehe ich Dein PHP nicht. Wo kommt das "$category['subcategories']" plötzlich her?

              Das korrespondiert mit dem Array, das ich vorgeschlagen habe. Ich verwende generell englische Bezeichner, da hat man es (bis auf Ausnahmen) leichter mit der Pluralbildung. Im Deutschen unterscheiden sich die Formen manchmal nicht. Und oftmals sind die Wörter auch kürzer.

              ID | ParentID | Name
              1  | 0        | Hauptkategorie
              2  | 1        | Unterkategorie
              3  | 0        | zweite Haupt
              4  | 0        | dritte Haupt
              5  | 3        | Unterkat zur zweiten

              Wenn diese Daten in einem Array landen, wie soll dann via PHP eine Liste mit den zusammengehörenden Unterkategorien als Unterliste entstehen, ohne dass auch hier für jede Zeile das komplette Array durchlaufen werden muss, wenn Kategorien und zusammengehörende Unterkategorien völlig durcheinender in der Tabelle gespeichert sind?

              Wie ich schon schrieb, erst sortieren, dann reicht einmal durchlaufen - jedenfalls bei zwei Ebenen. Wenn die Hauptkategorien (mit Parent-ID 0 oder NULL) zuerst ausgegeben werden, kannst du diese im vorderen Teil des Durchlaufs anlegen und hast dann die Basis für die jeweiligen Unterkategorien. Bei mehr als zwei Ebenen klappt das so nicht, da muss man tatsächlich mehrfach durchlaufen. Das ist ein einmaliger Aufwand, bei dem man schauen muss, ob sich das im späteren Verlauf durch Vereinfachung des Iterierens lohnt.

              Generell: Bei Deiner Tabelle ende ich mit einem mehrdimensionalen Array (Und ja, ich weiß, dass _Du_ das _nicht_ so nennst in PHP). Wenn ich das jetzt mit einem foreach($rows AS $row) durchlaufen lasse, habe ich pro Zeile 3 Informationen zur Verfügung: $row['id'], $row['ParentID'] und $row['Name']. Und wie soll da jetzt eine Liste entstehen, wo Kategorien und Unterkategorien logisch richtig angeordnet sind?

              Definiere "logisch richtig angeordnet"! Die Kategorien sind in der Reihenfolge, wie du sie sortiert ausgegeben hast, oder wie das DBMS denkt, wenn du nicht sortieren lässt (zweites Sortierkriterium nach dem 0-oder-nicht-0). Die Unterkategorien stehen bei der jeweiligen Hauptkategorie, weil beim Durchlaufen der Hauptkategorien zu jeder davon die Unterkategorien durchlaufen werden, bevor die nächste Hauptkategorie an die Reihe kommt.

              Das eine mag komplexer aussehen, hat aber den Vorteil, dass es beim Verarbeiten einfacher zugeht, weil die Beziehungen zueinander bereits da sind und nicht jedes Mal überprüft werden müssen.
              Also was da einfacher sein soll als meine 2. Lösung, sehe ich nicht.

              Du läufst einfach durch die jeweilige List der Unterkategorien anstatt durch alle Unterkategorien und dabei zu testen, ob sie dazugehört oder nicht. Du könntest sie auch mit einem einfachen count() auf das subcategories-Array zählen lassen. Bei deiner Variante müsste man da ebenfalls händisch durchlaufen und die nicht passenden aussortieren.

              Apropos "Code/Strukturen für Menschen lesbar zu schreiben/entwerfen, ist die hohe Kunst." und weil mir das immer wieder so auf der Zunge liegt: _Genau deshalb_ steigen mir bei der gängigen Art, wie viele Programmierer in PHP bei 'if' oder 'foreach' die geschweiften Klammern setzen, die Grausbirnen auf.

              Wenn du dir mal Python anschaust, wirst du sehen, dass Klammerei ziemlich überbewertet ist. Der Mensch braucht sie nicht, wenn der Code ordentlich eingerückt ist - und wer macht das nicht (und entwickelt ernsthaft Programme)? Und man kann Parser schreiben, die sie ebenfalls nicht brauchen, wie Python zeigt. Die Klammern kann man also als ein mehr oder weniger notwendiges Übel ansehen, das sich nun nicht mehr ausrotten lässt. (Noch "schlimmer" sind geschwätzigere Sprachen, die statt Klammern Wörter nehmen.)

              Schon bei einem Miniaturbeispiel ist doch
              auf den ersten Blick viel schneller und einfacher zu erfassen als würde man das Ganze als
              schreiben.

              Ansichtssache. Wenn ich Abstand für eine bessere Lesbarkeit hinzufüge, dann mach ich das bewusst. Zu viel Abstand, des Klammern-Prinzips wegen, erhöht auch nicht zwangsläufig die Lesbarkeit.

              Sobald mehr als 1 Klammer gleichzeitig offen ist, geht bei der herkömmlichen Schreibweise das Gefühl, was da jetzt womit zusammengehört und welcher Block zu welcher Bedingung gehört, verloren.

              Deswegen rückt man ja Blockinhalte der Lesbarkeit wegen auch ein, auch wenn es der Parser nicht braucht und man theoretisch auch die Klammern zählen könnte. Und wenn eine Funktion so groß wird, dass sie nicht mehr gut überblickbar ist, und man sie dann nicht in erfassbarere Häppchen aufteilt, macht man auch was falsch.

              Eine letzte Frage noch: In diesem speziellen Fall schreibe ich das Ganz nur für mich persönlich. Da ich nur URLs kompiere und in einem Formulatr einfüge, weiß ich ja, dass hier keine html Tags landen werden. Würdest Du da _trotzdem_ mit htmlspecialchars() arbeiten und wenn ja, wieso?

              Ja, weil es ohne nicht richtig ist. Oder kannst du garantieren, dass du niemals ein < im kopierten (Beschreibungs-)Text stehen hast? Es geht mir dabei nicht so sehr um Sicherheit, denn wenn du es nur für dich machst, ist mir diese egal. Aber schon ein einzelnes < kann die ganze Ausgabe ruinieren.

              dedlfix.

              1. Hallo dedlfix!

                Danke für die ausführliche Antwort!

                Wenn du Kategorien so aufteilst, dann kommst du bei mehr Ebenen gleich mal auf (mindestens) drei Tabellen. Hauptkategorien mit Kindern, Unterkategorien mit Eltern und Kindern (in beliebiger Anzahl an Ebenen - oder noch extremer für jede Ebene eine Tabelle), und Endkategorien nur mit Eltern ohne Kinder. Wenn sich nicht gravierende Unterschiede in Daten, die noch zu einer Kategorie dieser "Arten" abgelegt werden sollen, ergeben, so ist das Ergebnis eigentlich nur eine unnötig hohe Zahl an Tabellen und eine ziemliche Verzettelung. Das wäre dann totnormalisiert.

                Also in meinem speziellen Fall gibt es nie mehr als die 2 schon bekanntenen Ebenen. Ein Link gehört zu einer Unterkategorie und diese Unterkategorie gehört zu einer Kategorie. Eine weitere Verschachtelung bzw. Unterteilung kommt nicht in Frage für mich. Ist es dann noch immer eine "Totnormalisierung", wenn ich Kategorien und Unterkategorien in 2 seperate Tabellen stecke? Mit den Links habe ich dann _insgesamt_ genau 3 Tabellen. Das finde ich jetzt noich keine "Verzettelung". Siehst Du das anders?

                Nur verstehe ich Dein PHP nicht. Wo kommt das "$category['subcategories']" plötzlich her?
                Das korrespondiert mit dem Array, das ich vorgeschlagen habe.

                Aaaaahhhh, natürlich. Jetzt ist es mir klarer.

                Ich verwende generell englische Bezeichner, da hat man es (bis auf Ausnahmen) leichter mit der Pluralbildung. Im Deutschen unterscheiden sich die Formen manchmal nicht. Und oftmals sind die Wörter auch kürzer.

                Das finde ich eine sehr gute Idee! Ich werde das ab jetzt auch so handhaben! Danke für den Input!

                Definiere "logisch richtig angeordnet"!

                Ich meinte damit nur, dass die Unterkategorien zur jeweils dazugehörenden Hauptkategorie geschrieben werden bei der Ausgabe. Aber die Ausgabe aller Kategorien und Unterkategorien als Liste mit nur 1 Tabelle für die Kategorien ist ja zwischenzeitlich schon gelöst.

                Die Kategorien sind in der Reihenfolge, wie du sie sortiert ausgegeben hast, oder wie das DBMS denkt, wenn du nicht sortieren lässt (zweites Sortierkriterium nach dem 0-oder-nicht-0).

                Da kommt jetzt etwas dazu, was ich bis jetzt nicht geschrieben habe, weil es für die gepostete Fragenstellung irrelevant gewesen ist: Meine Linksammlung ist so programmiert, dass der Klick auf ein Lesezeichen zunächst immer auf die Ressource 'goto_link.php' verweist. Zum Beispiel in der Form 'http://goto_link.php?id=435'.

                Dann passiert Folgendes: Jede Kategorie, jede Unterkategorie und jeder Link hat in der Tabelle auch eine Spalte 'Aufrufe' (INT-Felder). Die Ressource 'goto_link.php' erhöht nun via PDO den aktuellen Wert von 'Aufrufe' in allen 3 Tabellen. Ich weiß somit genau, wie oft ich einen bstimmten Link aufgerufen habe und wie oft Links aus jeder Kategorie sowie jeder Unterkategorie aufgerufen worden sind. Somit kann ich mir bei der Ausgabe zB. aller Links der Unterkategorie 'Kulinarisches' die Seitenverweise nicht nur alphabetisch, sondern auch nach Häufigkeit des Aufrufs ausgeben lassen. Und danach erfolgt mittels header('Location: ... ') eine Weiterleitung zur entsprechenden Adresse.

                (Das war übrigens die Hauptmotivation, wieso alle meine Lesezeichen jetzt in Kürze in einer DB landen. Nicht nur, dass ich dann von jedem PC dieser Welt darauf zugreifen kann, mir geht bei geschätzten zig1000 Links die Sucherei oft sehr auf die Nerven. Und das Verwenden der 'Lesezeichen verwalten'-Funktion von Firefox ist da zwar etwas hilfreich, mir aber genauso unsympathisch.)

                Die Unterkategorien stehen bei der jeweiligen Hauptkategorie, weil beim Durchlaufen der Hauptkategorien zu jeder davon die Unterkategorien durchlaufen werden, bevor die nächste Hauptkategorie an die Reihe kommt.
                Du läufst einfach durch die jeweilige List der Unterkategorien anstatt durch alle Unterkategorien und dabei zu testen, ob sie dazugehört oder nicht.

                OK, ich habe mich damit jetzt lange auseinendergesetzt und das nach dem von Dir vorgeschlagenen Schema gelöst. Es funktioniert und nach 2 Tagen habe ich auch verstanden, wie Du das gemeint hast. Entstanden ist dabei allerdings eine neue Frage: Wenn ich die Kategorien so wie von Dir vorgeschlagen mit dem if($category['subcategories']) ausgebe, dann erhalte ich bei jeder Kategorie, die keine Unterkategorien besitzt, ein "Notice: Undefined index: subcategories in G:\XAMPP\htdocs\test\index.php on line...". Erst, wenn ich das ändere in if(isset($category['subcategories'])), kommen keine Notices mehr.

                Wieso ist das so? Und das nehme ich gleich zum Anlass, zu fragen, was genau der Unterschied ist, ob ich jetzt if($foo), if(empty($foo)) oder if(isset($foo)) schreibe?

                Schon bei einem Miniaturbeispiel ist doch
                auf den ersten Blick viel schneller und einfacher zu erfassen als würde man das Ganze als
                schreiben.
                Ansichtssache.

                Genau das ist es! Wie ich auch schon bei Matthias geantwortet habe: Ich denke, wir sind uns darüber einig, dass es jeder so handhaben soll, wie er den Codezusammenhang am leichtesten mental verarbeiten kann.

                Würdest Du da _trotzdem_ mit htmlspecialchars() arbeiten und wenn ja, wieso?
                Ja, weil es ohne nicht richtig ist.

                Das verstehe ich nicht so recht.

                Oder kannst du garantieren, dass du niemals ein < im kopierten (Beschreibungs-)Text stehen hast?

                Ja, das kann ich. Siehe auch der letzte Absatz meine Antwort an Matthias.

                Jetzt habe ich, wenn ich wie von Dir vorgeschlagen und zwischnzeitlich auch schon erfolgreich getestet, allerdings ein Problem: Nehmen wir an, die URL zur Seite "Blaue Frösche mit roten Augen" lautet www.example.com/funnyredfrogs und gehört in meiner DB zur Kategorie 'Natur' mit der ID '5' und der Natur-Unterkategorie 'Kurioses' mit der ID '29'. Dann würden ja bei Deiner Lösung die entsprechenden Teile meiner 2 Tabellen so aussehen:

                Tabelle Kategorien:

                ID | name     | parent_id
                -------------------------
                5  | Natur    | 0
                29 | Kurioses | 5

                Tabelle Links:

                ID  | name          | URL                           | Unterkategorie
                --------------------------------------------------------------------
                687 | Blaue Frösche | www.example.com/funnyredfrogs | 29

                Jetzt müsste ich ja theoretisch nicht auch noch die Hauptkategorie speichern weil ja durch die erste Tabelle hervorgeht, zu welcher Kategorie die Unterkategorie 29 gehört. Aber wie mache ich es dann, die Links _inclusive_ dem ausgeschriebenen Namen von Unterkategorie _und_ Kategorie auf den Bildschirm zu bekommen? Also eine Ausgabe wie:

                Kategorie | Unterkategorie | Link
                ------------------------------------------
                Natur     | Kurioses       | Blaue Frösche

                Ich überlege jetzt schon seit Stunden, wie ich da für meine DB-Interaktion das SELECT-Query formulieren soll, wenn Kategorie- und Unterkategorienamen in einer Tabelle stehen. Prinzipiell natürlich mit einem JOIN, damit ich zur Nummer aus Tabelle 2 auch einen Namen aus Tabelle 1 habe. ABer wie kommt dann noch der Name der Hauptkategorie dazu? Kannst Du mir da auf die Sprünge helfen bitte? (Heißt es eigentlich 'der', 'die' oder 'das' Query??)

                Mit lieben Grüßen

                Melvin Cowznofski

                --

                Melvin Cowznofski
                What – me worry?
                1. Tach!

                  Jede Kategorie, jede Unterkategorie und jeder Link hat in der Tabelle auch eine Spalte 'Aufrufe' (INT-Felder). Die Ressource 'goto_link.php' erhöht nun via PDO den aktuellen Wert von 'Aufrufe' in allen 3 Tabellen. Ich weiß somit genau, wie oft ich einen bstimmten Link aufgerufen habe und wie oft Links aus jeder Kategorie sowie jeder Unterkategorie aufgerufen worden sind.

                  Das ist auch kein Problem mit nur einer Kategorientabelle. Du kannst mit einem Statement beide Werte erhöhen. (Für den Link braucht es ein eigenes). UPDATE requests = requests + 1 FROM categories WHERE id IN (category, subcategory); (im IN natürlich die beiden entsprechenden IDs einfügen, und requests ist das, was bei dir Aufrufe heißt).

                  Wenn ich die Kategorien so wie von Dir vorgeschlagen mit dem if($category['subcategories']) ausgebe, dann erhalte ich bei jeder Kategorie, die keine Unterkategorien besitzt, ein "Notice: Undefined index: subcategories in G:\XAMPP\htdocs\test\index.php on line...".

                  Das kannst du auf zwei Arten lösen. Die eine hast du schon herausgefunden, die andere wäre, beim Anlegen der Hauptkategorie gleich ein leeres Subcategories-Array hinzuzufügen. Das bleibt eventuell später leer, ist aber generell vorhanden und wirft dan auch keine Notice.

                  Erst, wenn ich das ändere in if(isset($category['subcategories'])), kommen keine Notices mehr.
                  Wieso ist das so?

                  Es ist eben etwas nicht da und du versuchst lesend darauf zuzugreifen. Nicht selten ist das ein Fehler, wie zum Beispiel beim Variablennamen vertippt. Es ist aber immer eine gute Idee, alles was da sein muss irgendwo vor dem Gebrauch explizit zu definieren. (Das mache ich aber möglichst in der Nähe der Verwendungsstelle und nicht irgendwo generell am Script- oder Funktionsanfang. Wenn man das so unnötig verteilt hilft das nicht unbedingt der Übersichtlichkeit.)

                  Und das nehme ich gleich zum Anlass, zu fragen, was genau der Unterschied ist, ob ich jetzt if($foo), if(empty($foo)) oder if(isset($foo)) schreibe?

                  if prüft immer einen Ausdruck auf seinen booleschen Wert. Soweit ist das sicherlich klar. Bei if ($foo) ist es ein Variableninhalt. PHP weiß, dass if einen booleschen Wert haben will, also wird der Ausdruck ausgewertet und gegebenenfalls nach Boolean gecastet. Die Variable muss dazu gelesen werden. Bei PHP muss man die Variablen nicht explizit deklarieren, sondern sie werden beim ersten Schreibzugriff angelegt. Beim Lesen passiert das aber nicht. Da geht die PHP-Code-Ausführung lediglich die interne Liste der vorhandenen Variablen durch und wird nicht fündig. Ergebnis ist dann null und eine Notice. Das null wird nach boolean gecastet und ergibt false - das if ist zufrieden. empty() und isset() sind nun zwei Konstrukte (keine echten Funktionen), die dafür da sind, zu prüfen ob eine Variable oder ein Array-Element oder eine Objekteigenschaft vorhanden ist (und empty prüft auch noch auf „leer“). Sie geben deshalb keine Notice aus.

                  Würdest Du da _trotzdem_ mit htmlspecialchars() arbeiten und wenn ja, wieso?
                  Ja, weil es ohne nicht richtig ist.
                  Das verstehe ich nicht so recht.

                  Es ist nur dann nicht notwendig, wenn der Wert garantiert keine (in diesem Fall) HTML-spezifischen Zeichen enthält. Bei Inhalten aus einer anderen Quelle kann man das nicht wirklich garantieren. Im Prinzip geht das nur bei einem direkt im Code stehenden Wert.

                  Oder kannst du garantieren, dass du niemals ein < im kopierten (Beschreibungs-)Text stehen hast?
                  Ja, das kann ich.

                  Naja, das sagst du jetzt so, in deinem jugendlichen Leichtsinn ... Mach es lieber gleich richtig, als dich ständig an dein Garantieversprechen erinnern zu müssen, selbst wenn es überflüssig erscheint. Es ist nur ein Wort und zwei Klammern. (Na gut, gegebenenfalls noch ein paar Zeichen mehr, wenn es vorher ein "String mit $variable drin war", weil der jetzt aufgeteilt werden muss.)

                  Jetzt müsste ich ja theoretisch nicht auch noch die Hauptkategorie speichern weil ja durch die erste Tabelle hervorgeht, zu welcher Kategorie die Unterkategorie 29 gehört. Aber wie mache ich es dann, die Links _inclusive_ dem ausgeschriebenen Namen von Unterkategorie _und_ Kategorie auf den Bildschirm zu bekommen? [...] Prinzipiell natürlich mit einem JOIN, damit ich zur Nummer aus Tabelle 2 auch einen Namen aus Tabelle 1 habe. ABer wie kommt dann noch der Name der Hauptkategorie dazu? Kannst Du mir da auf die Sprünge helfen bitte? (Heißt es eigentlich 'der', 'die' oder 'das' Query??)

                  Join ist richtig, genauer ein Self-Join, wenn es nur noch eine Kategorien-Tabelle ist. Die (sagt mein Sprachgefühl (wegen „Abfrage“)) Query sieht da ungefähr so aus:

                    
                  SELECT c.id category_id, c.name category_name, s.id subcategory_id, s.name subcategory_name  
                  FROM categories s  
                  JOIN categories c ON c.id = s.parent_id  
                  WHERE s.id = ?  
                  
                  

                  Da sind zunächst gewählt die beiden Kategorienamen nebst deren IDs, die du sicher auch benötigen wirst. Dann hab ich die s ins FROM genommen, weil das die Tabelle ist, die du zuerst befragen musst (wenn es zwei verschiedene wären) und von da aus dann über die parent_id die „andere“ Tabelle erreichen kannst. Du kannst die in dem Fall auch andersrum joinen mit demselben Ergebnis. Und das WHERE sollte klar sein.

                  dedlfix.

                  1. Hallo dedlfix!

                    Join ist richtig, genauer ein Self-Join, wenn es nur noch eine Kategorien-Tabelle ist. Die (sagt mein Sprachgefühl (wegen „Abfrage“)) Query sieht da ungefähr so aus:

                    SELECT c.id category_id, c.name category_name, s.id subcategory_id, s.name subcategory_name
                    FROM categories s
                    JOIN categories c ON c.id = s.parent_id
                    WHERE s.id = ?

                      
                    Ich werfe das Handtuch! Ich bin gestern und heute jetzt sicher schon mehr als 5 Stunden vor dem PC gesessen und habe versucht, eine funktionierende QUERY zu erstellen. Was immer ich auch tue ... es liefert entweder eine Fehlermeldung oder eine Ausgabe mit null Resultaten. Ich bin am Ende mit meinem Latein und hab in der Zwischenzeit ehrlich gesagt auch keinen Freude mehr daran.  
                      
                      
                      
                    Das ist die Ausgangslage:  
                    =========================  
                      
                      
                    Auszug aus der Tabelle "categories":  
                      
                    id | name     | parent\_id  
                    \-------------------------  
                    1  | Politik  | 0  
                    3  | Parteien | 1  
                      
                    Auszug aus der Tabelle "links":  
                      
                    id | category\_id | name          | url  
                    \-------------------------------------------------------------------  
                    12 | 3           | Zwergenpartei | http://example.com/zwergenpartei  
                      
                      
                      
                    Das ist das Ziel / so soll die Ausgabe sein:  
                    ============================================  
                      
                      
                    Kategorie | Unterkategorie | Name          | URL  
                    \-----------------------------------------------------------------------------  
                    Politik   | Parteien       | Zwergenpartei | http://example.com/zwergenpartei  
                      
                      
                      
                    Meine bisherigen Schritte:  
                    ==========================  
                      
                      
                    Versuch 1 ~ Die Ausgabe der Links ohne Bezug zu einer Kategorie:  
                      
                    ~~~sql
                    SELECT l.name AS Name, l.url AS URL  
                    FROM links AS l  
                    ORDER BY l.id ASC
                    

                    Das funktioniert natürlich und liefert das gewünschte Ergebnis.

                    Versuch 2 ~ Die Ausgabe der Links incl. der dazugehörenden Kategorie ID:

                    SELECT l.name AS Name, l.url AS URL, l.category_id AS Kategorie_ID  
                    FROM links AS l  
                    ORDER BY l.id ASC  
                    
                    

                    Noch immer kein Join, noch immer kein Problem.

                    Versuch 3 ~ Die Ausgabe der Links samt dem Unterkategorienamen:

                    SELECT l.name AS Name, l.url AS URL, c.name AS Unterkategorie  
                    FROM links AS l  
                    INNER JOIN categories AS c ON c.id = l.category_id  
                    ORDER BY l.id ASC
                    

                    Das funktioniert auch noch und liefert das gewünschte Ergebnis.

                    Aber an diesem Punkt stehe ich jetzt vor einer Wand. Wie erweitere ich die Query jetzt in Folge, um neben der Unterkategorie auch die Kategorie stehen zu haben? Mir ist klar, dass irgendwo im JOIN jetzt die Spalte 'parent_id' auftauchen muss. Nur wo und wie??

                    Nachdem der Name der Kategorie ja aus der selben Spalte kommt wie der Name der Unterkategorie, muss der Anfang sohl so lauten:

                    SELECT l.name AS Name, l.url AS URL, c.name AS Unterkategorie, c2.name AS Kategorie  
                    FROM links AS l  
                    INNER JOIN categories AS c ON c.id = l.category_id  
                    INNER JOIN categories AS c1  
                    ORDER BY l.id ASC
                    

                    So ungefähr, oder? Wenn ich das mit den Joins richtig verstanden habe, dann muss ich beim Alisnamen für den 2. Join ja einen anderen nehmen als für den ersten. Deshalb ist die Tabelle 'categories' im ersten Join das "c" und im zweiten Join das "c1".

                    Und jetzt bin ich mit meinem Latein am Ende. Egal, was ich jetzt womit auch kombiniere - die Ausgabe ist entweder leer oder es kommt eine Fehlrmeldung. Deshalb ist "Wie geht das jetzt" leider schon die spezifischste Fragenstellung, die ich momentan anbieten kann. =/

                    Mit lieben Grüßen

                    Melvin Cowznofski

                    --

                    Melvin Cowznofski
                    What – me worry?
                    1. Und jetzt bin ich mit meinem Latein am Ende. Egal, was ich jetzt womit auch kombiniere - die Ausgabe ist entweder leer oder es kommt eine Fehlrmeldung. Deshalb ist "Wie geht das jetzt" leider schon die spezifischste Fragenstellung, die ich momentan anbieten kann. =/

                      SELECT l.name AS Name, l.url AS URL, c.name AS Unterkategorie, c2.name AS Kategorie  
                      FROM links AS l  
                      INNER JOIN categories AS c ON c.id = l.category_id  
                      INNER JOIN categories AS c1 ON c.parent_id = c2.id /* hier ergänzt */  
                      ORDER BY l.id ASC
                      
                      1. Hallo tinita!

                        SELECT l.name AS Name, l.url AS URL, c.name AS Unterkategorie, c2.name AS Kategorie

                        FROM links AS l
                        INNER JOIN categories AS c ON c.id = l.category_id
                        INNER JOIN categories AS c1 ON c.parent_id = c2.id /* hier ergänzt */
                        ORDER BY l.id ASC

                          
                        Danke für Deine Antwort! Also so funktioniert es nicht, weil c2 gibt es ja gar nicht. (Das ist aber meine Schuld, weil ich sehe grade, ich habe das in meinem letzten Posting schon falsch geschrieben.) Ändert man aber hier die c2 in c1 um, dann ergibt das  
                          
                        ~~~sql
                        SELECT l.name AS Name, l.url AS URL, c.name AS Unterkategorie, c1.name AS Kategorie  
                        FROM links AS l  
                        INNER JOIN categories AS c ON c.id = l.category_id  
                        INNER JOIN categories AS c1 ON c.parent_id = c1.id  
                        ORDER BY l.id ASC
                        

                        Und dies liefert nun das exakt gewünschte Ergebnis. Ich tue mir momentan nur noch etwas schwer, die Joins "einzudeutschen".

                        Die erste Join-Anweisung sagt: "Zusätzlich zu der in der FROM-Anweisung stehenden Tabelle 'links' nimm jetzt auch noch die Tabelle 'categories' dazu.". In der SELECT-Anweisung steht, dass aus dieser Tabelle die Spalte 'name' verwendet werden soll. Und die ON-Anweisung des ersten JOINS sagt:"Nimm für 'name' jene Zeile, in der die ID der Zahl im 'category_id' Feld der Tabelle 'links' entspricht.

                        Die zweite Join-Anweisung sagt: "Zusätzlich zu der in der FROM-Anweisung stehenden Tabelle 'links' nimm jetzt für einen witeren Wert _nochmal_ die Tabelle 'categories' dazu.". In der SELECT-Anweisung steht, dass aus dieser Tabelle _wieder_ die Spalte 'name' verwendet werden soll. Und die ON-Anweisung des zweiten JOINS sagt:"Nimm für 'name' jene Zeile, in der die ID der Zahl im 'parent_id' Feld der Tabelle 'categories' entspricht.

                        Ist das so richtig? Etwas verwirrend für mich, aber ich denke, langsam blicke ich durch.

                        Mit lieben Grüßen

                        Melvin Cowznofski

                        --

                        Melvin Cowznofski
                        What – me worry?
                        1. Tach!

                          Die erste Join-Anweisung sagt: "Zusätzlich zu der in der FROM-Anweisung stehenden Tabelle 'links' nimm jetzt auch noch die Tabelle 'categories' dazu.". In der SELECT-Anweisung steht, dass aus dieser Tabelle die Spalte 'name' verwendet werden soll. Und die ON-Anweisung des ersten JOINS sagt:"Nimm für 'name' jene Zeile, in der die ID der Zahl im 'category_id' Feld der Tabelle 'links' entspricht.

                          Die zweite Join-Anweisung sagt: "Zusätzlich zu der in der FROM-Anweisung stehenden Tabelle 'links' nimm jetzt für einen witeren Wert _nochmal_ die Tabelle 'categories' dazu.". In der SELECT-Anweisung steht, dass aus dieser Tabelle _wieder_ die Spalte 'name' verwendet werden soll. Und die ON-Anweisung des zweiten JOINS sagt:"Nimm für 'name' jene Zeile, in der die ID der Zahl im 'parent_id' Feld der Tabelle 'categories' entspricht.

                          Die Theorie geht etwas anders. (In der Praxis mag der Optimizer anders entscheiden, aber das ist egal.) Das SELECT steht zwar als erstes, wird jedoch erst später ausgewertet. Erst kommt FROM (mit JOINS), dann WHERE, dann GROUP BY, jetzt das SELECT eingeschoben, weiter mit HAVING, ORDER BY und LIMIT.

                          Nimm die Tabelle aus FROM, dazu nimm die JOINs *). Von diesen Mengen prüfe, was über die Join-Bedingung zusammenpasst. Diese eingeschränkte (wenn man kein kartesisches Produkt erzeugt hat) Menge filtere mit der WHERE-Bedingung. (GROUP BY lass ich weg.) Berechne nun die Angaben in der SELECT-Klausel für die bis jetzt entstandene Menge. (HAVING wieder ausgelassen.) Sortiere die Ergebnismenge und nimm dann nur noch die Menge gemäß LIMIT.

                          Im Klartext für deinen Fall: Nimm die Links, dazu die Kategorien. Verknüpfe alle Datensätze über l.category_id und c.id, wenn es entsprechende Datensätze gibt. Dann verknüpfe noch c1 über c.parent_id und c1.id mit der aus der ersten Verknüpfung entstandenen Menge. Die aktuelle Ergebnismenge hat nun für jeden Link-Datensatz einen verknüpften (oder weniger, wenn eine Kategorie oder Elternkategorie fehlen würde). Dann WHERE kommt und so weiter.

                          *) Implizite Joins machen keine Ausnahme (also die: FROM t1, t2 WHERE t1.x = t2.y).

                          dedlfix.

                          1. Hallo dedlfix!

                            Ich sehe meine Frage, ob es einen besseren Weg als den meinen für das von mir gewünschte Ergebnbis gibt, als beantwortet.

                            Danke Dir und den anderen für die Antworten und Hilfesetllung!

                            Mit lieben Grüßen

                            Melvin Cowznofski

                            --

                            Melvin Cowznofski
                            What – me worry?
                    2. Tach!

                      Nachdem der Name der Kategorie ja aus der selben Spalte kommt wie der Name der Unterkategorie, muss der Anfang sohl so lauten:

                      SELECT l.name AS Name, l.url AS URL, c.name AS Unterkategorie, c2.name AS Kategorie

                      FROM links AS l
                      INNER JOIN categories AS c ON c.id = l.category_id
                      INNER JOIN categories AS c1
                      ORDER BY l.id ASC

                      
                      >   
                      > So ungefähr, oder?  
                        
                      Da fehlt die zweite Join-Bedingung. Ohne diese hast du sozusagen zwei Inseln. Von diesen beiden Inseln/(Teil)mengen wird nun jeder mit jedem verbunden, was ein kartesisches Produkt ergibt (à la 3 × 4 = 12). Ein JOIN ohne ON ist meist nicht das, was man will, also ON c1.id = c.parent\_id. Und das c2 im Select muss c1 heißen (oder das c1-Alias c2).  
                        
                      
                      > Und jetzt bin ich mit meinem Latein am Ende. Egal, was ich jetzt womit auch kombiniere - die Ausgabe ist entweder leer oder es kommt eine Fehlrmeldung. Deshalb ist "Wie geht das jetzt" leider schon die spezifischste Fragenstellung, die ich momentan anbieten kann. =/  
                        
                      Wortlaute von Fehlermeldungen sind auch immer hilfreich beim Helfen.  
                        
                        
                      dedlfix.
                      
                      1. Hallo dedlfix!

                        Da fehlt die zweite Join-Bedingung.

                        Das ist mir schon klar, ich hatte ja auch geschrieben, so müsste es meiner Meinung nach "anfangen".

                        Wortlaute von Fehlermeldungen sind auch immer hilfreich beim Helfen.

                        Ich weiß weder, was ich jetzt fragen soll, noch was ich jetzt tun soll.

                        dedlfix.

                        Mit lieben Grüßen

                        Melvin Cowznofski

                        --

                        Melvin Cowznofski
                        What – me worry?
                        1. Tach!

                          Wortlaute von Fehlermeldungen sind auch immer hilfreich beim Helfen.
                          Ich weiß weder, was ich jetzt fragen soll, noch was ich jetzt tun soll.

                          Das erste wäre, die ON-Klausel zu ergänzen. Wenn das nichts hilft, zeig das Statement und die dazu erzeugte Fehlermeldung.

                          dedlfix.

                          1. Hallo dedlfix!

                            Das erste wäre, die ON-Klausel zu ergänzen. Wenn das nichts hilft, zeig das Statement und die dazu erzeugte Fehlermeldung.

                            Habe zwischenzeitlich gesehen, dass tinita in seiner/ihrer Antwort schon die Lösung gepostet hat. Wie ich auch schon dort geantwortet habe, verstehe ich diese so _halbwegs_, versuche aber noch, mir da auszudeutschen, was die Query genau von der DB will.

                            Mit lieben Grüßen

                            Melvin Cowznofski

                            --

                            Melvin Cowznofski
                            What – me worry?
            3. Da ich nur URLs kompiere und in einem Formulatr einfüge, weiß ich ja, dass hier keine html Tags landen werden.

              famous last words ;-)

              ich benutze bei einem Template-System z.b. automatisches escaping.
              lieber einmal zuviel escapen und nur ein harmloser bug als einmal zu wenig escapen und gleich eine sicherheitslücke.
              wer sagt, dass du nicht irgendwann URLs reinpackst, die nicht von dir kommen?

    2. Om nah hoo pez nyeetz, Melvin Cowznofski!

      echo "<ul>";

      foreach($rows_kategorien AS $kategorie)
            {
               echo "<li>".$kategorie['name']."</li>";
               foreach($rows_unterkategorien AS $unterkategorie)
                  {
                     if($unterkategorie['category'] == $kategorie['id'])
                        {
                           echo "<ul>";
                           echo "<li>".$unterkategorie['name']."</li>";
                           echo "</ul>";
                        }
                  }
            }
      echo "</ul>";

        
      keine Verbesserung am Code, aber Verzicht auf echos.  
        
      ~~~html
      <ul>  
        <?php foreach($rows_kategorien AS $kategorie) : ?>  
          <li><?=$kategorie['name']?></li>  
          <?php foreach($rows_unterkategorien AS $unterkategorie) :  
              if($unterkategorie['category'] == $kategorie['id']) :?>  
                  <ul><li><?=[$unterkategorie['name']?></li></ul>  
              <?php endif;  
            endforeach;  
          endforeach;?>  
      </ul>
      

      Allerdings steckst du jede Unterkategorie in eine eigene Liste. Das ist sicher nicht notwendig.

      Matthias

      --
      Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Fries und Friese.

      1. Tach!

        keine Verbesserung am Code, aber Verzicht auf echos.

        Allerdings darf man nicht auf htmlspecialchars() verzichten, in keiner von beiden Schreibweisen.

        dedlfix.

      2. Hallo Matthias!

        Allerdings steckst du jede Unterkategorie in eine eigene Liste. Das ist sicher nicht notwendig.

        Wieso "jede" Unterkategorie in eine "eigene" Liste? Ich erstelle eine Liste, in der jede Kategorie ein Listenpunkt ist. Und wenn eine Kategorie eine oder mehrere Unterkategorien besitzt, dann werden diese ebenfalls als _eine_ Liste ausgegeben. Anders ausgedrückt: Alle Unterkategorien einer Kategorie _zusammen_ bilden _eine_ Liste. (Siehe die "Grafik" dazu in meinem Ursprungsposting.)

        Mit lieben Grüßen

        Melvin Cowznofski

        --

        Melvin Cowznofski
        What – me worry?
        1. Om nah hoo pez nyeetz, Melvin Cowznofski!

          Wieso "jede" Unterkategorie in eine "eigene" Liste? Ich erstelle eine Liste, in der jede Kategorie ein Listenpunkt ist. Und wenn eine Kategorie eine oder mehrere Unterkategorien besitzt, dann werden diese ebenfalls als _eine_ Liste ausgegeben. Anders ausgedrückt: Alle Unterkategorien einer Kategorie _zusammen_ bilden _eine_ Liste. (Siehe die "Grafik" dazu in meinem Ursprungsposting.)

          Das stimmt nur für deine erste Idee.

          Matthias

          --
          Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Staub und Staubecken.