MrOrangeSky: Einträge in $_POST

Hallo und Guten Tag zusammen !
Ich bastel gerade an einer neuen Seite herum, habe aber mit der Suche nach einer Lösung aufgegeben. Ich hoffe es findet such jemand der mir ein wenig unter die Arme greift...

Folgendes Problem:
Im unten stehenden Script sollen mehrere inserts in die DB geschrieben werden. Allerdings wird mit $_POST immer nur die letze Auaswahl übernommen, bzw. im array überschrieben. Wie kann ich das ändern, bzw. wo liegt mein Denkfehler im Aufbau?

Ich wäre für Hilfe sehr dankbar !
MrOrangeSky

<?php
session_start();
 include("inc/functions.inc");
 include("inc/db_user.inc");
 include("inc/arrays.inc");

$cxn = Connect_to_db("db_user.inc");
 $sql = "SELECT *, DATE_FORMAT(online,'%d.%m.%Y') as online, DATE_FORMAT(online,'%w') as weekday
   FROM items order by rang";

$result = mysqli_query($cxn,$sql)
  or die ("Die Abfrage enthält einen Fehler !");
 $n = 1;

while($row = mysqli_fetch_assoc($result))
  {
  foreach($row as $field => $value)
         {
           $menu[$n][$field]=$value;
         }
        $n++;
        }
     for($i=1;$i<=2;$i++)
     {
     $key = $menu[$i]['weekday'];
        $menu[$i]['weekday'] = $weekday[$key];
     echo "<tr>";
     echo "<td>{$menu[$i]['weekday']}, der</td>";
     echo "<td>{$menu[$i]['online']}</td>";
     echo "<td>Men&uuml; {$menu[$i]['rang']}:&nbsp;</td>";
     echo "<td>{$menu[$i]['name']}</td>";
     echo "<td>{$menu[$i]['preis']} &euro;</td>";
     echo "<td align='center' width='12%'>
      <select name='anzahl' size='1'>
      <option value='0'>0</option>
         <option value='1'>1</option>
         <option value='2'>2</option>
         <option value='3'>3</option>
   </select></td>\n";
  echo "<input type='hidden' name='item_id' value='{$menu[$i]['id']}'>\n";
  echo "<input type='hidden' name='einzelpreis' value='{$menu[$i]['preis']}'>\n";
     echo "</tr>";
     }
echo "<td colspan='6'><input type='submit' name='order' value='Bestellen'></td></tr>";

if($_POST['order'] == "Bestellen")
   foreach($_POST as $field => $value)
   {
  $amount = $_POST['einzelpreis'] * $_POST['anzahl'];
  $connect = connect_to_db("db_user_zentrale.inc");
  $sql_tmp_order = "INSERT INTO transaktion (id,datum,client,benutzer_id,db_id,transaktionstyp_id,item_id,einzelpreis,anzahl,gesamtpreis,status_id)
                   VALUES ('',now(),'1','{$_SESSION['benutzer_id']}','02','1','{$_POST['item_id']}','{$_POST['einzelpreis']}','{$_POST['anzahl']}','$amount','0')";
  $result = mysqli_query($connect, $sql_tmp_order)
   or die("sql_tmp_order".mysqli_error($connect));
 }
