Hallo allemaal,

Voor een introductie in classes / OOP moet ik ervaring opdoen met classes.
Opdracht: classes maken voor ophalen, toevoegen, updaten, verwijderen van gegevens uit de verschillende database tabellen.

mijn idee was:
Eerst een database class maken voor connectie, simpel beginnen.
Dit gedaan en geprint, komt een keurig object uit. daarentegen snap ik nog niet hoe ik dit toepas.
Heb simpelweg de file gerequired en de code aangepast maar krijg nu 3 foutmeldingen:
mysqli_query() expects parameter 1 to be mysqli, object given in C:\wamp64\www\formulier.php on line 120
Undefined variable: sql3 in C:\wamp64\www\formulier.php on line 139
mysqli_error() expects parameter 1 to be mysqli, object given in C:\wamp64\www\formulier.php on line 139

ik heb nog gebrek aan inzicht mbt het toepassen van mijn class, maar dat de $sql3 niet goed zou zijn vind ik erg raar.. wat ben ik vergeten/doe ik fout?

class:

<?php
class database {

var $db_connection = FALSE;	
var $db_host = "localhost";
var $db_username = "root";
var $db_pass = "root";
var $db_name = "opdracht";

	function __construct (){
		$this->createConnection();
	}
	
	function createConnection() {
		$this->db_connection = mysqli_connect($this->db_host,$this->db_username,$this->db_pass,$this->db_name);
	}
	
	function getConnection() {
		return $this->db_connection;
	}
}

$connection = new database();
?>


formulier.php:

<?php

	include_once('header.php');
	require ('test2.php');
	//todo: include class met databasefuncties
	
