Invoeren van gegevens

Inleiding Introductie invoeren Praktijkvoorbeeld

Inleiding

In de vorige hoofdstukken heb je geleerd om een connectie met een database management service te leggen en gegevens op te vragen uit deze database. Er is ook een begin gemaakt met een eenvoudige website voor de administratie van een webwinkel. Gegevens opvragen is belangrijk, maar even belangrijk is het opslaan van gegevens in de database. We gaan nu de eenvoudige website uitbreiden met de invoer van een nieuw product. Een HTML formulier en de verwerking van een formulier zijn daarbij natuurlijk een noodzaak. Ben je die techniek vergeten kijk dan in de cursus Interactieve website: PhP . Hoofdstuk 3 in die cursus geeft het snelst het gewenste inzicht.

Aan het einde van dit hoofdstuk weet je :

  • welke gegevens nodig zijn om in te voeren;
  • welke commando's een rol spelen bij de invoering;
  • kun je zelf gegevens invoeren in een database;

Invoeren van gegevens

Dit hoofdstuk ga je gegevens invoeren in een database. Het opslaan van gegevens vanuit php vindt ook weer plaats via SQL code. In de regel hieronder vind je de SQL instructie voor het invoeren van een specifiek product.
INSERT INTO product (id, naam, prijs, voorraad, omschrijving) VALUES ('', 'Motorolie','25.99','60','Motorolie van zeer hoge kwaliteit')
Merk op dat voor het veld id een lege waarde ('') wordt verstuurd. De reden is dat in mysql automatische nummering voor dit veld is ingeschakeld en krijgt dit veld bij invoer automatisch een unieke waarde toegewezen. Het veld kan om deze reden ook volledig worden weggelaten.
INSERT INTO product (naam, prijs, voorraad, omschrijving) VALUES ('Motorolie','25.99','60','Motorolie van zeer hoge kwaliteit')

Om deze SQL opdracht in de code te kunnen gebruiken moet deze algemener worden gemaakt, want we moeten met een formulier de informatie voor een willekeurig product kunnen opsturen. De verwerking moet op een veilige manier gebeuren, want gebruikers zouden door middel van SQL injection een aanval op de database kunnen doen. Volg de link en lees hoe SQL Injection werkt.

Hier wordt met de techniek "Prepared Statements" gepoogd SQL Injection te voorkomen. Dit gaat in PDO als volgt. We veranderen de query naar:

$query ="INSERT INTO product (naam,prijs,voorraad,omschrijving) VALUES (:naam, :prijs, :voorraad, :omschrijving)";

In PDO zijn :naam, :prijs, :voorraad en :omschrijving een soort van variabelen die daarna aan overeenkomstige variabelen in php worden gekoppeld. In ons voorbeeld zijn dat $naam, $prijs, $voorraad en $omschrijving. Als die variabelen gevuld zijn dan wordt de volledige instructie in php:

$query = "INSERT INTO product (naam,prijs,voorraad,omschrijving) VALUES (:naam, :prijs, :voorraad, :omschrijving)";
$opdracht = $db->prepare($query);
$opdracht->bindParam(':naam', $naam);
$opdracht->bindParam(':prijs', $prijs);
$opdracht->bindParam(':voorraad', $voorraad);
$opdracht->bindParam(':omschrijving', $omschrijving);
$opdracht->execute();

In de voorbeeld code in product.php is deze instructie nog met wat extra controles omgeven.


Praktijkvoorbeeld

Opdrachten
  1. In voorbeeld 3 hieronder is een uitwerking te zien voor het invoeren van gegevens, samen met de code uit het vorige hoofdstuk. In de onderstaande code is met gele achtergrond aangegeven welke code is toegevoegd. We gebruiken weer de switch techniek om onderscheid te maken tussen de verschillende acties.
    1. Sla de eerste code in een bestand genoemd: index.php op.
    2. Voeg de functies in het tweede, derde en vierde code blok toe aan het bestand actie.php.
    3. Bekijk het resultaat.
    4. Bestudeer de code goed en begrijp waarom alles werkt zoals het werkt.
    5. Voeg eigen commentaar toe waarin je ook uitlegt hoe alles werkt.
voorbeeld 3: index.php
<?php
//Laad database code
require_once("database.php");
//Laad algemene code
require_once("algemeen.php");
//Laad code voor de acties met betrekking tot producten die we op de database gaan doen
require_once("product.php");