?>

  1. echo $begrüßung;

    Im unten stehenden Script sollen mehrere inserts in die DB geschrieben werden. Allerdings wird mit $_POST immer nur die letze Auaswahl übernommen, bzw. im array überschrieben. Wie kann ich das ändern, bzw. wo liegt mein Denkfehler im Aufbau?

    PHP liest die Name-Value-Pärchen des Formulars und legt daraufhin Elemente im Array $_POST ab (analog bei $_GET). Zum Beispiel so:

    $_POST['name'] = 'value';

    Was passiert, wenn die mehrere Formular-Elemente den gleichen Wert im name-Attribut haben? Dies:

    $_POST['name'] = 'value1';  
    $_POST['name'] = 'value2';
    

    Der Eintrag wird von nachfolgenden name-value-Pärchen überschrieben. Um das zu umgehen kannst du dem name-Attribut nun einen eindeutigen Namen geben: name_1, name_2, etc.

    $_POST['name_1'] = 'value1';  
    $_POST['name_2'] = 'value2';
    

    Oder aber, du verwendst eckige Klammern: name[1], name[2]:

    $_POST['name'][1] = 'value1';  
    $_POST['name'][2] = 'value2';
    

    Vielleicht ist diese Struktur ungünstig, da nun für jedes Feld ein eigenes Array mit sämtlichen Werten dieser Felder angelegt wird. Dies nennt man auch Spaltenarray:

    $_POST['feld1'] = array(1 => 'value1-1', 3 => 'value3-1');  
    $_POST['feld2'] = array(1 => 'value1-2', 3 => 'value3-2');
    

    Besser wäre dann ein Zeilenarray: Jeder Eintrag enthält einen kompletten Datensatz.

    $array[1] = array('feld1' => 'value1-1', 'feld2' => 'value1-2');  
    $array[3] = array('feld1' => 'value3-1', 'feld2' => 'value3-2');
    

    Doch man kann das name-Attribut nicht '[1][feld1]' benennen. Da braucht es noch einen Bezeichner davor, z.B. 'warenkorb[1][feld1]'. Somit ergibt sich dann:

    $_POST['warenkorb'][1][feld1] = 'value1-1';  
    $_POST['warenkorb'][1][feld2] = 'value1-2';  
    $_POST['warenkorb'][3] = array('feld1' => 'value3-1', 'feld2' => 'value3-2'); // alternative Schreibweise
    

    Damit du eine eindeutige Zuordnung zum Datensatz in der Datenbank hast, bietet sich an, statt der in meinem Beispiel verwendeten [1] und [3] die Datensatz-ID zu verwenden.

    Somit kannst du nun mit foreach durch den übergebenen Warenkorb laufen:

    foreach ($_POST['warenkorb'] as $id => $inhalt) {  
      echo $inhalt['feld1'];  
      echo $inhalt['feld2'];  
    }
    

    Nun noch ein paar Anmerkungen zu deinem Code

    include("inc/functions.inc");

    Die Klammern sind überflüssig.

    $result = mysqli_query($cxn,$sql)
      or die ("Die Abfrage enthält einen Fehler !");

    Das ist eine Fehlermeldung, die den Besucher deiner Seite vermutlich nicht die Bohne interessiert. Betrachte doch mal dein Werk aus dessen Sicht. <besucher>Sind alle Dinge, die da auf der Seite angezeigt werden für mich wichtige Informationen?</besucher>
    Warum geht denn eine Abfrage in die Hose? Wenn du einen Fehler eingebaut hast, oder durch einen von dir nicht beachteten Eingabewert die Query fehlerhaft wird, dann kann der Besucher nichts dafür und sollte nicht mit Details dazu belästigt werden. Wenn dagegen der Datenbank-Server streikt, geht den das auch nichts an, aber er muss trotzdem darunter leiden. Gib ihm eine allgemeine Trostmeldung. "Ihre Anforderung kann momentan (wegen technischer Probleme) nicht bearbeitet werden. $Alternativvorschlag". Der Teil in Klammern ist entbehrlich. Und für die Variable kannst du dir selbst was nettes ausdenken :-)

    <input type='submit' name='order' value='Bestellen'>
    if($_POST['order'] == "Bestellen")

    Es ist nicht genau spezifiziert, wie sich ein Browser verhalten soll, wenn der Benutzer das Formular mit Enter absendet. Der IE überträgt in dem Fall kein Submit-Button-Name-Value-Pärchen, weil kein Submit-Button aktiviert wurde. Der Firefox tut so als ob der Submit-Button gedrückt wurde und sendet es mit. Wie es bei anderen Browsern und bei mehreren Submit-Buttons aussieht kannst du ja selbst erforschen ...

    Du kannst ein Hidden-Input vor dem Submit-Button einfügen, das name und value des Default-Submit-Buttons enthält und so das Knopf-Drücken simulieren.

    $sql_tmp_order = "INSERT INTO transaktion (id,datum,client,benutzer_id,db_id,transaktionstyp_id,item_id,einzelpreis,anzahl,gesamtpreis,status_id)
                       VALUES ('',now(),'1','{$_SESSION['benutzer_id']}','02','1','{$_POST['item_id']}','{$_POST['einzelpreis']}','{$_POST['anzahl']}','$amount','0')";

    Das direkte Einsetzen der Benutzereingaben in die Query ist zwischen sehr ungünstig und sehr gefährlich anzusiedeln. Im einfachsten Fall reicht ein eingegebenes ' um deine Query außer Tritt kommen zu lassen. Da hört dann plötzlich der String auf ... Im Allgemeinen nennt man diese Sicherheitslücke SQL-Injection.

    Deshalb solltest du Werte, deren Inhalt du nicht kennst, wie bei den Benutzereingaben, immer mit mysqli_real_escape_string() behandeln. Das maskiert alle besonderen Zeichen so, dass sie ihre Sonderbedeutung verlieren.

    Da du aber mit mysqli arbeitest, bietet sich noch eine wesentlich bessere Möglichkeit an: Prepared Statements. Die variablen Teile der Query werden als ? notiert

    INSERT INTO table (feld1,feld2) VALUES (?,?)

    und später die einzusetzenden Werte mit dem Prepared Statement verbunden und selbiges dann ausgeführt. Dabei werden die gebundenen Werte in die Query eingefügt. Du brauchst dich weder um das '' bzw. "" um den Wert drumrum noch um das Maskieren von Sonderzeichen zu kümmern. Das machen die Funktionen für Prepared Statements für dich. Ein Beispiel für ein INSERT befindet sich im Handbuch bei mysqli_stmt_bind_param()

    Beachte bitte auch die Auswirkungen von Magic Quotes auf deine Eingabewerte und meine Empfehlung, dieses Feature nicht zu verwenden (Hinweise zum Deaktivieren), da es zum einen nicht richtig funktioniert und zum anderen ein aussterbendes Feature (PHP 6) ist.

    echo "$verabschiedung $name";

    1. Hallo dedlfix!
      Vielen lieben Dank für Deine sehr kompetenten und sachlichen Hinweise zu meinem Code. Das Problem habe ich nun verstanden und bin sehr erfreut das es im inet noch Menschen gibt, die sich ohne gleich eine Rechnung zu schreiben soviel Mühe geben!

      Meinen Horizont hast Du auf jeden Fall um Einiges erweitert!

      Was die Injections betrifft: Das ist durchaus bewusst, doch wird die Anwendung nicht im Netz laufen sondern auf einem Terminal. Einzige Bedienmöglichkeit ist die Maus, die Anmeldung erfolgt per Chipkarte.
      Doch waren die Hinweise nicht umsonst, ganz im Gegenteil, mein Verständnis wurde durchaus massiv erweitert!

      Besten Dank dafür, wenn ich mal irgend etwas für Dich tun kann, melde Dich bitte =)

      Grüße
      MrOrangeSky