// zodra submit wordt gedrukt, stop input in variabelen voor verificatie en verzending naar database
if(isset($_POST['submit'])){
		$voornaam = ($_POST["Voornaam"]);
		$achternaam = ($_POST["Achternaam"]);
		$straat = ($_POST["Straat"]);
		$plaats = ($_POST["Plaats"]);
		$email = ($_POST["Email"]);
		$telefoon = ($_POST["Telefoon"]);
		// todo: bestandsnaam /&bestand
		
//define test input functie voor validatie, 3 soorten type input verwacht (varchar-tekst, varchar-email, int-telefoon
	function test_input($var,$soort){
	if ($soort == 'tekst'){
		if(preg_match("/^[a-zA-Z -]*$/",$var) == FALSE)
		return FALSE;
	}elseif ($soort == 'email'){
		if(filter_var($var, FILTER_VALIDATE_EMAIL) == FALSE){
			return FALSE;
		}
	}
	elseif($soort == 'telefoon'){
	if(preg_match('/^[0-9]{10}$/',$var) == FALSE)
		return FALSE;
	}
	else {
		return TRUE;
	}
	}
	//?todo: validatie bestandtype, overzichtelijk of toch in aparte upload file?
	
// create error voor als leeg, validatie ja, validatie nee, voor elke variabele. 
// todo: toevoegen errors voor bestandsvariabelen, tododo: vervangen in array voor overzichtelijkheid
$error = FALSE;
	if (empty($voornaam)) {
		$voornaamerr = "<li>Voornaam is verplicht</li>";
		$error = TRUE; 
	}
	elseif (test_input($voornaam, 'tekst') === FALSE) {
		$voornaamerr = "<li>Voornaam ongeldig</li>"; 
		$error = TRUE; 
	}
	else {
		$voornaamerr = "";
	}
	
	if (empty($achternaam)) {
		$achternaamerr = "<li>Achternaam is verplicht</li>";
		$error = TRUE;
	}
	elseif (test_input($achternaam, 'tekst') === FALSE) {
		$achternaamerr = "<li>Achternaam is ongeldig</li>";
		$error = TRUE;
	}
	else {
		$achternaamerr = "";
	}
	
	if (empty($straat)) {
		$straaterr = "<li>Straat is verplicht</li>";
		$error = TRUE;
	}
	elseif  (test_input($straat, 'tekst') === FALSE) {
		$straaterr = "<li>Straatnaam is ongeldig</li>";
		$error = TRUE;
	}
	else {
		$straaterr = "";
	}
		
	if (empty($plaats)) {
		$plaatserr = "<li>Plaats is verplicht</li>";
		$error = TRUE;
	}
	elseif  (test_input($plaats, 'tekst') === FALSE) {
		$plaatserr = "<li>Plaatsnaam is ongeldig</li>";
		$error = TRUE;
	}
	else {
		$plaatserr = "";
	}
	
	if (empty($email)) {
		$emailerr = "<li>Email is verplicht</li>";
		$error = TRUE;
	}
	elseif  (test_input($email, 'email') === FALSE) {
		$emailerr = "<li>Email is ongeldig</li>";
		$error = TRUE;
	}
	else {	
	$emailerr = "";
	}
	
	if (empty($telefoon)) {
		$telefoonerr = "<li>Telefoonnummer is verplicht</li>";
		$error = TRUE;
	}
	elseif  (test_input($telefoon, 'telefoon') === FALSE) {
		$telefoonerr = "<li>Telefoonnummer is ongeldig</li>";
		$error = TRUE;
	}
	else {
		$telefoonerr = "";	
	}

// wanneer error niet aanwezig, verwerk gegevens in database, wanneer wel, creeer error en geef deze weer via errormelding
	if(!$error){
		//todo: Doe de post shit naar database via class 

		// oude/huidige sql: insert query in losse variabelen stoppen, per databasetabel dmv loop 
		$sql1 = "INSERT INTO persoonsgegevens(voornaam,achternaam) VALUES('$voornaam','$achternaam')";
		
		if (mysqli_query($connection, $sql1)) {
			
			$user_id = mysqli_insert_id($connection);
			$sql2 = "INSERT INTO contactgegevens (email,telefoon,user_id) VALUES('$email','$telefoon','$user_id')"; 
			//vraagje: kan de $user_id declaratie beter bij persoonsgegevens query weergeven ofniet?
			
			if (mysqli_query($connection, $sql2)) {
		
				$sql3 = "INSERT INTO adresgegevens (straat,plaats,user_id) VALUES('$straat','$plaats','$user_id')";
				if (mysqli_query($connection, $sql3)) {
					echo '<p style="width:100%; padding:10px; background-color:#32beeb;color:#000000;text-align:center;">Gelukt!</p>';
					header ("Refresh: 3 url=overzicht.php"); 
				} else {
					echo "Error: " . $sql1 . "<br>" . mysqli_error($connection);
				}
			} else {
				echo "Error: " . $sql2 . "<br>" . mysqli_error($connection);
			}
		} else {
			echo "Error: " . $sql3 . "<br>" . mysqli_error($connection);
		}
	}else{
		// create 1 errormelding voor implementatie, combineer losse errors van elke variabele string (.=) om zo en kleine, overzichtelijke PHP code in de HTML shell te krijgen
		$errormelding = '<ul>';
		if(isset($voornaamerr) && !empty($voornaamerr)){
			$errormelding .= $voornaamerr;
		}
		if(isset($achternaamerr) && !empty($achternaamerr)){
			$errormelding .=  $achternaamerr;
		}
		if(isset($straaterr) && !empty($straaterr)){
			$errormelding .=  $straaterr;
		}
		if(isset($plaatserr) && !empty($plaatserr)){
			$errormelding .=  $plaatserr;
		}
		if(isset($emailerr) && !empty($emailerr)){
			$errormelding .=  $emailerr;
		}
		if(isset($telefoonerr) && !empty($telefoonerr)){
			$errormelding .=  $telefoonerr;
		}		
		$errormelding .= '</ul>';
	}
}

?>
De $connection is een instance van jouw class 'database', en dat is geen extension van mysqli. In je class maak je wel een $db_connection aan. Kort door de bocht kun je dit probleem dus oplossen door mysqli_query($connection->db_connection, ...) te schrijven.

Wat je normaal echter met zo'n 'wrapper' class probeert te doen is zoveel mogelijk recht-toe-recht-aan ellende uit je code te krijgen. Je zou dus een functie $connection->query() kunnen maken:

public function query($sql){
  return mysqli_query($this->db_connection,$sql);
}

scheelt je steeds wat typewerk.

Of bijvoorbeeld nog abstracter: $connection->insert('tabel',['veld1' => 'waarde1', ...]);