// declareren van variabelen
$menu = getMenu(); // Haal het menu door de functie getMenu() uit algemeen.php op te roepen
$actie="";
$uitvoer = "";
if( isset($_POST['actie']) && !empty($_POST['actie']))
{
 $actie=$_POST['actie'];
}
// afhandelen actie ofwel uitvoer maken
switch($actie)
{
 case "Invoeren": // Voer de functie artikelInvoeren() in product.php uit
           $uitvoer=artikelInvoeren($db); 
          break;
 case "Voeg Product Toe":
    // Voer de functie toonArtikelInvoerTabel in acties.php uit met 4 lege waarde fout paren
 		  $uitvoer=toonArtikelInvoerTabel(legeInvoerEnFout(4),true);  
          break;
 case "Toon VerbindingStatus": $uitvoer = toonVerbindingStatus(); break;
 case "Toon Artikelen":
 default: $uitvoer=toonArtikelenLijst();
}

/* Met onderstaand echo commando kun je een stuk text tussen END en END;
 naar de client computer sturen
*/
echo <<<END
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />    
<title>Page title</title>
<style type="text/css">
td
{
 border-bottom: 1px groove black;
}
tr:nth-child(odd)
{
 background-color: lightblue;
}
</style>
</head>
<body>
<h1>Garagebedrijf</h1>
<form action='{$_SERVER['PHP_SELF']}' method="post" >
$menu
</form>
<hr>
<form action='{$_SERVER['PHP_SELF']}' method="post" >
$uitvoer 
</form>
</body>
</html>
END;
//einde van het PhP blok
?>
voorbeeld 3: functies voor algemeen.php

De functie getMenu

/*
 getMenu geeft de code voor het menu in de website
 */
function getMenu()
{
 $uitvoer=<<<END
 <table class='menu' summary="">
  <tr>
    <td><input type="submit" name="actie" value="Toon Artikelen">
    </td>
    <td><input type="submit" name="actie" value="Voeg Product Toe">
    </td>
    <td><input type="submit" name="actie" value="Toon VerbindingStatus">
    </td>	
  </tr>
 </table>
END;
 return $uitvoer; 
}

De functie legeInvoerEnFout

De volgende functie voegen we toe omdat die straks bij het invoeren van medewerkers en klanten ook nuttig is. Bij ieder veld dat je moet invullen hoort een waarde en je kan een fout bij het invullen maken. De waarden en de fouten moeten we kunnen volgen tussen de verschillende schermen.

/**
 * legeInvoerEnFout maakt een dubbele array met lege strings 
 * die staan voor waarde-fout paren
 * $aantal is aantal gewenste waarde-fout paren
 */
function legeInvoerEnFout($aantal)
{
	$resultaat = array();
	// voeg per iteratie een waarde-fout paar toe
	for($i=0;$i<$aantal;$i++) $resultaat[$i]=array("",""); 
	return $resultaat;
}
voorbeeld 3: functies voor product.php

De functie toonArtikelInvoerTabel

De functie toonArtikelInvoerTabel is een voorbeeld van een functie die meerdere argumenten nodig heeft om te kunnen werken.

/**
 * toonArtikelInvoerTabel toont de tabel waarmee een product kan worden ingevoerd
 * of het resultaat van de invoer actie kan laten zien
 * param $productEnFout: lijst van producteigenschappen bestaande uit 
 * 4 invoer velden en eventuele fouten
 *		[
			[naam,foutnaam],
			[prijs,foutprijs],
			[voorraad,foutvoorraad],
			[omschrijving,omschrijving],
 *		]
 * param $alsFormulier true als formulier getoond moet worden, 
                       false als alleen de informatie getoond moet worden
 */
function toonArtikelInvoerTabel($productEnFout,$alsFormulier)
{
 $invoer1="";
 $invoer2="";
 $invoer3="";
 $invoer4="";
 $invoerknop="";
 // Voeg hier eigen commentaar toe.
 //print_r($productEnFout);
 // Voeg hier eigen commentaar toe.
 if($alsFormulier)
 {
 	$invoer1="<input name='naam' value='{$productEnFout[0][0]}'/>"; // naam
 	$invoer2="<input name='prijs' value='{$productEnFout[1][0]}'/>"; // prijs
	$invoer3="<input name='voorraad' value='{$productEnFout[2][0]}'/>"; // voorraad
	$invoer4="<textarea name='omschrijving'/>{$productEnFout[3][0]}</textarea>"; // omschrijving
	
	$invoerknop=<<<END
  <tr>
    <td colspan="3">
      <input type="submit" name="actie" value="Invoeren"/>
   </td>
  </tr>
END;
 }
 else
 {
	$invoer1=$productEnFout[0][0]; // naam
	$invoer2=$productEnFout[1][0]; // prijs
	$invoer3=$productEnFout[2][0]; // voorraad
 	$invoer4=$productEnFout[3][0]; // omschrijving
 }
 // Voeg hier eigen commentaar toe.
 $uitvoer=<<<END
  <h3>Voer artikel in</h3>
  <table>
  <tr>
    <td>Geef naam artikel</td>
    <td>$invoer1</td>
    </td>
    <td>{$productEnFout[0][1]}</td>
  </tr>
  <tr>
    <td>Geef prijs</td>
    <td>$invoer2</td>
    <td>{$productEnFout[1][1]}</td>
  </tr>
  <tr>
    <td>Geef aantal in voorraad</td>
    <td>$invoer3</td>
    <td>{$productEnFout[2][1]}</td>
  </tr>
  <tr>
    <td>Geef omschrijving</td>
    <td>$invoer4</td>
    <td>{$productEnFout[3][1]}</td>
  </tr>
  $invoerknop
  </table>
END;
 return $uitvoer;
}

