Hinweis vorweg:
Ich habe mir diese Klasse für kleinere private Projekte geschrieben, für die ich kein ORM nutze. Sie ist auf meine persönlichen Vorlieben beim Entwickeln abgestimmt. An einigen Stellen lässt sie den Kindklassen durch Sichtbarkeit protected (bzw. den Objekten einer Kindklasse durch Sichtbarkeit public) genug Spielraum um Funktionalitäten zu zerschießen.
Das ist mir bequßt und ich nehme das aus Gründen der Komfortabilität beim Coden in Kauf. Wer auch immer diese Klasse verwenden möchte muss selber darauf achten, beim direkten Schreiben von protected-Eigenschaften aus einer Kindklasse heraus die Werte angemessen zu prüfen.
Falls gewünscht kann ich bei Gelegenheit auch noch ein Beispiel zur Verwendung posten.
______________________________________________________
Bei der Mysqli_Entity handelt es sich um eine abstrakte Klasse, die Standardfunktionen wie getter, setter, load, save, delete für Klassen bereitstellt, die eine Entitäten-Tabelle abbilden.
Eine konkrete Klasse die von Mysqli_Entity erbt kann die nötigen Informationen für die Datenbank-Operationen (Mysqli-Objekt, Tabellenname, Felder, PrimaryKey-Informationen etc.) an die Elternklasse Mysqli_Entity übergeben und anfallende Datenbank-Operationen komfortabel an diese delegieren.
Sämtliche Werte werden von Mysqli_Entity vor dem Eintragen in die Datenbank mit real_escape_string behandelt, und nach dem Auslesen mit stripslashes.
Backticks `` um Tabellen- und Feldnamen werden von Mysqli_Entity ebenfalls automatisch gesetzt, sollten also bei Übergabe der entsprechenden Werte von der Tochterklasse nicht gesetzt werden.
Mysqli_Entity stellt folgende Eigenschaften und Methoden bereit (kompletter Code im zweiten Post):
abstract class Mysqli_Entity {
private $loadPropertyOnDemand = false;
private $autosave = false;
private $isUpdated = false;
protected $db = null;
protected $table = null;
protected $pkField = null;
protected $pkValue = false;
protected $properties = array();
protected $data = array();
protected function setup(mysqli $db,$table,$pkValue=false) {}
protected function addProperty($property) {}
protected function setAutosave($autosave=true) {}
protected function loadPropertyOnDemand($loadPropertyOnDemand=true) {}
protected function loadSchemeFromTable() {}
public function setPrimaryKey($value) {}
public function getProperty($property) {}
public function setProperty($property,$value) {}
public function load() {}
public function save() {}
public function delete() {}
public function __destruct() {}
private function checkTableInfo($checkPrimaryKey=true) {}
}
Alles anzeigen
Die Eigenschaften und Methoden im Einzelnen:
Eigenschaften:
private $loadPropertyOnDemand = false;
Mit dieser Eigenschaft wird festgelegt, ob der Inhalt einer property bei Anforderung automatisch geladen werden soll, wenn dieser noch nicht gesetzt ist.
Default ist false, dies kann über die Methode loadPropertyOnDemand() geändert werden.
private $autosave = false;
Mit dieser Eigenschaft wird festgelegt, ob Änderungen an Objekt-Eigenschaften beim zerstören des Objektes automatisch gespeichert werden sollen. Mit dieser Eigenschaft auf true braucht die save()-Methode also nicht mehr explizit aufgerufen werden.
Default ist false, dies kann über die Methode autosave() geändert werden.
private $isUpdated = false;
Diese Eigenschaft wird nur intern verwendet um beim Aufruf der save()-Methode zu prüfen, ob Eigenschaften geändert wurden und ein UPDATE der Tabellenzeile nötig ist.
protected $db = null;
In dieser Eigenschaft wird das Mysqli-Objekt abgelegt. Dies kann entweder über eine direkte Zuweisung in der Tochterklasse geschehen, oder als Parameter an die Methode setup() übergeben werden.
protected $table = null;
In dieser Eigenschaft wird der Name der Tabelle abgelegt, von der das Objekt eine Zeile abbilden soll. Kann entweder über eine direkte Zuweisung in der Tochterklasse geschehen, oder als Parameter an die Methode setup() übergeben werden.
protected $pkField = null;
In dieser Eigenschaft wird der Feldname des PrimaryKeys der Tabelle abgelegt. Kann entweder über eine direkte Zuweisung in der Tochterklasse geschehen, oder über die Methode loadSchemeFromTable() (bzw. über die Methode setup() in welcher loadSchemeFromTable() aufgerufen wird) automatisch ermittelt werden.
protected $pkValue = false;
In dieser Eigenschaft wird der PrimaryKey-Wert der konkreten Tabellenzeile hinterlegt, die von dem Objekt abgebildet wird. Kann entweder über eine direkte Zuweisung in der Tochterklasse geschehen, oder als (optionaler) Parameter an die Methode setup() übergeben werden, oder explizit über die Methode setPrimaryKey() gesetzt werden.
Wird der Wert in der Tochterklasse direkt zugewiesen, so ist zu beachten, dass sich die Tochterklasse vorher selber um ein ausreichendes escapen des Wertes kümmern muss, sofern dieser vom Client kommt.
protected $properties = array();
In dieser Eigenschaften werden die Feldnamen der Objekteigenschaften hinterlegt (nicht die Werte). Der Array kann automatisch über die Methoden loadSchemeFromTable() (bzw. indirekt über die Methode setup(), welche loadSchemeFromTable() aufruft) gefüllt werden.
Alternativ (wenn nicht alle Felder der Tabelle verfügbar sein sollen) kann er manuell über die Methode addProperty() gefüllt werden. Hierbei ist zu beachten, dass die übergebenen Properties exakt den Spaltennamen der Tabelle entsprechen müssen (ohne Backticks ``, diese werden von der Klasse Mysqli_Entity automatisch ergänzt).
protected $data = array();
In dieser Eigenschaften werden die Werte der Properties hinterlegt. Der Array kann entweder automatisch über Aufruf der Methode load() aus der Datenbank gefüllt werden (mit allen Properties die zu diesem Zeitpunkt gesetzt sind) oder manuell über die Methode setProperty($property,$value).
Methoden:
protected function setup(mysqli $db,$table,$pkValue=false) {}
Über diese Methode können alle relevanten Informationen für Datenbank-Operationen gesetzt werden. Der PrimaryKey $pkValue ist hierbei ein optionaler Parameter, um das Einfügen neuer Zeilen in die Tabelle zu ermöglichen.
Diese Methode ruft die Methode loadSchemeFromTable() auf, in welcher der properties-Array automatisch mit allen Feldern der übergebenen Tabelle gefüllt wird.
protected function addProperty($property) {}
Mit dieser Methode können Properties zum properties-Array hinzugefügt werden. Wird nicht benötigt wenn dieser automatisch über Aufruf der Methode loadSchemeFromTable() (bzw. setup()) gefüllt wird.
protected function setAutosave($autosave=true) {}
Setter-Methode für die private Eigenschaft $autosave.
protected function loadPropertyOnDemand($loadPropertyOnDemand=true) {}
Setter-Methode für die private Eigenschaft $loadPropertyOnDemand.
protected function loadSchemeFromTable() {}
Diese Methode holt sich das Tabellenschema von der Datenbank und befüllt den $properties-Array mit allen Feldern der Tabelle. Ermittelt und setzt außerdem den Namen des Feldes, das als PrimaryKey fungiert.
public function setPrimaryKey($value) {}
Über diese Methode kann der PrimaryKey-Wert gesetzt werden. Der übergebene Wert wird mir real_escape_string behandelt.
public function getProperty($property) {}
Liefert den Wert einer Property. Ist der Wert für die angeforderte Property noch nicht gesetzt und die Eigenschaft $loadPropertyOnDemand steht auf false, so wird eine Exception geschmissen.
public function setProperty($property,$value) {}
Mit dieser Methode kann man einer Property einen Wert zuweisen.
public function load() {}
Diese Methode läd alle Property-Werte für das aktuelle Objekt in den $data-Array.
public function save() {}
Diese Methode speichert alle getätigten Änderungen. Wenn die Eigenschaft $autosave auf true steht wird diese Methode in __destruct() automatisch aufgerufen.
public function delete() {}
Löscht das aktuelle Objekt aus der Datenbank.
public function __destruct() {}
Magische Methode, wird automatisch vor dem Zerstören des Objektes aufgerufen. Prüft ob $autosave und $isUpdated auf true stehen und speichert gegebenenfalls alle getätigten Änderungen.
private function checkTableInfo($checkPrimaryKey=true) {}
Interne Methode die vor jeder Datenbank-Operation prüft, ob alle relevanten Eigenschaften gesetzt sind ($db, $table, $pkField, gegebenenfalls $pkValue)
Kompletter Code:
abstract class Mysqli_Entity {
private $loadPropertyOnDemand = false;
private $autosave = false;
protected $db = null;
protected $table = null;
protected $pkField = null;
protected $pkValue = false;
protected $properties = array();
protected $data = array();
protected $isUpdated = false;
protected function setup(mysqli $db,$table,$pkValue=false) {
if(!is_string($table)) {
throw new Exception("\$table has to be of type string.");
}
$this->db = $db;
$this->table = $this->db->real_escape_string($table);
$this->loadSchemeFromTable();
if($pkValue!==false) {
$this->setPrimaryKey($pkValue);
}
}
protected function addProperty($property) {
if(!in_array($property,$this->properties)) {
$this->properties[] = $property;
}
}
protected function setAutosave($autosave=true) {
$this->autosave = $autosave;
}
protected function loadPropertyOnDemand($loadPropertyOnDemand=true) {
$this->loadPropertyOnDemand = $loadPropertyOnDemand;
}
protected function loadSchemeFromTable() {
$this->checkTableInfo(false);
if(count($this->properties)) {
$this->properties = array();
}
$pkCount = 0;
$sql = "DESCRIBE `{$this->table}`";
$res = $this->db->query($sql);
while($row = $res->fetch_assoc()) {
$this->properties[] = $row['Field'];
if($row['Key']==="PRI") {
if($pkCount>0) {
throw new Exception("Class Mysqli_Entity does not support tables with composite primary keys.");
}
$this->pkField = $row['Field'];
$pkCount++;
}
}
if($pkCount===0) {
throw new Exception("No Primary Key defined for table `{$this->table}`");
}
}
public function setPrimaryKey($value) {
$this->pkValue = (int)$value;
}
public function getProperty($property) {
if(!in_array($property, $this->properties)) {
throw new Exception("Property {$property} does not exist.");
}
if(!array_key_exists($property,$this->data) && $this->loadPropertyOnDemand===false) {
throw new Exception("No value available for Property {$property}.",10);
}
elseif(!array_key_exists($property,$this->data) && $this->loadPropertyOnDemand!==false) {
$this->checkTableInfo();
$sql = "SELECT `{$property}` FROM `{$this->table}` WHERE `{$this->pkField}`='{$this->pkValue}'";
$res = $this->db->query($sql);
$row = $res->fetch_row();
if($row===null) {
throw new Exception("Property {$property} could not be retrieved from database.",20);
}
$res->close();
unset($res);
$this->data[$property] = stripslashes($row[0]);
unset($row);
}
return $this->data[$property];
}
public function setProperty($property,$value) {
if(!in_array($property,$this->properties)) {
throw new Exception("Property {$property} does not exist.");
}
elseif(!isset($this->data[$property]) || $value!=$this->data[$property]) {
$this->data[$property] = $value;
$this->isUpdated = true;
}
}
public function load() {
$this->checkTableInfo();
if(count($this->properties)===0) {
throw new Exception("No properties defined.");
}
$properties = implode("`,`",$this->properties);
$sql = "SELECT `{$properties}` FROM `{$this->table}` WHERE `{$this->pkField}`='{$this->pkValue}'";
$res = $this->db->query($sql);
$row = $res->fetch_assoc();
if($row===null) {
throw new Exception("Entity not found");
}
foreach($row as $key=>$value) {
$this->data[$key] = stripslashes($value);
}
$res->close();
unset($res);
unset($row);
return $this->data;
}
public function save() {
if($this->isUpdated===false) {
return;
}
$this->checkTableInfo(false);
foreach($this->data as $key=>$value) {
$value = $this->db->real_escape_string($value);
$data[] = "`{$key}`='{$value}'";
}
$data = implode(",",$data);
if($this->pkValue!==false) {
$sql = "UPDATE `{$this->table}` SET {$data} WHERE `{$this->pkField}`='{$this->pkValue}'";
}
else {
$sql = "INSERT INTO `{$this->table}` SET {$data}";
}
$this->db->query($sql);
if(!$this->db->affected_rows) {
throw new Exception("Object could not be saved.");
}
$this->isUpdated = false;
if($this->pkValue===false && $this->db->insert_id) {
$this->pkValue = $this->db->insert_id;
return $this->pkValue;
}
return true;
}
public function delete() {
$this->checkTableInfo();
$sql = "DELETE FROM `{$this->table}` WHERE `{$this->pkField}`='{$this->pkValue}'";
$this->db->query($sql);
return $this->db->affected_rows;
}
private function checkTableInfo($checkPrimaryKey=true) {
if(!$this->db || !($this->db instanceof mysqli)) {
throw new Exception("Database-Object is not defined or not of type mysqli.");
}
elseif(!is_string($this->table)) {
throw new Exception("No table defined.");
}
if($checkPrimaryKey!==false) {
if(!is_string($this->pkField) || $this->pkValue===null) {
throw new Exception("Primary Key is not defined.");
}
}
}
public function __destruct() {
if($this->autosave && $this->isUpdated) {
$this->save();
}
}
}
Alles anzeigen