O: 'var' is niet meer van deze tijd. Gebruik public of private of protected.
O2: zoek eens op SQL-injectie, of vul anders een keer 's Hertogenbosch als plaatsnaam in.
O3: als je deze class vaker wilt gaan gebruiken moet je de connection details (user, pass, enz) niet in de class zelf opslaan maar juist als params meegeven in de __construct. Kun je elke keer wat anders meegeven.

[size=xsmall]Toevoeging op 22/09/2017 07:22:26:[/size]

O4: je 'else error' constructie staan in omgekeerde volgorde van de aanroep. Op regel 132 moet je dus de foutmelding geven van $sql3, enz. Nu gaat het meteen al fout op regel 119 met $sql1, en dan kom je dus in de error melding op regel 138 uit. Daar tover je dan ineens $sql3 uit de hoge hoed. Die had je nog niet aangemaakt (want zover kwam je code niet), en dus krijg je een PHP foutmelding.
Rob Doemaarwat op 22/09/2017 07:12:55

Wat je normaal echter met zo'n 'wrapper' class probeert te doen is zoveel mogelijk recht-toe-recht-aan ellende uit je code te krijgen. Je zou dus een functie $connection->query() kunnen maken:

public function query($sql){
  return mysqli_query($this->db_connection,$sql);
}



Ben hiermee aan de slag geweest. Wat ik nog niet snap is het verplaatsen van mijn querycode naar een nieuwe class. ik ben begonnen met een class voor mijn eerste tabel: persoonsgegevens. Deze had ik alsvolgt uitgedacht:

<?php
class persoonsgegevens extends database { 
public $sql = "INSERT INTO persoonsgegevens(voornaam,achternaam) VALUES('$voornaam','$achternaam')";
public $firstName = ($_POST["Voornaam"]);
public $lastName = ($_POST["Achternaam"]);
public $userID = mysqli_insert_id($connection->db_connection);

	function query($sql){
		return mysqli_query ($this->db_connection,$sql);
}
?>


De bedoeling is dat de input van de gebruiker via de class bij de juiste kolommen en tabellen uitkomt, beginnend dus met de tabel persoonsgegevens. en dan moet dit uiteindelijk voor zowel een INSERT, UPDATE, SELECT als DELETE..
Het klinkt een beetje vreemd dat je persoonsgegevens extend van de database-klasse.
Persoonsgegevens zijn immers geen database.
Je bent al gewend aan mysqli. Mysqli wordt standaard al als een class aangeleverd door PHP.
Mijn advies zou zijn deze class uit te breiden. Je kunt dan direct je vertrouwde mysqli methods gebruiken maar je kunt de class uitbreiden met custom methods mocht je daar ineens behoefte aan hebben. Leer je zelf overigens aan om classnames te beginnen met een hoofdletter.

<?php

class Database extends mysqli // <== You see?
{
public function __construct($host, $username, $passwd, $dbname) {
parent::__construct($host, $username, $passwd, $dbname);
}

/*
* custom methods
*/
}

$db = new Database('localhost', 'user', 'pass', 'database');
$result = $db->query('SELECT * FROM table');
// etc
?>

Daarnaast kun je de database class ook in andere classes gebruiken

<?php
class UserMapper
{
private $db;

public function __construct(Database $db) {
$this->db = $db;
}

public function getAllUsers() {
$result = $this->db->query("SELECT * FROM users");

if(!$result)
return NULL;

return $result->fetch_all(MYSQLI_ASSOC);
}
}

$db = new Database('localhost', 'user', 'pass', 'database');
$userMapper = new UserMapper($db);

$users = $userMapper->getAllUsers();

if($users) {
foreach($users as $user) {
// ...
}
}

?>
Wat mij aanvankelijk goed geholpen heeft, is het strikt scheiden van dingen de iets doen en dingen die iets zijn. Die scheiding ontbreekt in jouw klasse Persoonsgegevens, want die is iets én die doet iets. Zoals Ariën al aankaart, kun je het sleutelwoord "extends" vrij letterlijk lezen als "is een": "persoonsgegevens is een database" klopt niet, daar ontbreekt nog wat.

Wat je eigenlijk nodig hebt, is een klasse met persoonsgegevens (of een persoon) enerzijds en een tweede klasse die de persoonsgegevens opslaat. Ik gebruik daarvoor meestal een data mapper of een data access object.

Reageren