Peter Nack: suchergebnisse, detailseite und blaetterfunktion

Hallo,

ich habe eine Frage zum konzeptionellen Aufbau fuer das Durchblaettern von Datensaetzen.

Angenommen ich habe eine Suchseite, auf der Fahrzeuge angezeigt werden (abhaengig von den Filterkriterien, die der Benutzer gesetzt hat). Durch einen Link neben jedem Datensatz gelangt man zu dessen Detailseite. Jede Detailseite hat eine feste Adresse (also example.org/fahrzeuge/firma/typ/model/name).
Alles in allem also eine recht normale Konfiguration.

Wie kann ich jetzt auf den Detailseiten eine Vorheriger-Naechster-Navigation implementieren - unter Beruecksichtigung der vom Benutzer eingestellten Filter?

Die Suchkriterien werden in einem Search-Objekt gehalten. Dieses kann ich also gut in der Session hinterlegen. Die Search-Klasse beinhaltet auch die aktuellen Offset- und Limit-Angaben. Anhand dessen limitiert die SQL-Suche die Ausgabe - mittels SQL_CALC_FOUND_ROWS ermittle ich die eigentliche Anzahl aller gefundener Datensaetze.

Mir schweben einige (nicht ganz zuende gedachte) Varianten im Kopf, aber wuerde mal gerne ein paar Ratschlaege von anderen hierzu hoeren.

