PHP MySQL-Update Array Bedingungen

unXtremo

Ensign
Registriert
Feb. 2009
Beiträge
228
Hallo zusammen,
ich bin momentan wieder an einem Webprojekt und wollte meine Gedanken etwas ordnen und ein paar Meinungen einholen. Der Titel dieses Threads ist leider nicht ganz aussagekräftig, ich wusste jedoch nicht genau wie ich diesen Fall beschreiben sollte.
Zunächst kurz was ich vor habe (Ausschnitt): Es soll über ein Formular (Selekt-Liste) angemeldeten Benutzern erlaubt werden sich in ein Feld einer Tabelle einzutragen, dazu ist in einer Datenbanktabelle ein Feld in dem sich die User eintragen, das Feld hängt von der Auswahl im Formular ab (logisch). So jetzt können sich je nach Feld unterschiedlich viele eintragen, dass ist über eine Kategorie in einer anderen Tabelle schon festgelegt. Es soll dann vor dem eintragen ermittelt werden ob man bereits in diesem Feld steht (Username, oder ID), dann soll man sich nicht eintragen können, und wenn bereits die max. Anzahl an Benutzern eingetragen ist, dann soll man sich auch nicht eintragen können.

So jetzt mein derzeitiger Entwicklungsstand:
  • Formular ist fertig und übergibt per POST die Werte (Benutzername oder ID)
  • Es wird überprüft ob man sich bereits in diesem Feld eingetragen hat, wenn nicht kann man sich eintragen

PHP:
// Prüfen ob man bereits bei diesem Ereignis eingeteilt ist
$result = mysql_query("SELECT e_id FROM ereignis WHERE user LIKE '%\" . $benutzername . \"%' ");
$menge = mysql_num_rows($result);

// Eintragen wenn man noch nicht eingeteilt ist
if($menge == 0)
{
         $sql = "UPDATE ereignis SET user= CONCAT(user,$benutzername)
		    WHERE datum=$mittwochs OR
		          	datum=$samstags OR
				datum=$sonntags OR
				datum=$sonder ";
		
	// Abfrage ausführen
	mysql_query($sql);
}
ach ja das mit dem datum= Mittwochs/Samstags/Sonntags/Sonder ist das Ereignis welches der Benutzer über das Formular auswählen kann, dabei wird aus dem Datum der Wochentag ermittelt und das ganze dann in unterschiedliche Kategorien geordnet, woraus sich auch die mögliche Anzahl an Usern ergibt.

Wo mache ich am besten die Überprüfung ob bereits die Anzahl der max. Benutzer erreicht ist?

Ich habe schon die Ermittlung der max. Anzahl über eine Abfrage mit INNER JOIN mit der Kategorie realisiert und kann diese mit der aktuellen Anzahl vergleichen:
Indem ich das Feld user als Array abfrage und mit explode am Komma auftrenne und dann die größe des Arrays mit count ermittle.

Bis jetzt funktioniert es auch noch nicht so richtig, dass man sich nur einmal pro Feld einteilen kann, ich glaube es liegt an dem kommaseparierten String.

Könntet ihr mir bitte helfen und mal schauen ob ich auf dem richtigen Weg bin, oder ob ich die Sache ganz anders angehen muss. Danke schon mal an alle, die sich bishierher durchgelesen haben ;)
 
Dein Ansatz ist ein bisschen ungeschickt. Ich würde dir empfehlen das Datenbankdesign nochmal zu überdenken. Lösungen die darauf hinaus laufen, dass man Daten in einem Feld komma-separiert speichert, sind problembehaftet (schwierig auszulesen, inperformant, problematisch wenn maximale Feld-Länge überschritten wird oder Werte selbst Trennzeichen enthalten, usw.)

Besser wäre es, wenn du jede individuelle Benutzer-Anmeldung als eigenständigen Datensatz in eine neue Tabelle (z.B. namens "anmeldung") schreibst, der per Fremdschlüssel-Feld das Ereignis in der Tabelle "ereignis" referenziert. Das ist genau das, was man unter einer "relationalen Datenbank" auch versteht :)

Nebenbei hoffe ich, dass du die Variabeln, die du im SQL-String verwendest vorher durch die Funktion mysql_real_escape_string() filterst, sonst machst du deine Webseite angreifbar für Hacker (SQL-Injection).
 