De functie artikelInvoeren

De functie artikelInvoeren is de langste van het stel.
/*
 * artikelInvoeren leest de invoer uit de $_POST variabele,
 * test de invoer, geeft foutmeldingen bij mislukkingen en voegt als
 * er niets misgaat een record toe aan de database. 
 * param &$db: verwijzing naar de database
 */
function artikelInvoeren(&$db)
{
 $fout1="";
 $fout2="";
 $fout3="";
 $fout4="";
 $naam = "";
 $prijs = "";
 $voorraad = "";
 $omschrijving = "";
 $uitvoer ="";
 $melding="";

 //verbinding maken met database
 if(!$db) maakVerbinding($db,$melding);
 if(!$db) // test verbinding
 {
    // mislukt
    $uitvoer=$melding;
 }
 else
 {
  // isset is een functie die kijkt of een element in een array bestaat
   // post variabelen ophalen 
   // https://www.w3schools.com/php/php_mysql_prepared_statements.asp
   //  prepared statements worden gebruikt om sql injection te voorkomen
   try{
        $query = "INSERT INTO product (naam,prijs,voorraad,omschrijving) VALUES (:naam, :prijs, :voorraad, :omschrijving)";
        $opdracht = $db->prepare($query);
        $opdracht->bindParam(':naam', $naam);
        $opdracht->bindParam(':prijs', $prijs);
        $opdracht->bindParam(':voorraad', $voorraad);
        $opdracht->bindParam(':omschrijving', $omschrijving);
        if(isset($_POST["prijs"]))
        {
            $prijs = $_POST["prijs"];
        }
        if(isset($_POST["naam"]))
        {
            $naam = $_POST["naam"];
        }
        if(isset($_POST["voorraad"]))
        {
            $voorraad = $_POST["voorraad"];
        }
        if(isset($_POST["omschrijving"]))
        {
            $omschrijving= $_POST["omschrijving"];
        }
        // test of er waarden en getallen zijn
        // Voeg hier eigen commentaar toe.
        if( !empty($naam) && 
              ((!empty($prijs) && is_numeric($prijs))||$prijs=="0") && 
              ((!empty($voorraad) && is_numeric($voorraad))||$voorraad=="0") && 
              !empty($omschrijving) ) 
        {    
            // Maak van prijs een double
            $prijs=(double)$prijs; 
            //Alle waarden voor de invoer zouden nu goed moeten zijn 
            // en zijn we nu klaar om de opdracht door te geven aan database
            $opdracht->execute();
            $uitvoer = toonArtikelInvoerTabel( array(
                        array($naam,$fout1),
                        array($prijs,$fout2),
                        array($voorraad,$fout3),
                        array($omschrijving,$fout4)
                    ),false);
            $uitvoer .= "Artikel is ingevoerd";
        }
        else // er is een fout in waarden
        {
         // Voeg hier eigen commentaar toe. 
         if(empty($naam)){$fout1="Er moet een naam worden ingevuld";}
         if(empty($prijs)){$fout2="Er moet een getal worden ingevuld";}
         elseif(!is_numeric($prijs)){$fout2="$prijs is geen getal";}
         if(empty($voorraad)){$fout3="Er moet een getal worden ingevuld";}
         elseif(!is_numeric($voorraad)){$fout3="$voorraad is geen getal";}
         if(empty($omschrijving)){$fout4="Er moet een omschrijving worden ingevuld";}
         
         $uitvoer = toonArtikelInvoerTabel( array(
                        array($naam,$fout1),
                        array($prijs,$fout2),
                        array($voorraad,$fout3),
                        array($omschrijving,$fout4)
                    ),true);
        }
    }
    catch(PDOException $e) { // Er is een fout opgetreden in SQL
        $uitvoer = toonArtikelInvoerTabel( array(
            array($naam,$fout1),
            array($prijs,$fout2),
            array($voorraad,$fout3),
            array($omschrijving,$fout4)
            ),true);
        $uitvoer .= "Fout uitvoeren query!<br/>";
        $uitvoer .= $e->getMessage();
    }
    //sluit database
    verbreekVerbinding($db);
  }
  return $uitvoer;
}