Besten Dank im Voraus
Peter

  1. Hi!

    Wie kann ich jetzt auf den Detailseiten eine Vorheriger-Naechster-Navigation implementieren - unter Beruecksichtigung der vom Benutzer eingestellten Filter?

    Wie du schon richtig erkannt hast, musst du die Kriterien irgendwie zwischen den Requests aufheben. Dazu gibt es die bekannten Methoden Requestparameter (Path/Querystring/POST) oder eine Session (und Cookies, aber die halte ich nicht für besonders geeignet).

    Der Vorteil an Path- und Querystring-Parametern ist, dass man Requests bookmarken kann. Alle anderen Methoden übertragen oder speichern ihre Daten unsichtbar, liefern damit bei augenscheinlich gleichen Requests (besser gesagt URLs) unterschiedliche Daten.

    Mir schweben einige (nicht ganz zuende gedachte) Varianten im Kopf, aber wuerde mal gerne ein paar Ratschlaege von anderen hierzu hoeren.

    Nenn mal deine Varianten. In welcher Richtung hast du Bedenken, generelles Konzept oder Umsetzung?

    Lo!

    1. Hallo dedlfix,

      danke fuer deine Antwort.

      Wie du schon richtig erkannt hast, musst du die Kriterien irgendwie zwischen den Requests aufheben. Dazu gibt es die bekannten Methoden Requestparameter (Path/Querystring/POST) oder eine Session (und Cookies, aber die halte ich nicht für besonders geeignet).

      Also derzeit ist es wie folgt:

      • ohne Javascript -
        Die Filterform wird via GET abgesetzt. Das heisst, dass Blaettern auf der Ergebnisseite (also ich meine jetzt nicht die von mir erwuenschte Blaetterunktion, sondern lediglich das Durchstoebern der Suchergebnisse) habe ich so realisiert, dass $_GET['page'] in- bzw. dekrementiert wird und bei einem neuen Request uebergeben wird.

      • Mit JavaScript -
        Beim Klick auf die naechste Ergebnisseite lade ich lediglich den Container mit den Ergebnissen neu. Es findet also kein neuer Request der kompletten Seite statt.

      Der Vorteil an Path- und Querystring-Parametern ist, dass man Requests bookmarken kann.

      Daher sende ich das Filterformular - wie oben bereits erwaehnt - ueber GET ab. Folglich kann man eine Suche bookmarken.
      Da eine Detailseite stets eine eindeutige URL besitzt, kann auch diese gebookmarked werden.

      Was nicht moeglich ist - und meiner Meinung nach auch nicht Ziel sein sollte - ist das Bookmarken einer Detailseite inklusive der - moeglicherweise(!) - zuvor abgesetzten Suche.

      Das heisst also, die Blaetterfunktion auf den Detailseiten wird somit nur dann eingeblendet, wenn zuvor eine Suche abgesetzt wurde.

      Nenn mal deine Varianten. In welcher Richtung hast du Bedenken, generelles Konzept oder Umsetzung?

      1. Benutzer setzt Filterkriterien
      2. Filterform wird via GET abgesetzt
      3. Suchkriterien werden in der Session hinterlegt
      3. Auf gleicher Seite werden gefundene Resultate aufgelistet
      4. Benutzer kann dort blaettern (siehe oben bezgl. Page-Parameter)
      5. Benutzer klickt auf Detail
      6. Detailseite oeffnet sich im selben Fenster
      7. Detailseite schaut, ob $_SESSION['filter_criteria'] existiert
      8. Wenn, dann Links zum Vor- und zurueckblaettern einbinden

      So, und genau zwischen 7 und 8 liegt mein derzeitiges Fragezeichen.
      Woher weiss ich an dieser Stelle, welches Farhzeug als naechstes/vorheriges dran ist?
      Es muesste ja eine Art Zeiger existieren, der mir die aktuelle Position des Fahrzeuges in der Fahrzeug-Suchergebnisliste verraet. Anhand des Zeigers koennte ich dann ja das SQL fuer die Suche mit entsprechenden Limit-Offset-Angaben praeparieren (also so dass lediglich ein einziger Datensatz zurueckgegeben wird).

      Was mir daran allerdings ein wenig aufstoeszt, ist, dass ich somit auf der Detailseite zwei Mal die Suche absetzen muss (einmal um den vorherigen Datensatz zu ermitteln, und das zweite Mal fuer den naechsten Datensatz). Da die Suche recht komplex ist, habe ich hier ein paar designtechnische Bedenken.

      MfG
      Peter

      1. Hi!

        Also derzeit ist es wie folgt:

        • ohne Javascript -
        • Mit JavaScript -

        Mit und ohne Javascript ist im Grunde egal. Beim Webserver kommt in jedem Fall nur ein Request an, und der muss in beiden Fällen seine Parameter in ähnlicher Weise übergeben.

        Da eine Detailseite stets eine eindeutige URL besitzt, kann auch diese gebookmarked werden.
        Was nicht moeglich ist - und meiner Meinung nach auch nicht Ziel sein sollte - ist das Bookmarken einer Detailseite inklusive der - moeglicherweise(!) - zuvor abgesetzten Suche.

        Doch, warum nicht? Du hast die URL der Detailseite. In der ist alle relevante Information für die Haupt-Funktionalität im Pfadteil untergebracht. Man kann da auch ganz gut noch im Such-Fall einen Querystring mit den Suchparametern anhängen.

        Das heisst also, die Blaetterfunktion auf den Detailseiten wird somit nur dann eingeblendet, wenn zuvor eine Suche abgesetzt wurde.

        Eben. Und wenn man es wie eben vogeschlagen umsetzt: ohne Querystring: "einfache" Detailseite - mit Querystring: Detailseite mit Blätterelementen.

        1. Detailseite schaut, ob $_SESSION['filter_criteria'] existiert
        2. Wenn, dann Links zum Vor- und zurueckblaettern einbinden
          So, und genau zwischen 7 und 8 liegt mein derzeitiges Fragezeichen.
          Woher weiss ich an dieser Stelle, welches Farhzeug als naechstes/vorheriges dran ist?

        Eine sortierte Menge hast du ja vorliegen, beziehungsweise weißt, wie du sie für die erste Liste erstellen musst. Der Datensatz davor ist der mit dem größten Wert im Sortierkriterium, der kleiner als der vom aktuellen Item (Fahrzeug) ist. Der danach ist dann der kleinste von denen, die größer sind. Wenn die Werte gleich sind, hat man sowieso eine undefinierte Reihenfolge und darf sich nicht über Unregelmäßigkeiten beim Blättern wundern. Da muss man dann das Sortierkriterium um etwas "vereindeutigendes" erweitern (und wenn man dafür die ID missbraucht, wenn sich nichts besseres finden lässt).

        Was mir daran allerdings ein wenig aufstoeszt, ist, dass ich somit auf der Detailseite zwei Mal die Suche absetzen muss (einmal um den vorherigen Datensatz zu ermitteln, und das zweite Mal fuer den naechsten Datensatz). Da die Suche recht komplex ist, habe ich hier ein paar designtechnische Bedenken.

        Indexe helfen bei Suchaktionen in vielen Anwendungsfällen, wenn man sie gesetzt hat. Ob sie verwendet werden, kann man unter MySQL mit EXPLAIN ermitteln. Andere Systeme bieten meist auch solche Query-Analyse-Tools. Um zwei Abfragen wirst du nicht umhin kommen, aber du kannst sie mit UNION zu einem Request zusammenfassen.

        Lo!

        1. Hi Dedlfix,

          Da eine Detailseite stets eine eindeutige URL besitzt, kann auch diese gebookmarked werden.
          Was nicht moeglich ist [..]
          Doch, warum nicht? Du hast die URL der Detailseite. In der ist alle relevante Information für die Haupt-Funktionalität im Pfadteil untergebracht. Man kann da auch ganz gut noch im Such-Fall einen Querystring mit den Suchparametern anhängen.

          Die Variante kam mir auch schon in den Sinn. Allerdings habe ich sie wieder verworfen.
          Zum einen aus rein subjektivem Befinden (irgendwie moechte ich in einem Bookmark nicht gerne einen "Zustand" hinterlegen).
          Zum anderen geht es aus technischer Sicht nicht ("geht nicht" gibt's nicht, aber es waere unsinnig), da jede Detailseite in sich noch mal in einige Unterseiten unterteilt ist, von denen einige eigene Formulare besitzen, welche auch mittels GET ueberliefert werden.

          Eben. Und wenn man es wie eben vogeschlagen umsetzt: ohne Querystring: "einfache" Detailseite - mit Querystring: Detailseite mit Blätterelementen.

          In meinem Fall dann: ohne Session: "einfache" Detailseite - mit Session: Detailseite mit Blaetterfunktion.

          Eine sortierte Menge hast du ja vorliegen, beziehungsweise weißt, wie du sie für die erste Liste erstellen musst.

          Stimmt.

          Der Datensatz davor ist der mit dem größten Wert im Sortierkriterium, der kleiner als der vom aktuellen Item (Fahrzeug) ist. Der danach ist dann der kleinste von denen, die größer sind.

          Genau. Aber genau hier habe ich ja das Verstaendnisproblem... ahh.. gerade ist es mir ein wenig klarer geworden.. ich werde mir das jetzt noch mal ganz genau durch den Kopf gehen lassen bevor ich nur halb zu Ende gedachte Fragen stelle.

          Indexe helfen bei Suchaktionen in vielen Anwendungsfällen, wenn man sie gesetzt hat.

          Jep.

          Um zwei Abfragen wirst du nicht umhin kommen, aber du kannst sie mit UNION zu einem Request zusammenfassen.

          Ah, Stichwort UNION.

          OK. Jetzt werde ich mir das nochmal naeher anschauen und bei Zeiten eine Rueckmeldung geben.
          Besten Dank nochmal!

          MfG
          Peter

          PS: Manchmal ist es schon komisch: Man gruebelt und gruebelt ueber ein Problem nach, und wenn man sich dann endlich mal aufgerafft hat sein Problem im Forum zu schildern, so kommt man der Loesung dann meisztens ganz von selbst Schritt fuer Schritt naeher ;)

          1. Hi!

            Doch, warum nicht? Du hast die URL der Detailseite. In der ist alle relevante Information für die Haupt-Funktionalität im Pfadteil untergebracht. Man kann da auch ganz gut noch im Such-Fall einen Querystring mit den Suchparametern anhängen.
            Die Variante kam mir auch schon in den Sinn. Allerdings habe ich sie wieder verworfen.
            Zum einen aus rein subjektivem Befinden (irgendwie moechte ich in einem Bookmark nicht gerne einen "Zustand" hinterlegen).

            Nunja, einen Zustand sehe ich darin nicht. Es sind Parameter, um eine bestimmte Suche auszuführen / zu wiederholen. Zustandsinformationen braucht man da erst einmal nicht. Wenn du hingegen noch weitere Daten in deinem Suchobjekt abgelegt hast, die mehr in Richtung Zustand gehen, ...

            Zum anderen geht es aus technischer Sicht nicht ("geht nicht" gibt's nicht, aber es waere unsinnig), da jede Detailseite in sich noch mal in einige Unterseiten unterteilt ist, von denen einige eigene Formulare besitzen, welche auch mittels GET ueberliefert werden.

            Ist auch ein Argument. Aber durch deine GET-Formulare wird doch sowieso der Querystring des Dokuments verworfen. Er ist dafür nicht mehr im Weg - aber auch nicht mehr vorhanden, wenn du die Suchparameter weiterhin benötigst.

            ich werde mir das jetzt noch mal ganz genau durch den Kopf gehen lassen bevor ich nur halb zu Ende gedachte Fragen stelle.

            Das macht doch nichts. Vielleicht ist ja in dieser Hälfte schon ein Irrweg zu erkennen oder ein Klärungsbedarf entstanden, um erfolgreich zum Ende zu gelangen.

            PS: Manchmal ist es schon komisch: Man gruebelt und gruebelt ueber ein Problem nach, und wenn man sich dann endlich mal aufgerafft hat sein Problem im Forum zu schildern, so kommt man der Loesung dann meisztens ganz von selbst Schritt fuer Schritt naeher ;)

            Manchmal hilft es schon, wenn man das Problem konkret in Worte fassen muss. Mitunter stelle ich mir bei eigenen Problemen vor, ich müsste sie jemandem erklären und versuche dafür gedanklich eine Formulierung zu finden. Hilft nicht immer, aber ich finde dabei durchaus auch Lücken in der eigenen Argumentation.

            Lo!

            1. Hallo Dedlfix,

              Das macht doch nichts. Vielleicht ist ja in dieser Hälfte schon ein Irrweg zu erkennen oder ein Klärungsbedarf entstanden, um erfolgreich zum Ende zu gelangen.

              Ich konnte nun bereits ein Teilergebnis erzielen.
              Ausgehend davon, dass die Ergebnisse zB nach ORDER BY Baujahr ASC, id DESC sortiert werden. Bei numerischen Werten klappt meine Vorgehensweise*.

              Allerdings weiss ich nicht, wie ich vorgehen muss, wenn die Ergebnisse nach Strings sortiert wurden, zb ORDER BY name ASC, id DESC.
              Denn in dem Query zum Selektieren des vorherigen bzw. naechsten Fahrzeuges kann ich ja schlecht sagen, dass der Name _groeszer_ (bzw. _kleiner_) sein soll, als der aktuelle (also der Name des Fahrzeuges auf der Detailseite).
              Wie habe ich hier korrekt vorzugehen?

              Danke & MfG
              Peter

              * Siehe Posting von dir weiter oben

              Der Datensatz davor ist der mit dem größten Wert im Sortierkriterium, der kleiner als der vom aktuellen Item (Fahrzeug) ist. Der danach ist dann der kleinste von denen, die größer sind.

              1. Hi!

                Allerdings weiss ich nicht, wie ich vorgehen muss, wenn die Ergebnisse nach Strings sortiert wurden, zb ORDER BY name ASC, id DESC.
                Denn in dem Query zum Selektieren des vorherigen bzw. naechsten Fahrzeuges kann ich ja schlecht sagen, dass der Name _groeszer_ (bzw. _kleiner_) sein soll, als der aktuelle (also der Name des Fahrzeuges auf der Detailseite).

                Warum nicht? Zum Sortieren wird genau dieses "Größenverhältnis" als Kriterium verwendet. A < B < C usw. ... AuWeh < Fnord < Popel.

                Lo!

                1. Hi Dedlfix!

                  Warum nicht? Zum Sortieren wird genau dieses "Größenverhältnis" als Kriterium verwendet. A < B < C usw. ... AuWeh < Fnord < Popel.

                  Na das ist ja erfreulich ;-)

                  Besten Dank!

                  MfG
                  Peter

                  PS: Ich hatte mich durch phpmyadmin blenden lassen. Denn in dem Suchformular wird bei Strings kein groeszer/kleiner-gleich angezeigt.

                2. Hallo Dedlfix,

                  ich moechte einfach nicht weiter voran kommen ;-(

                  Ich habe den Anwendungsfall jetzt mal separat gekapselt. Hierbei kreise ich jedoch permanent um das gleiche Problem, und zwar dass mir der aktuelle Zeiger* nicht bekannt ist, und ich somit nicht den korrekten Nachfolger und Vorgaenger ermitteln kann. Wobei letzterer sowieso noch einige weitere Aspekte mit sich bringt. Denn um den Vorgaenger zu ermitteln muss ich ja die Sortierung invertieren (so weit ich das jetzt richtig verstanden habe).

                  Hier jetzt mal mein Beispielcode.
                  Er liest Daten aus der Tabelle my_items aus, sortiert nach
                  1. absteigender Position
                  2. absteigendem Preis
                  3. aufsteigendem Name
                  4. aufsteigender ID
                  und stellt sie in einer Tabelle dar.
                  Ist GET['did'] gesetzt, so versucht das Program den Vorgaenger und den Nachgaenger zu ermitteln (links in der Tabelle gibt es Links hierfuer).

                  Das Programm laeuft korrekt, so fern die einzelnen Positionen konsequent unterschiedlich sind. Sprich, wenn mehrere Item mit der gleichen Position existieren, kommt der Select logischerweise durcheinander - und hierzu weisz ich immernoch keine funktionierende Loesung.

                  ====================================================
                  = MYSQL

                  CREATE TABLE IF NOT EXISTS `my_items` (  
                    `id` int(11) NOT NULL auto_increment,  
                    `name` varchar(255) collate latin1_general_ci NOT NULL,  
                    `position` int(11) NOT NULL default '0',  
                    `price` int(11) NOT NULL default '0',  
                    PRIMARY KEY  (`id`)  
                  ) ENGINE=MyISAM  DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=13 ;  
                  INSERT INTO `my_items` (`id`, `name`, `position`, `price`) VALUES  
                  (1, 'Andorra', 1, 100),  
                  (2, 'Andorraflex', 1, 1000),  
                  (3, 'Caesar', 0, 4000),  
                  (4, 'Zeus', 0, 4433),  
                  (5, 'Marx', 4, 1222),  
                  (6, 'Manoman', 4, 4533),  
                  (7, 'Oligo', 0, 4900),  
                  (8, 'Denier', 4, 1200),  
                  (9, 'Stofanus', 2, 4300),  
                  (10, 'Korgos', 2, 4300),  
                  (11, 'Situra', 1, 4500),  
                  (12, 'Radion', 1, 3400);
                  

                  ====================================================
                  = PHP

                  <?php  
                  $order_default = 'ORDER BY position DESC, id ASC';  
                  $order_reverse = 'ORDER BY position ASC, id DESC';  
                    
                  /** OVERVIEW */  
                  $link = mysql_connect('localhost', 'root', '');  
                  $db_selected = mysql_select_db('test');  
                  $query = 'SELECT id, name, position, price FROM my_items '.$order_default;  
                  $result = mysql_query($query);  
                    
                  echo "<table border='1' cellpadding='4'><tr><th>ID</th><th>NAME</th><th>POSITION</th><th>PRICE</th></tr>";  
                  while ($row = mysql_fetch_assoc($result))  
                  {  
                   $detailUrl = $_SERVER['PHP_SELF'].'?did='.$row["id"];  
                   $myLink = '<a href="'.$detailUrl.'">'.$row["id"].'</a>';  
                   echo "<tr>";  
                   echo "<td>".$myLink."</td>";  
                   echo "<td>".$row["name"]."</td>";  
                   echo "<td>".$row["position"]."</td>";  
                   echo "<td>".$row["price"]."</td>";  
                   echo "</tr>";  
                  }  
                  echo "</table>";  
                    
                    
                    
                  /** DETAIL VIEW */  
                  if( isset( $_GET['did'] ) )  
                  {  
                   echo "<hr>";  
                  	  
                   // Get Current  
                   $currentQuery = 'SELECT id, name, position, price FROM my_items WHERE id = '.$_GET['did'];  
                   $currentResult = mysql_query($currentQuery);  
                   $currentRow = mysql_fetch_assoc($currentResult);  
                    
                   // Previous Item	  
                   $prevQuery = 'SELECT id, name, position, price FROM my_items '.  
                                ' WHERE position >= '.$currentRow['position'].' '. // >= becoz of "position DESC"  
                  	      ' AND id != '.$currentRow['id'].' '. // no need for the current one  
                                $order_reverse.  
                  	      ' LIMIT 0,5 '; // ?? Starten __AB__ dem aktuellen Item ???  
                   $prevResult = mysql_query($prevQuery);  
                   $prev = "";  
                   while ($prevRow = mysql_fetch_assoc($prevResult))  
                   {  
                    $prev .= $prevRow['name']. ' <<< ';  
                   }  
                  		  
                   // Next Item	  
                   $nextQuery = 'SELECT id, name, position, price FROM my_items '.  
                                ' WHERE position <= '.$currentRow['position'].' '.// >= becoz of "position DESC"  
                  	      ' AND id != '.$currentRow['id'].' '. // no need for the current one  
                  	      $order_default.  
                  	      ' LIMIT 0,5 '; // ?? Starten __AB__ dem aktuellen Item ???  
                   $nextResult = mysql_query($nextQuery);  
                   $next = "";  
                   while ($nextRow = mysql_fetch_assoc($nextResult))  
                   {  
                    $next .= ' >>> '.$nextRow['name'];  
                   }  
                  	  
                   // Output  
                   echo "<p>";  
                   echo $prev;  
                   echo "<strong>".$currentRow['name']."</strong> ";  
                   echo $next;  
                   echo "</p>";  
                  }  
                  ?>
                  

                  Vielleicht kannst du da ja einmal drueber schauen und mir ggfs. meinen Denkfehler vor Augen fuehren.

                  Vielen Dank & MfG
                  Peter

                  * Siehe Posting weiter oben. Gemeint ist der "Zeiger" auf den aktuellen Datensatzes innerhalb des Resultsets.

                  1. Hi!

                    Ich habe den Anwendungsfall jetzt mal separat gekapselt. Hierbei kreise ich jedoch permanent um das gleiche Problem, und zwar dass mir der aktuelle Zeiger* nicht bekannt ist, und ich somit nicht den korrekten Nachfolger und Vorgaenger ermitteln kann.

                    Der muss dir aber bekannt sein, sonst könntest du den zum Zeiger gehörenden Datensatz gar nicht bestimmen. Das kannst du aber - anhand der ID. Die muss dein Zeiger-Kriterium sein. Mehrfach vorkommende Werte sind dafür nicht geeignet.

                    Im jetzigen Fall zeigst du, dass du die ID für den Detaildatensatz verwendest, im vorangegangenen Verlauf des Threads sprachst du von benutzerfreundlichen URLs. Die kannst du nur verwenden, wenn du ein Unique-Kriterium hast. Das müsste dann auch durch einen entsprechenden Index gleich auf Tabellenebene sichergestellt werden.

                    Ich weiß nicht, welche Bedeutung die Spalte position hat, aber als Auswahlkriterium ist sie unbrauchbar, weil sie keinen eindeutigen Inhalt hat. Wenn du ein zweites Kriterium hast, durch das die Wahl eindeutig wird, dann musst du auch immer dieses zur Auswahl hinzuziehen und an den Übergabestellen von position in andere Programmteile mit anführen.

                    Wobei letzterer sowieso noch einige weitere Aspekte mit sich bringt. Denn um den Vorgaenger zu ermitteln muss ich ja die Sortierung invertieren (so weit ich das jetzt richtig verstanden habe).

                    Ja, zumindest bei der LIMIT-Lösung. Bei nur einem gesuchten Kandidaten kann auch MIN() und MAX() verwendet werden.

                    Ist GET['did'] gesetzt, so versucht das Program den Vorgaenger und den Nachgaenger zu ermitteln (links in der Tabelle gibt es Links hierfuer).

                    Damit hast du ein eindeutiges Kriterium, das muss dein Zeiger sein. Denn darüber bestimmst du ja auch den anzuzeigenden Detail-Datensatz. Und zusammen mit der Sortierung ergeben sich die Vorgänger und Nachfolger.

                    Der Rest sind allgemeine Hinweise zum Code:

                    Ich sehe keine Fehlerbehandlung, sprich: Auswertungen der Rückgabewerte der mysql_*-Funktionen. (Wenn du sie nur aufgrund des Quick'n'Dirty-Charakters des Beispiel weggelassen hast, will ich nichts gesagt haben.)

                    $detailUrl = $_SERVER['PHP_SELF'].'?did='.$row["id"];
                    $myLink = '<a href="'.$detailUrl.'">'.$row["id"].'</a>';
                    echo "<tr>";
                    echo "<td>".$myLink."</td>";
                    echo "<td>".$row["name"]."</td>";
                    echo "<td>".$row["position"]."</td>";
                    echo "<td>".$row["price"]."</td>";
                    echo "</tr>";

                    Sowohl bei der URL als auch bei den Tabellenzellen hast du den Kontextwechsel nicht behandelt. Über $_SERVER['PHP_SELF'] und eine entsprechend präparierte Aufruf-URL kann man die HTML- usw. -Code in die angezeigte Seite einfügen. Bei den Tabellenzellen geht das auch, wenn es jemandem gelingt, den HTML-Code in die Datenbank zu bringen. SCRIPT_NAME enthält im Gegensatz zu PHP_SELF nur den reinen Script-Namen, keine angehängten Path-Bestandteile oder den Querystring.

                    Du kannst PHP_SELF in deinem Fall auch ganz weglassen und nur ?did=... im href-Attribut verwenden. Der Browser ergänzt das selbständig mit der aktuellen Adresse des Dokuments.

                    if( isset( $_GET['did'] ) )
                    // Get Current
                    $currentQuery = 'SELECT id, name, position, price FROM my_items WHERE id = '.$_GET['did'];

                    Diese Abfrage ist nicht notwendig. Du hast ja bereits alle Datensätze für die Übersichtstabelle abgefragt, unter anderem auch diesen. Du brauchst nur in der ersten while-Schleife hinzuzufügen, auf $_GET['did'] == $row["id"] zu vergleichen und daraufhin $currentRow zu füllen.

                    $prev .= $prevRow['name']. ' <<< ';
                      $next .= ' >>> '.$nextRow['name'];

                    Auch hier wieder den Kontextwechsel beachten. Und < sowie > sind Zeichen mit Sonderbedeutung in HTML, müssen also korrekterweise auch maskiert werden.

                    Lo!

                    1. Hi Dedlfix,

                      vorab: bei dem Beispiel handelte es sich logischerweise um eine quick and dirty Loesung.
                      In meiner Applikation nutze ich ein Framework, einen Persistence-Layer, MVC-Pattern und das eigentliche SQL-Statement (um welches es mir in diesem Thread geht) laesst sich auf zwei ganze DIN-A 4-Seiten ausdrucken.
                      (Einen Teil davon habe ich mit Hilfe von einigen Leuten hier erstellt)

                      Ich habe den Anwendungsfall jetzt mal separat gekapselt. Hierbei kreise ich jedoch permanent um das gleiche Problem, und zwar dass mir der aktuelle Zeiger* nicht bekannt ist, und ich somit nicht den korrekten Nachfolger und Vorgaenger ermitteln kann.
                      Der muss dir aber bekannt sein, sonst könntest du den zum Zeiger gehörenden Datensatz gar nicht bestimmen. Das kannst du aber - anhand der ID. Die muss dein Zeiger-Kriterium sein. Mehrfach vorkommende Werte sind dafür nicht geeignet.

                      Da waeren wir doch schon bei dem Problem. Klar ist mit der Zeiger auf das Item bekannt. Aber nicht der Zeiger auf die Position im Resultset. Und letzteres ist ja das, was mir derzeit meine Sorgen bereitet.

                      Im jetzigen Fall zeigst du, dass du die ID für den Detaildatensatz verwendest, im vorangegangenen Verlauf des Threads sprachst du von benutzerfreundlichen URLs.

                      Siehe quick and dirty.
                      In meiner Appliation ist die eindeutigkeit wie folgt gegeben:
                      <group>/<region>/<province>/<city>/<name>
                      Pro <city> kann es nur ein <name> geben.

                      Ich weiß nicht, welche Bedeutung die Spalte position hat, aber als Auswahlkriterium ist sie unbrauchbar, weil sie keinen eindeutigen Inhalt hat. Wenn du ein zweites Kriterium hast, durch das die Wahl eindeutig wird, dann musst du auch immer dieses zur Auswahl hinzuziehen und an den Übergabestellen von position in andere Programmteile mit anführen.

                      Sie hat keine Bedeutung. Siehe quick and dirty ;-)
                      Die Eindeutigkeit liegt einzig und allein in dem PK. Aber nach diesem wird ja nicht gesucht, statt dessen wird zb eben nach der Position sortiert.

                      Ist GET['did'] gesetzt, so versucht das Program den Vorgaenger und den Nachgaenger zu ermitteln (links in der Tabelle gibt es Links hierfuer).
                      Damit hast du ein eindeutiges Kriterium, das muss dein Zeiger sein. Denn darüber bestimmst du ja auch den anzuzeigenden Detail-Datensatz. Und zusammen mit der Sortierung ergeben sich die Vorgänger und Nachfolger.

                      Und exakt hier liegt der Hund begraben. Lediglich anhand der Sortierung plus der ID ist es nicht moeglich zum vorherigen oder naechsten Datensatz zu gelangen. Denn angenommen, es wurde nach der Position sortiert und auf der Ergebnisseite werden 10 Items dargestellt. Wovon die ersten fuenf die gleiche Position besitzen. Klickt der Benutzer jetzt auf den Eintrag in der Mitte, so kann ich auf der Detailseite die vordere und naechste Position nicht ermitteln. Denn dort weiss ich ja nicht, dass das Item (auf der aktuellen Detailseite) in der Mitte der Ergebnisliste stand.

                      Der Rest sind allgemeine Hinweise zum Code:

                      Siehe oben.
                      Der Code diente einzig und allein dazu das Problem zu schildern. Und diesen Zweck hat er auch erfuellt.
                      Wie ich deinen Antworten nun entnehmen kann, bist du dir auch keiner Loesung bewusst? Es kann aber auch gut sein, dass ich dich missverstanden habe.

                      Besten Dank Dedlfix,
                      Peter

                      1. Hi!

                        Die Eindeutigkeit liegt einzig und allein in dem PK. Aber nach diesem wird ja nicht gesucht, statt dessen wird zb eben nach der Position sortiert.

                        Problem erkannt. Selbiges ist, dass vom DBMS zuerst die WHERE-Klausel ausgewertet wird und dann erst die Ergebnismenge sortiert wird. Um alle kleineren (oder größeren) Datensätze per WHERE-Bedingung einschließen zu können, muss das Sortierkriterium als Bedingung herhalten. Bei einem Unique-Kriterium ist das kein Problem. Aber, wie wäre es für Vorgänger damit:

                        WHERE IF(position < $currentPosition, TRUE, IF(position > $currentPosition, FALSE, id < $currentID))

                        Mit kleineren Positionen sind es sowieso Vorgänger, ansonsten mit größeren sind es Nachfolger und für die Vorgänger-Abfrage nicht interessant. Übrig bleiben die gleichen und da entscheidet die ID. Alles was kleiner ist, ist Vorgänger. Nun noch absteigend sortieren und limitieren.

                        Für Nachfolger gilt das gleiche Prinzip, nur angepasst.

                        Einfacher als die verschachtelten IFs ginge es, wenn man beide Sortierkriterien zu einem kombinieren könnte (CONCAT()). Aber das geht bei Zahlenwerten als Kriterien nur, wenn man sie auf gleiche Länge bringt (führende Nullen) (oder sich was anderes eindeutig verknüpfendes als CONCAT() findet).

                        Lo!

                        1. Hi Dedlfix,

                          danke fuer deine Geduld ;-)

                          WHERE IF(position < $currentPosition, TRUE, IF(position > $currentPosition, FALSE, id < $currentID))

                          Hm.. ich verstehe. Allerdings habe ich Probleme das Beispiel zu adaptieren, sobald mehrere Sortierkriterien ins Spiel kommen.
                          In der "echten" Anwendung ist die ID erst das letzte Soriterkriterium, davor kommen vier andere Spalten zum Einsatz. Erschwerend kommt hinzu, dass position dort kein fester Spaltenwert ist, sondern sich durch diverse Subselects zusammensetzt.

                          Ich bin drauf und dran einen anderen Weg zu gehen. Ein Grund ist sicherlich der, dass ich damals bereits geschlagene zwei Wochen an dem eigentlichen Select gesessen habe (bis es mir letztendlich exakt die Ergebnisse geliefert hat, welche ich bekommen wollte) und ich so langsam mit meiner Motivation zu kaempfen habe ;-)

                          Und zwar koennte ich bei der Suche der Items gleich zwei Datenbankabfragen absetzen (auf der Detailseite kaemen ja sowieso zwei weitere Abfragen hinzu (oder evtl. nur eine wenn ich mit UNION arbeiten kann)).
                          Die erste Abfrage liefert mir die limitierten Ergebnisse (sprich die kompletten Objekte mit all ihren dutzenden Referenzobjekten).
                          In der zweiten Abfrage lasse ich mir einfach die IDs ausgeben - ohne Limitierfunktion, ohne Models, ohne Referenzobjekte. Dieses ID-Array uebergebe ich dann mit der Session, so dass jede Detailseite lediglich zu schauen braucht, welches Item in der Liste vorher bzw. nachher dran ist.

                          Eventuell hiesze dies unnoetiger Overhead. Jedoch kalkuliere ich den Datenbestand auf eine maximale Anzahl von 1000 (Tausend) Items. Also eine sehr geringe Menge, wobei eine Suchseite sowieso bereits nur auf eine gefilterte Menge zugreifen kann. Folglich habe ich im Durchschnitt eine Ergebnismenge von ca. 100 Items zu erwarten. Und ein Query, welches lediglich die IDs ausliefert, wird im Gegensatz zu dem objektorientierten Select - sowohl was MySQL als auch PHP betrifft - sicherlich nicht so viel  Resourcen schlucken.

                          MfG
                          Peter

                          1. Hi!

                            WHERE IF(position < $currentPosition, TRUE, IF(position > $currentPosition, FALSE, id < $currentID))

                            Hm.. ich verstehe. Allerdings habe ich Probleme das Beispiel zu adaptieren, sobald mehrere Sortierkriterien ins Spiel kommen.

                            Das lässt sich ja noch durch "Rekursion" lösen.

                            In der "echten" Anwendung ist die ID erst das letzte Soriterkriterium, davor kommen vier andere Spalten zum Einsatz.

                            Die gesamte Bedingung kopieren und anstelle von id < $currentID einfügen, dabei das position durch das jeweilige Kriterium austauschen. Den Vorgang solange wiederholen, bis die ID das letzte Kriterium ist. Ergibt eine mordsmäßig verschachtelte Konstruktion bei ausreichend vielen Kriterien. Übersicht lässt sich wieder herstellen, wenn du das in eine Stored Function auslagerst (siehe Stored Programs and Views).

                            Erschwerend kommt hinzu, dass position dort kein fester Spaltenwert ist, sondern sich durch diverse Subselects zusammensetzt.

                            Subselects in Stored Functions oder Views auslagern, um die Übersicht wieder herzustellen, wäre eine Möglichkeit.

                            Ich bin drauf und dran einen anderen Weg zu gehen.

                            Eigentlich hast du doch die Ergebnismenge schon in sortierter Form abgefragt und könntest aus dieser auch die Blätterinformation extrahieren (wenn ich aus deinem Beispiel richtig auf die eigentliche Anwendung schließe). Wenn du nach dem EVA-Prinzip zunächst die Daten zusammensuchst, sie in einerm Array ablegst und erst irgendwann später ausgibst, lässt sich dieses Array auch für weitere Auswertungen heranziehen.

                            Lo!

                            1. Hi Dedlfix,

                              [viele SQL-Tip(p)s]

                              Viele Wege fuehren nach Rom ;-)
                              Ich denke ich hoere besser auf das sowieso bereits komplizierte SQL-Statement noch weiter zu verkomplizierem und entscheide mich fuer die von mir im letzten Posting erwaehnte Moeglichkeit.

                              Eigentlich hast du doch die Ergebnismenge schon in sortierter Form

                              Tja, allerdings nur die Ergebnisse fuer _eine_ Seite (zb Ergebnisse #19 - #29). Und wenn der 29. Eintrag geoeffnet wird gibt es keinen Folgeeintrag - selbst wenn 137 Ergebnisse existieren.

                              Moment... gerade kam mir folgende Idee:
                              Wenn es sich tatsaechlich um den letzten Eintrag handeln sollte (also der "letzte" Eintrag einer Seite der Ergebnisse), dann kann ich ja einfach den Seitenzaehler inkrementieren und die regulaere Suche anstoszen... hierfuer muesste ich zu den Suchergebnissen noch zusaetzlich die Suchkriterien mit in der Session uebergeben.. Auf nach Rom!

                              Nochmal vielen Dank den Austausch!

                              MfG
                              Peter