Hallo NeoTiger,
Danke für deine Antwort!!!
Das mit der SQL-Injektion weiß ich und habe ich nur vorerst weg gelassen um nicht schon in so einem frühen Stadium der Entwicklung zusätzliche Fehlerquellen einzubauen.
Das Prinzip der Relation habe ich ja bereits über die Benutzernamen und über die Kategorie, aber das mit dem Feld in das sich die Benutzer dem bestimmten Ereignis zuordnen gefällt mir ja ebenfalls nicht so sonderlich gut.
Also nochmal zum Verständnis, mein DB-Design würde dann so aussehen:

PHP:
/**
 * Benutzer
 */
CREATE TABLE user (
 u_id INT NOT NULL UNIQUE AUTO_INCREMENT,
 vorname VARCHAR(50) NOT NULL,
 nachname VARCHAR(50) NOT NULL,
 mail VARCHAR(50) NOT NULL,
 username VARCHAR(50) NOT NULL,
 password VARCHAR(50) NOT NULL,
 u_status BOOLEAN NOT NULL DEFAULT 1,
 PRIMARY KEY (u_id)
);

/**
 * Ereignis Kategorien
 */
CREATE TABLE kategorie (
 kategorie_nr INT NOT NULL UNIQUE AUTO_INCREMENT,
 kategorie_name VARCHAR(50) NOT NULL,
 kategorie_tag VARCHAR(50) NOT NULL,
 kategorie_ort VARCHAR(50) NOT NULL,
 kategorie_uhrzeit TIME NOT NULL,
 anzahl_user INT NOT NULL,
 PRIMARY KEY (kategorie_nr)
);

/**
 * Ereignisse
 */
CREATE TABLE ereignis (
 e_id INT NOT NULL UNIQUE AUTO_INCREMENT,
 datum DATE NOT NULL,
 kategorie INT NOT NULL,
 e_status BOOLEAN NOT NULL DEFAULT 0,	// 0 = nicht genug user, 1 = genug user
 PRIMARY KEY (e_id),
 FOREIGN KEY (kategorie)
 REFERENCES kategorie(kategorie_nr)
 ON DELETE CASCADE
);

/**
 * Orte
 */
CREATE TABLE orte (
 o_id INT NOT NULL UNIQUE AUTO_INCREMENT,
 o_name VARCHAR(50) NOT NULL,
 PRIMARY KEY (o_id)
);

/**
 * Anmeldung
 */
CREATE TABLE anmeldung (
 a_id INT NOT NULL UNIQUE AUTO_INCREMENT,
 user VARCHAR(50) NOT NULL,
 ereignis INT NOT NULL,
 PRIMARY KEY (a_id)
 FOREIGN KEY (user)
 REFERENCES user(username)
 FOREIGN KEY (ereignis)
 REFERENCES ereignis(e_id)
 ON DELETE CASCADE
);

Und wenn ich dann ein bestimmtes Ereignis abfrage muss ich dann ein SELECT * FROM anmeldung WHERE ereignis=?; machen.
Und wenn ich dann die Anzahl der user ermitteln möchte, die sich für dieses Ereignis angemeldet haben muss ich einfach die Anzahl der ausgegebenen Datensätze zählen, das ist ja dann nicht das Problem, diese Anzahl sollte ich dann am bessten auch als eine Spalte in "anmeldung" setzen und diese bei jedem neuen Datensatz um eins erhöhen, oder?

Habe ich das soweit richtig verstanden?
 
1. Die Anzahl der Teilnehmer würde ich nicht in der Tabelle Kategorie speichern sondern immer errechnen lassen. Wenn nicht, musst du sicherstellen, dass auch im Fehlerfall der Counter immer stimmt. Müsste die Kategorie nicht auch die maximale Nutzeranzahl enthalten, oder wo kommt die her?
2. Gleiches gilt für e_status in ereignis.
3. Die Tabelle orte scheint nicht genutzt zu werden?
4. In der Tabelle anmeldung hast du eine Referenz auf den Benutzernamen. Nimm lieber die BenutzerID (dafür ist die da). Sonst musst du, wenn sich ein Benutzer umbenennen will, auch daran denken, die Tabelle anmeldung zu ändern.
 
