mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-06 00:04:07 +01:00
431 lines
15 KiB
Plaintext
431 lines
15 KiB
Plaintext
Erstellen des Models
|
|
====================
|
|
|
|
Bevor man sich an das HTML für ein Formular macht, sollte man sich überlegen,
|
|
welche Daten überhaupt erfasst werden sollen und welchen Regeln diese Daten
|
|
entsprechen müssen. Die nötigen Informationen dazu können in einer Modelklasse
|
|
festgehalten werden. Wie im Kapitel [Model](/doc/guide/basics.model)
|
|
beschrieben, ist ein Model ja der zentrale Ort zur Speicherung und Validierung
|
|
von eingegebenen Daten.
|
|
|
|
Es stehen zwei Modeltypen zur Auswahl, je nachdem, wie die eingegebenen Daten
|
|
weiterverwendet werden sollen. Geht es lediglich darum, Daten zu erfassen, zu
|
|
verabeiten und dann wieder zu verwerfen, bietet sich ein
|
|
[Formularmodel](/doc/guide/basics.model) an. Sollen die Daten hingegen in einer
|
|
Datenbank gespeichert werden, ist ein [ActiveRecord](/doc/guide/database.ar)
|
|
die bessere Wahl. Beide Modeltypen stammen von der selben Basisklasse [CModel]
|
|
ab. In ihr sind die Teile der Schnittstelle definiert, die beide Typen
|
|
gemeinsam haben.
|
|
|
|
> Note|Hinweis: Obwohl in den Beispielen dieses Abschnitts hauptsächlich
|
|
Formularmodels vorkommen, kann man stattdessen genauso auch
|
|
[ActiveRecords](/doc/guide/database.ar) verwenden. Die Verfahren sind bei
|
|
beiden die selben.
|
|
|
|
|
|
Definieren der Modelklasse
|
|
---------------------------
|
|
|
|
Im folgenden Beispiel erzeugen wir die Modelklasse `LoginForm` für die Daten
|
|
einer Anmeldeseite. Da diese Daten nur zum Authenifizieren eines Benutzers
|
|
verwendet werden und ansonsten nicht gespeichert werden müssen, verwenden wir
|
|
ein Formularmodel.
|
|
|
|
~~~
|
|
[php]
|
|
class LoginForm extends CFormModel
|
|
{
|
|
public $username;
|
|
public $password;
|
|
public $rememberMe=false;
|
|
}
|
|
~~~
|
|
|
|
`LoginForm` hat die drei Attribute `$username`, `$password` und `$rememberMe`.
|
|
Darin werden entpsrechend der Benutzername, das Passwort und die Option
|
|
"Angemeldet bleiben" (engl.: Remember me) gespeichert. Da `$rememberMe`
|
|
bereits den Startwert `false` hat, wird die entsprechende Option beim ersten
|
|
Anzeigen des Anmeldeformulars nicht markiert werden.
|
|
|
|
> Info|Info: Für Modeleigenschaften verwenden wir den Begriff *Attribute*,
|
|
um sie von normalen Klasseneigenschaften zu unterscheiden. Attribute dienen
|
|
also hautpsächlich dazu, Benutzerdaten oder Datenbankwerte abzulegen.
|
|
|
|
|
|
Bestimmen der Validierungsregeln
|
|
--------------------------------
|
|
|
|
Wenn ein Besucher das Formular abschickt und die Daten in das Model übernommen
|
|
werden sollen, müssen diese vorher überprüft werden. Das geschieht bei der
|
|
sog. Validierung (engl.: validation) anhand einer Reihe von Regeln. Diese
|
|
Regeln werden in der Modelmethode `rules()` festgelegt und in Form eines
|
|
Arrays zurückgegeben:
|
|
|
|
~~~
|
|
[php]
|
|
class LoginForm extends CFormModel
|
|
{
|
|
public $username;
|
|
public $password;
|
|
public $rememberMe=false;
|
|
|
|
private $_identity;
|
|
|
|
public function rules()
|
|
{
|
|
return array(
|
|
array('username, password', 'required'),
|
|
array('rememberMe', 'boolean'),
|
|
array('password', 'authenticate'),
|
|
);
|
|
}
|
|
|
|
public function authenticate($attribute,$params)
|
|
{
|
|
$this->_identity=new UserIdentity($this->username,$this->password);
|
|
if(!$this->_identity->authenticate())
|
|
$this->addError('password','Incorrect username or password.');
|
|
}
|
|
}
|
|
~~~
|
|
|
|
Mit dieser `rules()`-Methode wird bestimmt, dass `username` und `password`
|
|
zwingend ausgefüllt sein müssen (engl.: required) und `rememberMe` vom Typ
|
|
Boolean sein muss. Außerdem soll `password` mit der Methode `authenticate`
|
|
geprüft werden, wo die eigentliche Anmeldung erfolgt.
|
|
|
|
Jede der Regeln im zurückgegebenen Array muss folgendem Format entsprechen:
|
|
|
|
~~~
|
|
[php]
|
|
array('AttributListe', 'Validator', 'on'=>'SzenarienListe', ...Zusätzliche Optionen)
|
|
~~~
|
|
|
|
`AttributListe` enthält eine Reihe von Attributnamen (mit Komma getrennt), für
|
|
die diese Regel gelten soll. `Validator` (sinngem.: Gültigkeitsprüfer) gibt an,
|
|
welche Art der Prüfung durchgeführt werden soll. Der `on` Parameter ist optional
|
|
und gibt die Szenarien an, in denen diese Regel überhaupt verwendet werden
|
|
soll. `Zusätzliche Optionen` können als Name-Wert-Paare angegeben werden um
|
|
weitere Validatoreigenschaften zu konfigurieren.
|
|
|
|
Es gibt drei Varianten, wofür `Validator` stehen kann. Erstens kann es der
|
|
Name einer Methode innerhalb der aktuellen Modelklasse sein, wie
|
|
`authenticate` im obigen Beispiel. Diese Methode muss folgende Signatur
|
|
aufweisen:
|
|
|
|
~~~
|
|
[php]
|
|
/**
|
|
* @param string der Name des Attributs, das geprüft werden soll
|
|
* @param array Optionen der Prüfregel
|
|
*/
|
|
public function ValidatorName($attribute,$params) { ... }
|
|
~~~
|
|
|
|
Zweitens kann `Validator` der Name einer ganzen Validatorklasse sein. Bei der
|
|
Validierung wird dann eine Instanz dieser Klasse erzeugt, die die eigentliche
|
|
Prüfung durchführt. Über die zusätzlichen Optionen können die
|
|
Objekteigenschaften dieser Instanz konfiguriert werden. Eine solche Klasse
|
|
muss von [CValidator] abgeleitet werden.
|
|
|
|
Drittens kann `Validator` einem von mehreren vorgegebenen Aliasen entsprechen.
|
|
Im obigen Beispiel ist der Name `required` ein Alias für [CRequiredValidator].
|
|
Dieser stellt sicher, dass der zu prüfende Attributwert nicht leer ist.
|
|
|
|
Hier eine Übersicht aller verwendbaren Aliase:
|
|
|
|
- `boolean`: Alias für [CBooleanValidator]; prüft, ob der
|
|
Attributwert [CBooleanValidator::trueValue] oder [CBooleanValidator::falseValue] ist.
|
|
|
|
- `captcha`: Alias für [CCaptchaValidator]; prüft, ob der Attributwert mit dem
|
|
angezeigten [CAPTCHA](http://de.wikipedia.org/wiki/Captcha)-Code übereinstimmt.
|
|
|
|
- `compare`: Alias für [CCompareValidator]; prüft, ob das Attribut mit einem anderen
|
|
Attribut oder einer Konstanten übereinstimmt.
|
|
|
|
- `email`: Alias für [CEmailValidator]; prüft auf eine gültige E-Mail Adresse.
|
|
|
|
- `default`: Alias für [CDefaultValueValidator]; weist dem Attribut einen
|
|
Standardwert zu.
|
|
|
|
- `exist`: Alias für [CExistValidator]; stellt sicher, dass der Attributwert in
|
|
einer bestimmten Tabellenspalte existiert
|
|
|
|
- `file`: Alias für [CFileValidator]; stellt sicher, dass das Attribut den Namen einer
|
|
hochgeladenen Datei enthält.
|
|
|
|
- `filter`: Alias für [CFilterValidator]; wandelt den Attributwert mit
|
|
einem Filter um.
|
|
|
|
- `in`: Alias für [CRangeValidator]; prüft, ob der Attributwert in einer
|
|
vorgegebenen Liste von Werten enthalten ist.
|
|
|
|
- `length`: Alias für [CStringValidator]; stellt sicher, dass die Länge des
|
|
Attributwerts innerhalb eines bestimmten Bereichs liegt.
|
|
|
|
- `match`: Alias für [CRegularExpressionValidator]; prüft, ob der
|
|
Attributwert einem bestimmten regulären Ausdruck entsprechen.
|
|
|
|
- `numerical`: Alias für [CNumberValidator]; prüft, ob das Attribut
|
|
eine gültige Zahl enthält.
|
|
|
|
- `required`: Alias für [CRequiredValidator]; stellt sicher, dass das Attribut nicht
|
|
leer ist.
|
|
|
|
- `type`: Alias für [CTypeValidator]; prüft, ob der Attributwert von einem
|
|
bestimmten Datentyp ist.
|
|
|
|
- `unique`: Alias für [CUniqueValidator]; stellt sicher, dass der Attributwert nur
|
|
einmal in einer bestimmten Tabellenspalte vorkommt.
|
|
|
|
- `url`: Alias für [CUrlValidator]; prüft, ob das Attribut eine gültige URL enthält.
|
|
|
|
Hier einige Beispiele, wie man diese Validatoren verwendet:
|
|
|
|
~~~
|
|
[php]
|
|
// `username` ist zwingend erforderlich
|
|
array('username', 'required'),
|
|
// `username` muss 3 bis 12 Zeichen lang sein
|
|
array('username', 'length', 'min'=>3, 'max'=>12),
|
|
// `password` muss im `register`-Szenario mit `password2` übereinstimmen
|
|
array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
|
|
// `password` muss im `login`-Szenario authentifiziert werden
|
|
array('password', 'authenticate', 'on'=>'login'),
|
|
~~~
|
|
|
|
Sichere Attributzuweisungen
|
|
---------------------------
|
|
|
|
Nach dem Instanziieren eines Models muss dieses oft mit den Daten eines
|
|
Webformulars befüllt werden. Das geschieht am einfachsten mit einer
|
|
sogenannten *Massenzuweisung* (engl.: *massive assignment*):
|
|
|
|
~~~
|
|
[php]
|
|
$model=new LoginForm;
|
|
if(isset($_POST['LoginForm']))
|
|
$model->attributes=$_POST['LoginForm'];
|
|
~~~
|
|
|
|
In der letzten Zeile wird jedes Modelattribut automatisch mit dem
|
|
entsprechenden Wert in `$_POST['LoginForm']` befüllt. Diese Schreibweise ist
|
|
eine Abkürzung für folgenden (Pseudo-)Code:
|
|
|
|
~~~
|
|
[php]
|
|
foreach($_POST['LoginForm'] as $name=>$value)
|
|
{
|
|
if($name ist ein sicheres Attribut)
|
|
$model->$name=$value;
|
|
}
|
|
~~~
|
|
|
|
Es ist sehr wichtig, festzulegen, welche Attribute als "sicher" gelten (also
|
|
über eine Massenzuweisung beschrieben werden dürfen). Würde man zum Beispiel
|
|
auch das Attribut für den Primärschlüssel einer Tabelle als sicher
|
|
definieren, könnte ein Angreifer diesen evtl. bei einem gegebenen
|
|
Record verändern und sich so Zugang zu ansonsten geschützten Daten verschaffen.
|
|
|
|
Wann ein Attribut als *sicher* gilt und wann nicht, unterscheidet sich zwischen
|
|
den Yii-Versionen 1.0 und 1.1. Wir gehen daher auf beide Versionen gesondert
|
|
ein.
|
|
|
|
###Sichere Attribute in 1.1
|
|
|
|
In Version 1.1 gilt ein Attribut als sicher, wenn es dafür eine
|
|
Validierungsregel im gegebenen Szenario gibt. Zum Beispiel:
|
|
|
|
~~~
|
|
[php]
|
|
array('username, password', 'required', 'on'=>'login, register'),
|
|
array('email', 'required', 'on'=>'register'),
|
|
~~~
|
|
|
|
Im Szenario `login` sind hier die Attribute `username` und `password` zwingend
|
|
erforderlich, im Szenario `register` die Attribute `username`, `password` und
|
|
`email`. Im `login`-Szenario können somit nur die Attribute
|
|
`username` und `password` per Massenzuweisung verändert werden, da dies die einzigen
|
|
Attribute sind, die auch eine Regel in diesem Szenario haben.
|
|
Im Szenario `register` können hingegen alle drei Attribute per Massenzuweisung
|
|
befüllt werden.
|
|
|
|
~~~
|
|
[php]
|
|
// Im Szenario login
|
|
$model=new User('login');
|
|
if(isset($_POST['User']))
|
|
$model->attributes=$_POST['User'];
|
|
|
|
// Im Szenario register
|
|
$model=new User('register');
|
|
if(isset($_POST['User']))
|
|
$model->attributes=$_POST['User'];
|
|
~~~
|
|
|
|
Was ist der Hintergrund dieser Konvention? Nun, wenn ein Attribut bereits
|
|
mittels einer Regel überprüft wurde, worüber sollte man sich dann noch Sorgen?
|
|
|
|
Man bedenke, dass Validierungsregeln ohnehin nur dazu da sind, um Daten, die von
|
|
"außen" (also von Besuchern) stammen, zu prüfen. Nicht aber die Daten, die man
|
|
per eigenem Code generiert und in Attribute schreibt (z.B. Zeitstempel oder
|
|
autogenerierte Primärschlüssel). Fügen Sie daher AUF KEINEN FALL Regeln für
|
|
jene Attribute hinzu, die nicht mit Daten von Besuchern befüllt werden müssen.
|
|
|
|
Es kann vorkommen, dass ein Attribut sicher sein soll, obwohl es keine
|
|
spezielle Regel dafür gibt. Zum Beispiel das Attribut für einen Artikeltext,
|
|
bei dem jeglicher Inhalt erlaubt sein soll. In diesem Fall kann man die
|
|
spezielle Regel `safe` verwenden:
|
|
|
|
~~~
|
|
[php]
|
|
array('inhalt', 'safe')
|
|
~~~
|
|
|
|
Der Vollständigkeit halber gibt es auch eine `unsafe`-Regel, die ein Attribut
|
|
explizit als `nicht sicher` festlegt:
|
|
|
|
~~~
|
|
[php]
|
|
array('erlaubnis', 'unsafe')
|
|
~~~
|
|
|
|
Diese Regel wird nur selten verwendet. Sie bildet eine Ausnahme zu unserer
|
|
Konvention über sichere Attribute.
|
|
|
|
|
|
###Sichere Attribute in 1.0
|
|
|
|
In Version 1.0 wurden die sicheren Attribute von der Methode `safeAttributes`
|
|
(sichere Attribute) zurückgeliefert. Für [CFormModel] gab diese Methode
|
|
alle als public deklarierten Attribute und für [CActiveRecord] alle Tabellenspalten
|
|
außer dem Primärschlüssel zurück. Man kann diese
|
|
Methode überschreiben, um die je nach Szenario als sicher geltenden Attribute
|
|
einzuschränken. Ein Benutzermodel kann zum Beispiel viele Attribute enthalten,
|
|
im `login`-Szenario davon aber nur `username` und `password` als sicher
|
|
definieren. Die Methode sieht dann wie folgt aus:
|
|
|
|
~~~
|
|
[php]
|
|
public function safeAttributes()
|
|
{
|
|
return array(
|
|
parent::safeAttributes(),
|
|
'login' => 'username, password',
|
|
);
|
|
}
|
|
~~~
|
|
|
|
Der Rückgabewert von `safeAttributes` muss dieser Struktur entsprechen:
|
|
|
|
~~~
|
|
[php]
|
|
array(
|
|
// Diese Attribute können per Massenzuweisung in jedem Szenario befüllt
|
|
// werden, das unten nicht explizit angegeben wird
|
|
'attr1, attr2, ...',
|
|
*
|
|
// Diese Attribute können nur in `szenario1` per Massenzuweisung befüllt werden
|
|
'szenario1' => 'attr2, attr3, ...',
|
|
*
|
|
// Diese Attribute können nur in `scenario2` per Massenzuweisung befüllt werden
|
|
'Scenario2' => 'attr1, attr3, ...',
|
|
)
|
|
~~~
|
|
|
|
Wenn ein Model keine unterschiedlichen Szenarien benötigt (es also nur in
|
|
einem Szenario verwendet wird, oder alle Szenarien die selben sicheren
|
|
Attribute haben), kann auch ein einfacher String zurückgegeben werden:
|
|
|
|
~~~
|
|
[php]
|
|
'attr1, attr2, ...'
|
|
~~~
|
|
|
|
Möchte man einem nicht-sicheren Attribut Daten zuweisen, muss dies von Hand
|
|
erfolgen:
|
|
|
|
~~~
|
|
[php]
|
|
$model->permission='admin';
|
|
$model->id=1;
|
|
~~~
|
|
|
|
Auslösen der Validierung
|
|
------------------------
|
|
|
|
Wurde ein Model mit gesendeten Daten befüllt, kann die Validierung mit
|
|
[CModel::validate()] durchgeführt werden. Der Rückgabewert zeigt an, ob die
|
|
Validierung erfolgreich war. Bei einem [CActiveRecord]-Model wird die
|
|
Validierung zudem automatisch ausgelöst, sobald man die Modelmethode
|
|
[CActiveRecord::save()] ausführt.
|
|
|
|
Die Validierung erfolgt immer im Rahmen eines bestimmten Szenarios. Über
|
|
[scenario|CModel::scenario] wird das aktuelle Szenario gesetzt und somit
|
|
bestimmt, welche Regeln angewendet werden sollen. Bei einem Benutzermodel sollen
|
|
z.B. im `login`-Szenario nur `username` und `password` überprüft werden. Im
|
|
Szenario `register` hingegen sollen evtl. weitere Attribute validiert werden, z.B.
|
|
`email`, `address`, etc. Die Validierung würde man dann wie folgt durchführen:
|
|
|
|
~~~
|
|
[php]
|
|
// Erstellt ein neues Model User im register-Szenario. Enstpricht diesem Code:
|
|
// $model=new User;
|
|
// $model->scenario='register';
|
|
$model=new User('register');
|
|
|
|
// Befüllt das Model mit den gesendeten Daten
|
|
$model->attributes=$_POST['User'];
|
|
|
|
// Führt die Validierung durch
|
|
if($model->validate()) // falls Eingabewerte in Ordung ...
|
|
...
|
|
else
|
|
...
|
|
~~~
|
|
|
|
Um anzugeben, in welchem Szenario eine Regel verwendet werden soll, werden die
|
|
Szenarien in der `on`-Option der Regel angegeben. Ist die `on`-Option nicht gesetzt,
|
|
gilt die Validierungsregel in allen Szenarien. Ein Beispiel:
|
|
|
|
~~~
|
|
[php]
|
|
public function rules()
|
|
{
|
|
return array(
|
|
array('username, password', 'required'),
|
|
array('password_repeat', 'required', 'on'=>'register'),
|
|
array('password', 'compare', 'on'=>'register'),
|
|
);
|
|
}
|
|
~~~
|
|
|
|
Die erste Regel wird in allen Szenarien verwendet, die anderen beiden
|
|
nur um Szenario `register`.
|
|
|
|
|
|
Abfragen von Validierungsfehlern
|
|
--------------------------------
|
|
|
|
Bei der Validierung werden die auftretenden Fehler im Model gespeichert.
|
|
Mit den Modelmethoden [CModel::getErrors()] und [CModel::getError()] können
|
|
diese Fehler hinterher abgerufen werden. Die erste Methode liefert *alle*
|
|
Fehler für das angegebene Attribut zurück, die zweite nur den *ersten* Fehler.
|
|
|
|
|
|
Attributlabel
|
|
--------------
|
|
|
|
Jedes Eingabefeld in einem Formular benötigt normalerweise ein Label(Beschriftung),
|
|
um den Inhalt des Eingabefeldes zu beschreiben. Man kann ein
|
|
Label zwar fest in einem View hinterlegen, aber die Labels im Model zu
|
|
definieren lässt mehr Flexibilität zu und ist am Ende meist komfortabler.
|
|
|
|
Standardmäßig liefert [CModel] einfach den Attributnamen als Label zurück.
|
|
Überschreibt man jedoch die Methode [attributeLabels()|CModel::attributeLabels],
|
|
können die Labels angepasst werden. Wie wir in den nächsten Abschnitten sehen
|
|
werden, ermöglicht die Labeldefinition innerhalb des Models, die
|
|
zugehörigen Formulare schnell und effektiv zu erzeugen.
|
|
|
|
<div class="revision">$Id: form.model.txt 2285 2010-07-28 20:40:00Z qiang.xue $</div>
|