PHP: HTML-Formular -- Felder und Regeln teilen
User- und Form-Modell sind sich ähnlich. Um die Wiederholung von Programmcode zu vermeiden sollen sie sich gemeinsame Felder und Regeln teilen und diese um eigene Felder erweitern.
User- und Form-Modell:
- Teilen sich gleiche Felder mit demselben Inhalt
- Besitzen gleichnamige Felder mit unterschiedlichen Inhalt
- Besitzen Felder unabhängig voneinander
User-Modell Form-Modell ------------------------------------ id login login password // hash password // raw passwordRepeat salt favLanguages favLanguages isComputerfreak isComputerfreak comment comment lastLogin preview register PHPSESSION
DRY - Don't repeat yourself
Gleiche Felder mit gleichen Inhalt sollten nur einmal deklariert werden und dann von beiden Modellen benutzt werden.
- Gemeinsame Felder an zentralen Ort deklarieren
- Ohne gleichnamige Felder mit unterschiedlichen Inhalt
- Mit gleichnamigen Feldern mit unterschiedlichen Inhalt
- Gemeinsame Felder übernehmen
- Gemeinsame Felder mit unterschiedlichen Inhalt überschreiben
- Gemeinsame Felder mit unterschiedlichen Inhalt modifizieren
- Eigene Felder hinzufügen
Zentral
In oop_share sind die gemeinsamen Felder an einer Stelle deklariert. User- und Form-Modell übernehmen diese Felder, fügen die eigenen hinzu und überschreiben die gleichnamige Felder mit unterschiedlichen Inhalten.
Nachteil: Im Quelltext ist nur schwer erkennbar welche Felder für eine Klasse deklariert sind, insbesonders für das User-Modell ist dies vom Nachteil.
$userShare = [ // Field Name Value // ------------------------------------------------------ ['Text', 'login', '', ['displayName' => 'Login', 'size' => 10], // Options ['maxLength' => 8, 'match' => '/^[a-z]+$/']], // Rules ['Password', 'password', '', ['displayName' => 'Password', 'size' => 10], ['minLength' => 8]], ['Select', 'favLanguages', [], ['displayName' => 'Favourite languages', 'options' => $favLanguages, 'size' => 5, 'multiple' => true], ['sanitizeOptions' => $favLanguages]], ['Textarea', 'comment', '', ['displayName' => 'Comment', 'rows' => 5, 'cols' => 40], ['convertEndOfLine' => "n"]] ]; // Classes class UserModel extends BaseModel { public function __construct() { parent::__construct($GLOBALS['userShare'], [ ['Integer', 'id', '', [], ['minValue' => 1, 'maxValue' => 1000]], ['Password', 'password', '', // Overwrites password ['overwrite' => true], ['length' => 32, 'match' => '/^[a-z0-9]{32}$/']], ['Bool', 'isComputerfreak', true, ['displayName' => 'Computerfreak'], ['bool' => [true, false]]] ]); }//__construct
User-Modell stellt Felder zur Verfügung
In oop_share_provide_fields deklariert das User-Modell alle eigenen Felder und stellt die gemeinsamen dem Form-Modell zur Verfügung.
Vorteil: Gemeinsame Felder sind nur einmal deklariert. Für die User-Klasse ist leicht ersichtlicht welche Felder sie besitzt. Änderungen am User-Modell werden automatisch ans Form-Modell weitergegeben.
Nachteil: Für das Form-Modell ist nicht direkt im Quelltext erkennbar welche Felder es besitzt.
class UserModel extends BaseModel { public function __construct() { parent::__construct([ // Field Name Value // ------------------------------------------------------ ['Integer', 'id', '', [], // Options ['minValue' => 1, 'maxValue' => 1000]], // Rules ['Text', 'login', '', ['displayName' => 'Login', 'size' => 10], ['maxLength' => 8, 'match' => '/^[a-z]+$/']], [...] /** * Provide fields for other Classes */ public function getFields($for) { switch($for) { case 'form': return [clone $this->fields['login'], clone $this->fields['favLanguages'], clone $this->fields['comment'], ]; break;
$form = new FormModel('index.php', 'post', $user, [ ['Password', 'password', '', ['overwrite' => true, 'displayName' => 'Password', 'size' => 10], ['minLength' => 8, 'isEqualWith' => 'passwordRepeat']], ['Password' , 'passwordRepeat', '', ['displayName' => 'Password repeat', 'size' => 10], []], [...]
Form-Modell holt sich Felder
In oop_share_get_fields deklariert das User-Modell wieder alle eigenen Felder. Das Form-Modell holt sich nun aber die gemeinsamen Felder selbst vom User-Modell und ergänzt die eigenen.
Vorteil: Gemeinsame Felder sind nur einmal deklariert und es ist für beide Klassen leicht im Quelltext sichtbar welche Felder sie besitzen.
Nachteil: Wenn beim User-Modell Felder hinzugefügt, entfernt oder verändert werden muss das Form-Modell von Hand ans User-Modell angepaßt werden.
class UserModel extends BaseModel { public function __construct() { parent::__construct([ // Field Name Value // ------------------------------------------------------ ['Integer', 'id', '', [], // Options ['minValue' => 1, 'maxValue' => 1000]], // Rules ['Text', 'login', '', ['displayName' => 'Login', 'size' => 10], ['maxLength' => 8, 'match' => '/^[a-z]+$/']], ['Password', 'password', '', [], ['length' => 32, 'match' => '/^[a-z0-9]{32}$/']], [...]
$form = new FormModel('index.php', 'post', $user, [ // ['login', $user], [BaseModel::getFieldFrom('login', $user)], ['Password', 'password', '', ['overwrite' => true, 'displayName' => 'Password', 'size' => 10], ['minLength' => 8, 'isEqualWith' => 'passwordRepeat']], ['Password' , 'passwordRepeat', '', ['displayName' => 'Password repeat', 'size' => 10], []], [BaseModel::getFieldFrom('favLanguages', $user)], [...]
Felder von zentraler Stelle holen
In einer nicht implementierten Variante könnten sich sowohl das User- als auch Form-Modell die Felder von einer zentralen Stelle holen.
Vorteil: Eine Reihe von Klassen können sich verschiedene Felder in beliebiger Kombination teilen.
Nachteil: Abhängige Änderungen müssen in allen Klassen von Hand nachgeführt werden.
class UserModel extends BaseModel { public function __construct() { parent::__construct([ // Field Name Value // ------------------------------------------------------ ['Integer', 'id', '', [], // Options ['minValue' => 1, 'maxValue' => 1000]], // Rules ['login'], ['Password', 'password', '', [], ['length' => 32, 'match' => '/^[a-z0-9]{32}$/']], ['favLanguages'], [...] ['comment'], [...]
Form-Modell holt und setzt Werte mit einem Accessor
oop_share_accessor
ist eine Erweiterung von oop_share_provide_fields
in der
das
User-Modell
zusätzlich jeweils einen Accessor zum Lesen und Setzen der Werte bereitstellt. Das
Form-Modell
kann nun selbst aktiv die Werte aus dem User-Modell holen und setzen.
Der Accessor
schränkt dazu den Zugriff auf die Properties des Modells ein
und übernimmt auch die
Konvertierung der Werte,
so dass das Passwort im User-Modell als MD5-Hash und nicht im Klartext gespeichert wird.
class UserModel extends BaseModel { private $accessors = []; public function __construct() { [...] $this->accessors['form_register_read'] = new Accessor($this, ['login', 'favLanguages', 'isComputerfreak', 'comment'], // read []); // write $this->accessors['form_register_write'] = new FormRegisterAccessor($this, [], ['login', 'password', 'favLanguages', 'isComputerfreak', 'comment']); //['login', 'password' => function($value) {return md5($value);}, 'favLanguages', 'comment']); }//__construct
class FormModel extends BaseModel { private $action = ''; private $method = 'get'; private $model = null; [...] public function process() { // Get values from model $model = $this->model->getAccessor('form_register_read'); foreach($model as $key => $value) { $this->$key = $value; } [...] $this->processRules(); // Set values in model if($this->isValid) { $model = $this->model->getAccessor('form_register_write'); foreach($model as $key => $value) { $model->$key = $this->$key; } } }// process
class FormRegisterAccessor extends Accessor { public function __construct($obj, $read = [], $write = [], $readwrite = []) { parent::__construct($obj, $read, $write, $readwrite); }// __construct public function __set($name, $value) { switch($name) { case 'password'; $value = md5($value); break; default: break; } parent::__set($name, $value); }// __set }// FormRegisterAccessor
user- und devErrors[]
Statt errros[]
gibt es nun userErrors[]
und
devErrors[]
. Dadurch können die Fehlermeldungen für Entwickler den field
-
statt displayName
und zusätzliche Informationen enthalten.
CheckboxField nur true und false
Das CheckboxField
ist so geändert, dass es nur noch
true
und false
als Wert haben kann. Dadurch kann der Wert
ohne Konvertierung auf das BoolField
des User-Modells abgebildet werden.