Hallo Darlis,
Danke für deinen Beitrag,
zu 1. ja vielleicht ist der Name nicht gut gewählt, aber im Feld anzahl_user wollte ich die Maximale Anzahl festlegen und die aktuelle Anzahl wusste ich ja wie in meinem vorherigen Betrag noch nicht ob ich die in die Tabelle anmeldung noch einbringe, du meinst also das ich das jedesmal neu errechnen soll.
und zu 2. wenn für das Ereignis bereits eine maximale Anzahl an Anmeldungen eingegangen ist ist es doch sinnvoll hier den Status zu ändern, damit ich dann eine Abfrage machen kann und mir alle Ereignisse anzeigen lassen kann, bei denen noch Anmeldungen frei sind.
3. Doch die Tabelle wird genutzt, das ist so jedoch nicht ersichtlich, ich könnte sie noch mit der Kategorie verknüpfen, sie füllt eine Auswahlliste im Formular, mit dem ich neue Kategorien anlegen kann.
4. Der hinweiß ist gut, ich war mir nämlich nicht sicher was ich am besten nehme Benutzernme oder ID beides sollte nämlich eindeutig sein aber dann werde ich besser die ID nehmen, danke für den Tipp.
 
unXtremo schrieb:
und zu 2. wenn für das Ereignis bereits eine maximale Anzahl an Anmeldungen eingegangen ist ist es doch sinnvoll hier den Status zu ändern, damit ich dann eine Abfrage machen kann und mir alle Ereignisse anzeigen lassen kann, bei denen noch Anmeldungen frei sind.
Kann man sicher so machen. Du natürlich sicherstellen, dass das Flag auch entsprechend aktualisiert wird. Meldet sich z.B. ein Benutzer (erfolgreich) ab und es gibt einen Fehler, bevor du das Flag geändert hast, enthält die Tabelle falsche Informationen. Bei kleineren Datenbanken ist es daher sicherer, sich den Status immer zu errechnen.
 
gut, ok dann werde ich doch auf e_status verzichten und mir immer die Anzahl errechnen und diese mit der max. Anzahl vergleichen, du hast recht der Aufwand ist nicht groß, und wenn ich damit einer Fehlerquelle entgehen kann werde ich das natürlich machen.
 
PHP:
if($menge == 0)
{
         $sql = "UPDATE ereignis SET user= CONCAT(user,$benutzername)
		    WHERE datum=$mittwochs OR
		          	datum=$samstags OR
				datum=$sonntags OR
				datum=$sonder ";
		
	// Abfrage ausführen
	mysql_query($sql);
}
aus sql sicht gibt es hier ein paar knackpunkt,
zu einem sind es externe eingaben daher müssen sie gequotet oder durch ein prepared statement gejagt werden.

und die datum abfrage liese sich schöner mit einem IN() releasieren:
"where datum IN($foo,$bar...)"
 
Hallo AlbertLast,
wie du oben sicherlich gelesen hast werde ich das UPDATE anders gestalten, da ich die Datenbankstruktur abändern werde. Ich werde somit zum anmelden nur noch ein INSERT brauchen, da ja jedes mal ein neuer Datensatz angelegt wird.
Aber dein Einwand ist doch trotzdem zum Verständnis und zum Lernen sehr interessant.
Also ich habe mir mal Prepared Statements durchgelesen, aber noch nicht 100%ig verstanden.

Das mit dem IN() habe ich verstanden, habe da bei der Entwicklung gar nicht mehr dran gedacht, aber das ist natürlich eleganter:
PHP:
$sql = "UPDATE ereignis SET user= CONCAT(user,$benutzername)
			WHERE datum IN($mittwochs,$samstags,$sonntags,$sonder) ";
Aber was ich hier mit dem Prepared Statement machen soll weiß ich jetzt noch nicht so ganz

Mit gequotet meinst du sicher den Aspekt mit SQL-Injection, den NeoTiger schon angesprochen hat.
 
ja escaped wäre hier bessere terminus gewesen,

bei normalen Qry schreibst du eins und schickst diesen an die db.
Beim Prepared passiert ein bissin mehr, hier bei wird nämlich zunächst das qry nur platzhalter an die Datenbank geschickt und dann erst im nächsten schrit die variablen.
Dadurch ist ein sql injection nicht mehr möglich weil das qry als solches schon in der Datenbank ist und nur noch die platzhalter entsprechende werte zu lassen.
 
Zurück
Oben