Allgemein
Dieser Artikel behandelt die einfache / bequeme Erstellung einer Datensatz-Suche innerhalb einer MySQL Datenbank. Er soll aufzeigen, wie es möglich ist ohne tiefgehende Kenntnisse von HTML Formularen oder der MySQL Syntax, sondern lediglich durch die Konfiguration einer Klasse, eine Datenbank-Suche auszuführen.
Alle Klassen-, Funktions-, und Variablennamen wurden frei von mir gewählt, ohne mich an allgemeine Konventionen (wie sie auch in diesem wiki beschrieben werden) zu halten.
Grundsätzliche Ideen
Wie funktioniert eine Suche üblicherweise?! Vom Prinzip her relativ simpel: wir erstellen ein Formular, geben eine Beschreibung für die zu durchsuchende Spalte an, zeigen ein text Formular Element für Eingaben und einen Button zum Ausführen der Suche.
Der HTML Teil grob skizziert:
Code:
<form action="suche.php">
<label for="benutzername">Benutzername:</label> <input type="text" name="benutzername">
<input type="submit">
</form>
Der zugehörige Code könnte folgendermaßen aussehen:
PHP Quellcode:
// schreibe query zum auslesen der daten
$query = "SELECT * FROM benutzer";
// wurde eine suche abgeschickt?
if ( isset( $_POST['submit'] ) )
// hänge die suche an die query an
$query .= " WHERE benutzername = '" . mysql_real_escape_string( $_POST['benutzername'] ) . "'";
// query abschicken
$result = mysql_query( $query );
// ausgabe tätigen
// ...
Soweit so gut: wir können nun einen Benutzernamen mit Hilfe eines Formulars in der Datenbank Tabelle "benutzer" suchen.
Problematik
Was passiert aber, wenn wir den genauen Namen des Benutzers nicht mehr kennen? Wenn wir also anstatt "combie" nur noch wissen, dass der Name mit "com" beginnt. Wir bräuchten also eine Vorauswahl für die Suche: "ist", "enthält", "beginnt mit" etc
Es existieren aber auch weitere Daten-Typen zb Integer oder Date. Für andere Typen bräuchten wir auch eine andere Vorauswahl. Für Integer Werte zusätzlich "ist kleiner" oder "ist größer" und für Date Werte zusätzlich "innerhalb" oder "nicht innerhalb". Eine ID wäre ein Integer Wert und das Datum der Registrierung eine Date Wert.
Ein Spezialfall wären zb noch Kategorien oder Gruppen. Man bekommt eine Auswahl aller Gruppen ("Gast", "Benutzer", "Admin") und möchte alle Benutzer, die der Gruppe "Admin" angehören, auslesen.
Und zu guter Letzt: was, wenn wir mehrere Spalten durchsuchen möchten? Bisher müssen wir jedes Formular Element per Hand definieren, prüfen und auswerten.
Anforderungen
Idealerweise steht uns eine Klasse - eine Blackbox - zur Verfügung, der wir einfach nur mitteilen, welche Spalten wir durchsuchen möchten. Zusätzlich übergeben wir weitere Infos über Beschreibung und Typ. Als Rückgabe erwarten wir erstens das komplette Formular und zweitens die Teil-query, die wir an unsere query einfach anhängen. Optional ist eine Auswahl der logischen Verknüpfung OR oder AND im Formular möglich.
Ein vollständiges Beispiel:
PHP Quellcode:
// instanz erstellen
$obj = new mysql_suche
();
// string suche
$obj->append_element( "a.benutzer_name", "Benutzer Name", "string" );
// string suche aus anderer tabelle (per join verbunden)
$obj->append_element( "b.vorname", "Vorname", "string" );
// integer suche
$obj->append_element( "a.id", "ID", "integer" );
// volltext suche
$obj->append_element( "a.text", "Text", "text" );
// datum suche - abgespeichet als date
$obj->append_element( "a.registriert_am", "Registriert am", "date" );
// datum suche - abgespeichert als timestamp
$obj->append_element( "a.registriert_am", "Registriert am", "date_timestamp" );
// gruppen suche
$obj->append_element( "a.gruppen_id", "Gruppe", "array", array( 1 => "Benutzer", 2 => "Moderator", 3 => "Admin" ) );
// hole formular
$form = $obj->get_form();
// hole sql where append
$where = $obj->get_where_append();
// erzeuge query
$query = "SELECT a.*, b.* FROM benutzer a " .
"LEFT JOIN benutzer_details b ON a.id = b.benutzer_id ";
// hänge where append and
$query .= "WHERE " . $where;
// schicke query ab
$result = mysql_query( $query );
// gib suchformular aus
echo $form;
// gib gefundene elemente aus
while ( $element = mysql_fetch_array( $result ) ) { }
Klasse
Folgend eine Variante einer Implementierung der gewünschten Anforderungen.
suche_interface
PHP Quellcode:
interface suche_interface
{
public function append_element
( $sql_column, $description, $type = "string", $params = array() );
public function get_form
();
public function get_where_append
();
public function set_destination_url
( $url );
}
mysql_suche
PHP Quellcode:
class mysql_suche implements suche_interface
{
// wohin soll die form action führen
private $_destination_url = null;
// alle elemente
private $_elements = array();
// wird die suche gerade ausgeführt?
private $_active = false;
// $_POST
public $post = array();
public function __construct
()
{
// speicher $_POST im objekt
$this->post =& $_POST;
}
private function _get_class
( $type )
{
// hole klassenname des typs
return "mysql_suche_element_" . $type;
}
public function set_desctination_url
( $url )
{
// speicher im objekt
return $this->_destination_url
= $url;
}
public function append_element
( $sql_column, $description, $type = "string", $params = array() )
{
// typ unterstüzt?
if ( !class_exists( $this->_get_class
( $type ) ) )
// abbruch
die( "typ nicht unterstüzt" );
// element speichern
return array_push( $this->_elements
, array( 'sql' => $sql_column, 'description' => $description, 'type' => $type, 'params' => $params ) );
}
public function get_where_append
()
{
// suche abgeschickt?
if ( !isset( $this->post['submit'] ) )
{
$this->_active
= false;
// schicke standard zurück
return "1";
}
// ausgabe array
$return = array();
// gehe alle such elemente durch
foreach ( $this->_elements
as $element )
{
// klassenname setzen
$class = $this->_get_class
( $element['type'] );
// hole aus objekt
$obj = new $class;
$obj->set_params( $element );
$where = $obj->get_where_append();
// where part gültig?
if ( !empty( $where ) )
// hinzufügen
array_push( $return, $where );
// objekt freigeben
$obj = null;
unset( $obj );
}
// überhaupt ein element angegeben?
if ( count( $return ) > 0 )
return "1";
// suche wird ausgeführt
$this->_active
= true;
// gib where append zurück
return " ( " . implode( " ) AND ( ", $return ) . " ) ";
}
public function get_form
()
{
// suche bereits ausgeführt?
if ( $this->_active
== true )
// standard zurück geben
return "Die Suche wird gerade ausgeführt.";
// ausgabe string
$str = "<form action='" . $this->_destination_url
. "' method='POST'>";
// gehe alle elemente durch
foreach ( $this->_elements
as $element )
{
// hole formular aus klasse
$class = $this->_get_class
( $element['type'] );
$obj = new $class();
$str .= $obj->get_form();
$obj = null;
unset( $obj );
}
// ausführen button
$str .= "<input type'submit' name='submit'>";
// formular schließen
$str .= "</form>";
// formular zurückgeben
return $str;
}
}
mysql_suche_element_interface
PHP Quellcode:
interface mysql_suche_element_interface
{
public function get_form
();
public function get_where_append
();
public function set_params
( array $params );
}
mysql_suche_element
PHP Quellcode:
abstract
class mysql_suche_element implements mysql_suche_element_interface
{
// vorauswahl möglichkeiten
protected $_cf = array();
// attribute
protected $_params = array();
// $_POST
public $post = array();
public function __construct
()
{
// speicher $_POST im objekt
$this->post =& $_POST;
}
abstract
protected function _get_value
();
public function get_form
()
{
// beschreibung
$str = $this->_params
['description'];
// vorauswahl
$str .= $this->_get_preselection
();
// formular element für eingabe
$str .= $this->_get_form_input
( $this->params['sql'] );
// form zurück geben
return $str;
}
protected function _check_post
()
{
// elemente in $_POST vorhanden?
return ( ( !isset( $this->post[$this->params['sql']] ) ) or
( !isset( $this->post[$this->params['sql'] . '_pre'] ) ) );
}
// nur gültig für typen mit preselection und einem eingabefeld.
// also gültig für integer/string, aber nicht für date
public function get_where_append
()
{
// prüfe post variablen
if ( $this->_check_post
() == false )
// fehler
die( "fehler" );
// element ausgefüllt?
if ( empty( $this->post[$sql] ) )
// gib fehler zurück
return false;
// sql holen
$sql = $this->params['sql'];
// vorauswahl holen
$pre = $this->post[$sql . '_pre'];
// post holen
$value = $this->post[$sql];
// query zusammenführen
$str = " ( " . $sql . " " . $this->cf[$pre]['pre'] . $this->_get_value
() . $this->cf[$pre]['post'] . " ) ";
// und zurück geben
return $str;
}
public function set_params
( array $params )
{
// speicher element attribute im objekt
return $this->_params
= $params;
}
protected function _get_preselection
()
{
// tmp array
$arr = array();
// kompatibel zu form_select() machen
foreach ( $this->_cf
as $key => $value )
$arr[$key] = $value['desc'];
// select zurück geben
return $this->_get_form_select
( $this->params['sql'] . "_pre", $arr );
}
protected function _get_form_select
( $name, $values )
{
// select element beginnen
$str = "<select name='" . $name . "'>";
// leere zeile hinzufügen
$str .= "<option value'0'> </option>";
// alle vorauswahl elemente durchgehen
foreach ( $values as $key => $value )
// option erzeugen
$str .= "<option value'" . $key . "'>" . $value . "</option>";
// element schließen
$str .= "</select>";
// und zurück geben
return $str;
}
protected function _get_form_input
( $name )
{
// eingabefeld zurückgeben
return "<input type='text' name'" . $name . "'>";
}
}
Kindklassen
Im folgenden ein paar Kindklassen von mysql_suche_element bzw ein paar Implementierungen verschiedener Typen.
mysql_suche_element_string
PHP Quellcode:
final
class mysql_suche_element_string
extends mysql_suche_element
{
// vorauswahl möglichkeiten
protected $_cf = array( 1 => array( 'desc' => "ist", 'pre' => " = '", 'post' => "'" ),
2 => array( 'desc' => "ist nicht", 'pre' => " != '", 'post' => "'" ),
2 => array( 'desc' => "enthält", 'pre' => " LIKE '%", 'post' => "%'" ),
3 => array( 'desc' => "beginnt mit", 'pre' => " LIKE '%", 'post' => "'" ),
4 => array( 'desc' => "endet mit", 'pre' => " LIKE '", 'post' => "%'" ) );
protected function _get_value
()
{
// post holen
$value = $this->post[$this->params['sql']];
// wert zurück geben
return mysql_real_escape_string( $value );
}
}
mysql_suche_element_integer
PHP Quellcode:
final
class mysql_suche_element_integer
extends mysql_suche_element
{
// vorauswahl möglichkeiten
protected $_cf = array( 1 => array( 'desc' => "ist", 'pre' => " = ", 'post' => "" ),
2 => array( 'desc' => "ist nicht", 'pre' => " != ", 'post' => "" ),
3 => array( 'desc' => "ist größer", 'pre' => " > ", 'post' => "" ),
4 => array( 'desc' => "ist größer gleich", 'pre' => " >= ", 'post' => "" ),
5 => array( 'desc' => "ist kleiner", 'pre' => " < ", 'post' => "" ),
6 => array( 'desc' => "ist kleiner gleich", 'pre' => " <= ", 'post' => "" ) );
protected function _get_value
()
{
// post holen
$value = $this->post[$this->params['sql']];
// auf integer casten
return (int
) $value;
}
}
mysql_suche_element_array
PHP Quellcode:
final
class mysql_suche_element_array
extends mysql_suche_element
{
// vorauswahl möglichkeiten
protected $_cf = array( 1 => array( 'desc' => "ist", 'pre' => " = ", 'post' => "" ),
2 => array( 'desc' => "ist nicht", 'pre' => " != ", 'post' => "" ) );
protected function _get_value
()
{
// post holen
$value = $this->post[$this->params['sql']];
// auf integer casten
return (int
) $value;
}
protected function _get_form_input
( $name )
{
// auswahl holen
$arr = $this->params['params'];
// leerzeile hinzufügen
$arr = array_merge( array( 0 => "" ), $arr );
// gib select zurück
return $this->get_form_select( $this->params['sql'], $arr );
}
}