mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-24 00:46:52 +01:00
435 lines
17 KiB
Plaintext
435 lines
17 KiB
Plaintext
Tworzenie modelu
|
|
==============
|
|
|
|
Zanim rozpoczniemy pisanie kodu HTML wymaganego przez formularze, powinniśmy
|
|
przemyśleć jakich danych oczekujemy od użytkownika końcowego oraz do jakich reguł
|
|
dane te powinny się stosować. Klasa modelu może być używana do zapisania tych
|
|
informacji. Model, tak jak to opisano w podpunkcie [model](/doc/guide/basics.model),
|
|
jest centralnym miejscem do przechowywania danych wejściowych od użytkownika oraz
|
|
do sprawdzania ich poprawności.
|
|
|
|
W zależności od tego w jaki sposób używamy danych wejściowych dostarczonych
|
|
przez użytkownika, możemy stworzyć dwa typy modelów. Jeśli dane wejściowe są zbierane,
|
|
używane a na końcu wyrzucane, powinniśmy stworzyć [model formularza](/doc/guide/basics.model);
|
|
jeśli dane wejściowe są zbierane a następnie zapisywane w bazie danych, powinniśmy
|
|
w zamian używać [rekordu aktywnego](/doc/guide/database.ar). Oba typy modeli
|
|
dziedziczą tą samą klasę bazową [CModel], która definiuje wspólny interfejs
|
|
wymagany dla formularzy.
|
|
|
|
> Note|Uwaga: W tej sekcji używamy głównie modeli formularza jako przykładów.
|
|
Jednakże ma to również zastosowanie do modeli [rekordu aktywnego](/doc/guide/database.ar).
|
|
|
|
Definiowanie klasy modelu.
|
|
--------------------
|
|
|
|
Poniżej utworzymy klasę modelu `LoginForm` używaną do zbierania danych wejściowych
|
|
od użytkownika na stronie logowania. Ponieważ informacje pozwalające się zalogować
|
|
używane są tylko do uwierzytelnienia użytkownika i nie muszą być zapisywane, utworzymy
|
|
`LoginForm` jako model formularza.
|
|
|
|
~~~
|
|
[php]
|
|
class LoginForm extends CFormModel
|
|
{
|
|
public $username;
|
|
public $password;
|
|
public $rememberMe=false;
|
|
}
|
|
~~~
|
|
|
|
W modelu `LoginForm` zadeklarowaliśmy 3 atrybuty: `$username` (nazwa użytkownika),
|
|
`$password` (hasło) oraz `$rememberMe` (zapamiętaj mnie). Są one używane do
|
|
zapamiętywania wprowadzonych przez użytkownika informacji o jego nazwie, haśle oraz
|
|
opcji pozwalającej określić, czy użytkownik chce zapamiętać swoje dane logowania.
|
|
Ponieważ pole `$rememberMe` posiada domyślną wartość ustawioną na `false`,
|
|
odpowiadająca mu opcja jest wyświetlana inicjalnie w formularzu jako odznaczona.
|
|
|
|
> Info|Info: Zamiast nazywać te zmienne właściwościami, będziemy używali nazwy *atrybuty*
|
|
aby odróżnić jest od zwykłych właściwości. Atrybut jest właściwością, która jest
|
|
głównie używana do przechowywania danych pochodzących od danych wejściowych użytkownika
|
|
lub z bazy danych.
|
|
|
|
Tworzenie reguł sprawdzania poprawności (ang. Declaring Validation Rules)
|
|
--------------------------
|
|
|
|
Kiedy użytkownik wyśle dane wejściowe a model zostanie nimi wypełniony, zanim ich użyjemy,
|
|
musimy się upewnić, że dane wejściowe są poprawne. Robi się to poprzez wywołanie
|
|
sprawdzania poprawności danych wejściowych względem zestawu reguł. Definiujemy je
|
|
w metodzie `rules()`, która powinna zwracać tablicę zawierającą konfiguracje reguł.
|
|
|
|
~~~
|
|
[php]
|
|
class LoginForm extends CFormModel
|
|
{
|
|
public $username;
|
|
public $password;
|
|
public $rememberMe=false;
|
|
|
|
public function rules()
|
|
{
|
|
return array(
|
|
array('username, password', 'required'),
|
|
array('password', 'authenticate'),
|
|
);
|
|
}
|
|
|
|
public function authenticate($attribute,$params)
|
|
{
|
|
if(!$this->hasErrors()) // chcemy uwierzytelniać, tylko wtedy gdy nie ma błędów
|
|
{
|
|
$identity=new UserIdentity($this->username,$this->password);
|
|
if($identity->authenticate())
|
|
{
|
|
$duration=$this->rememberMe ? 3600*24*30 : 0; // 30 dni
|
|
Yii::app()->user->login($identity,$duration);
|
|
}
|
|
else
|
|
$this->addError('password','Incorrect password.');
|
|
}
|
|
}
|
|
}
|
|
~~~
|
|
|
|
Powyższy kod mówi, iż zarówno użytkownik `username` jak i hasło `password` są wymagane,
|
|
hasło `password` powinno zostać uwierzytelnione.
|
|
|
|
Każda regułą zwrócona przez metodę `rules()` musi posiadać następujący format:
|
|
|
|
~~~
|
|
[php]
|
|
array('AttributeList', 'Validator', 'on'=>'ScenarioList', ...dodatkowe opcje)
|
|
~~~
|
|
|
|
gdzie `AttributeList` jest łańcuchem znaków, zawierającym oddzielone przecinkami
|
|
nazwy atrybutów, które powinny zostać sprawdzone z regułą; `Validator` określa jaki rodzaj
|
|
porównywania powinien zostać użyty; parametr `on` jest opcjonalny i określa
|
|
listę scenariuszy, gdzie reguła powinna mieć zastosowanie; dodatkowe opcje są parami
|
|
nazwa-wartość i są używane do zainicjalizowania wartości odpowiadających im właściwości
|
|
walidatora.
|
|
|
|
Istnieją trzy sposoby aby określić `walidator` (ang. Validator) w regułach sprawdzania.
|
|
W pierwszym `walidator` może być nazwą metody w klasie modelu, jak `authenticate`
|
|
w przykładzie powyżej. Metoda walidatora musi posiadać następującą składnię:
|
|
|
|
~~~
|
|
[php]
|
|
/**
|
|
* @param string nazwa atrybutu sprawdzanego
|
|
* @param array opcje określone w regule walidacji
|
|
*/
|
|
public function ValidatorName($attribute,$params) { ... }
|
|
~~~
|
|
|
|
W drugim, `walidator` może być nazwą klasy walidacji. Kiedy reguła ma zastosowanie,
|
|
instancja walidatora zostanie stworzona w celu dokonania sprawdzenia danych. Dodatkowe
|
|
opcje w regule są używane do zainicjalizowania wartości instancji atrybutu.
|
|
Klasa walidatora musi rozszerzać klasę [CValidator].
|
|
|
|
> Note|Uwaga: Kiedy określamy regułę dla modelu aktywnego rekordu, możemy używać
|
|
specjalnej opcji nazwanej `on`. Opcja ta może posiadać wartość `'insert'` (wstawianie) lub
|
|
`'update'` (aktualizowanie), tak, że reguła ta ma zastosowanie podczas wstawiania lub
|
|
aktualizowania rekordu. Gdy nie jest ona ustawiona, reguła będzie miała zastosowanie
|
|
w obu przypadkach w momencie gdy metoda `save()` jest wywoływana.
|
|
|
|
W trzecim, `walidator` może być predefiniowanym aliasem do klasy walidatora.
|
|
W powyższym przykładzie, nazwa `required` (wymagany) jest aliasem do klasy [CRequiredValidator],
|
|
która sprawdza czy wartość atrybutu sprawdzanego nie jest pusta. Poniżej znajduje się
|
|
pełna lista predefiniowanych aliasów walidatorów:
|
|
|
|
- `boolean`: alias klasy [CBooleanValidator], sprawdza czy atrybut posiada wartość
|
|
zarówno [CBooleanValidator::trueValue] czy też [CBooleanValidator::falseValue].
|
|
|
|
- `captcha`: alias klasy [CCaptchaValidator], sprawdza czy atrybut zgadza się
|
|
z kodem weryfikującym wyświetlanym przy użyciu [CAPTCHA](http://en.wikipedia.org/wiki/Captcha).
|
|
|
|
- `compare`: alias klasy [CCompareValidator], sprawdza czy atrybut jest równy
|
|
innemu atrybutowi bądź stałej.
|
|
|
|
- `email`: alias klasy [CEmailValidator], sprawdza czy atrybut jest poprawnym
|
|
adresem email.
|
|
|
|
- `default`: alias klasy [CDefaultValueValidator], przypisuje domyślną wartość
|
|
do danego atrybutu.
|
|
|
|
- `exist`: alias klasy [CExistValidator], sprawdza czy wartość atrybutu znajduje się w określonej kolumnie tabeli.
|
|
|
|
- `file`: alias klasy [CFileValidator], sprawdza czy atrybut zawiera nazwę
|
|
wczytywanego pliku.
|
|
|
|
- `filter`: alias klasy [CFilterValidator], transformuje atrybut przy użyciu filtra.
|
|
|
|
- `in`: alias klasy [CRangeValidator], sprawdza czy dana zawiera się
|
|
określonej wcześniej liście wartości.
|
|
|
|
- `length`: alias klasy [CStringValidator], sprawdza czy długość danych zgadza się
|
|
z pewną wartością.
|
|
|
|
- `match`: alias klasy [CRegularExpressionValidator], sprawdza czy dane
|
|
pasują do wyrażenia regularnego.
|
|
|
|
- `numerical`: alias klasy [CNumberValidator], sprawdza czy dane są poprawnym
|
|
numerem.
|
|
|
|
- `required`: alias klasy [CRequiredValidator], sprawdza czy atrybut nie jest pusty.
|
|
|
|
- `type`: alias klasy [CTypeValidator], sprawdza czy atrybut jest określonego typu.
|
|
|
|
- `unique`: alias klasy [CUniqueValidator], sprawdza czy dana jest unikalna w kolumnie
|
|
tabeli bazy danych.
|
|
|
|
- `url`: alias klasy [CUrlValidator], sprawdza czy dana jest poprawnym adresem URL.
|
|
|
|
Poniżej pokazujemy przykłady pokazujące sposób użycia predefiniowanych walidatorów:
|
|
|
|
~~~
|
|
[php]
|
|
// nazwa użytkownika jest wymagana
|
|
array('username', 'required'),
|
|
// nazwa użytkownika musi zawierać się pomiędzy 3 a 12 znakami
|
|
array('username', 'length', 'min'=>3, 'max'=>12),
|
|
// dla scenariusza rejestracji register, hasło z atrybutu password musi zgadzać się z tym
|
|
// z atrybutu password2
|
|
array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
|
|
// dla scenariusza logowania login, atrybut zawierający hasło musi by uwierzytelniony
|
|
array('password', 'authenticate', 'on'=>'login'),
|
|
~~~
|
|
|
|
|
|
Zabezpieczanie przypisywania atrybutów (ang. Securing Attribute Assignments)
|
|
------------------------------
|
|
|
|
Po utworzeniu instancji modelu, często potrzebujemy wypełnić jej atrybuty danymi
|
|
dostarczonymi przez użytkowników końcowych. Można to zrobić wygodnie przy użyciu
|
|
następującego grupowego przypisania:
|
|
|
|
~~~
|
|
[php]
|
|
$model=new LoginForm;
|
|
if(isset($_POST['LoginForm']))
|
|
$model->attributes=$_POST['LoginForm'];
|
|
~~~
|
|
|
|
Ostatnia linia jest grupowym przypisaniem, które przypisuje każdy wpis w `$_POST['LoginForm']`
|
|
do odpowiadającego mu atrybutu w modelu dla scenariusza logowania `login`. Jest to równoznaczne z następującym przypisaniem:
|
|
|
|
~~~
|
|
[php]
|
|
foreach($_POST['LoginForm'] as $name=>$value)
|
|
{
|
|
if($name is a safe attribute)
|
|
$model->$name=$value;
|
|
}
|
|
~~~
|
|
|
|
|
|
Kluczową sprawą jest, aby określić, które atrybuty są bezpieczne. Na przykład, jeśli
|
|
uwidocznimy klucz główny tablicy jako bezpieczny, wtedy osoba atakująca dostanie
|
|
szanse do zmodyfikowania klucza głównego danego rekordu i tym samym manipulację danych,
|
|
do których nie jest on upoważniony.
|
|
|
|
Polityka decydowania, które atrybuty są bezpieczne różnic się w wersjach 1.0
|
|
oraz 1.1. W następnych akapitach opiszemy ją osobno dla każdej z wersji.
|
|
|
|
***Bezpieczne atrybuty w wersji 1.1
|
|
|
|
W wersji 1.1, atrybut rozważany jest jako bezpieczny jeśli pojawia się on w regule
|
|
sprawdzania poprawności, która ma zastosowanie w danym scenariuszu. Na przykład,
|
|
|
|
~~~
|
|
[php]
|
|
array('username, password', 'required', 'on'=>'login, register'),
|
|
array('email', 'required', 'on'=>'register'),
|
|
~~~
|
|
|
|
W powyższym kodzie, atrybuty `username` oraz `password` są wymaganymi atrybutami
|
|
w scenariuszu `register`. W rezultacie, jesli dokonamy masowego przypisania
|
|
w scenariuszu `login`, tylko `username` oraz `password` będą masowo przypiane, gdyż
|
|
tylko one są atrybutami, które pojawiły się w regule sprawdzania poprawności dla
|
|
scenariusza `login`. Z drugiej strony, jeśli będzie to scenariusz `register`,
|
|
wszystkie trzy atrybuty będą masowo przypisane.
|
|
|
|
~~~
|
|
[php]
|
|
// scenariusz login
|
|
$model=new User('login');
|
|
if(isset($_POST['User']))
|
|
$model->attributes=$_POST['User'];
|
|
|
|
// scenariusz register
|
|
$model=new User('register');
|
|
if(isset($_POST['User']))
|
|
$model->attributes=$_POST['User'];
|
|
~~~
|
|
|
|
Dlaczego więc korzystać z takiej polityki w celu określenia czy atrybut jest bezpieczny
|
|
czy też nie? Uzasadnieniem tego jest to, że jeśli atrybut posiada już jedną lub kilka
|
|
reguł sprawdzania poprawności nie musimy się o nic więcej martwić.
|
|
|
|
Należy pamiętać, co ważne, iż reguły sprawdzania poprawności są używane do sprawdzania
|
|
danych wprowadzanych przez użytkownika, rzadziej do danych, które generujemy w kodzie
|
|
(np. stempel czasu, automatycznie generowany klucz główny). Dlatego też NIE NALEŻY
|
|
DODAWAĆ reguł walidacji dla tych atrybutów, które nie są oczekiwane jako dane wejściowe
|
|
od użytkownika.
|
|
Czasami, chcemy zadeklarować atrybut jako bezpieczny, pomimo tego, że nie mamy dla niego żadnej
|
|
określonej reguły. Dobryn przykładem jest atrybut reprezentujący zawartość aartykułu.
|
|
który może zawierać dowolne dane wprowadzone przez użytkownika. W tym celu możemy
|
|
użyć specjalnej reguły `safe` aby osiągnąc ten cel:
|
|
|
|
~~~
|
|
[php]
|
|
array('content', 'safe')
|
|
~~~
|
|
|
|
Dla komplementarności, istnieje również reguła `unsafe`, które używana jest do zadeklarowania
|
|
wprost, iż atrybut jest niebezpieczny:
|
|
|
|
~~~
|
|
[php]
|
|
array('permission', 'unsafe')
|
|
~~~
|
|
|
|
Atrybut `unsafe` jest używany rzadko i jest wyjątkiem do naszego wcześniej zdefiniowanego
|
|
atrybutu `safe`.
|
|
|
|
|
|
***Bezpieczne atrybuty w wersji 1.0
|
|
|
|
W wersji 1.0, decyzja o tym, czy dane wejściowe są bezpieczne czy też nie jest podejmowana
|
|
na podstawie wartości zwracanej przez metodę `safeAttributes` dla wybranego scenariusza.
|
|
Domyślnie metoda ta zwraca wszystkie publiczne zmienne jako bezpieczne atrybuty,
|
|
dla klasy [CFormModel], jednakże dla klasy [CActiveRecord] zwracane są wszystkie
|
|
kolumny z wyjątkiem klucza głównego. Możemy nadpisać tą metodę aby ograniczyć liczbę
|
|
bezpiecznych argumentów w zależności od scenariusza. Na przykład, model użytkownika
|
|
może zawierać wiele atrybutów, ale dla scenariusza logowania `login`, powinniśmy
|
|
używać tylko atrybutów: nazwy użytkownika `username` oraz hasła `password`. Ograniczenie
|
|
te możemy zdefiniować następująco:
|
|
|
|
~~~
|
|
[php]
|
|
public function safeAttributes()
|
|
{
|
|
return array(
|
|
parent::safeAttributes(),
|
|
'login' => 'username, password',
|
|
);
|
|
}
|
|
~~~
|
|
|
|
Ściślej rzecz ujmując, wartości zwracane przez metodę `safeAttributes` powinny posiadać
|
|
następującą strukturę:
|
|
|
|
~~~
|
|
[php]
|
|
array(
|
|
// atrybuty te mogą być grupowo przypisane w każdym scenariuszu,
|
|
// który nie jest bezpośrednio podany poniżej
|
|
'attr1, attr2, ...',
|
|
*
|
|
// atrybuty te mogą być grupowo przypisane tylko dla scenariusza scenario 1
|
|
'scenario1' => 'attr2, attr3, ...',
|
|
*
|
|
// atrybuty te mogą być grupowo przypisane tylko dla scenariusza scenario 2
|
|
'scenario2' => 'attr1, attr3, ...',
|
|
)
|
|
~~~
|
|
|
|
Jeśli model nie zależy od scenariuszy (np. jest on używany tylko w jednym scenariuszu,
|
|
albo wszystkie scenariusze współdzielą ten sam zestaw bezpiecznych atrybutów),
|
|
wartości zwracane mogą być uproszczone do pojedynczego łańcucha znaków:
|
|
|
|
~~~
|
|
[php]
|
|
'attr1, attr2, ...'
|
|
~~~
|
|
|
|
Dla danych wejściowych, które nie są bezpieczne, musimy je przypisać do odpowiadających
|
|
mu atrybutów przy użyciu indywidualnych wyrażeń przypisujących wartość, jak w kolejnym
|
|
przykładzie:
|
|
|
|
~~~
|
|
[php]
|
|
$model->permission='admin';
|
|
$model->id=1;
|
|
~~~
|
|
|
|
|
|
Wyzwalanie sprawdzania poprawności (ang. Triggering Validation)
|
|
---------------------
|
|
|
|
Kiedy model jest wypełniony danymi pochodzącymi od użytkownika, możemy wywołać metodę
|
|
[CModel::validate()] w celu rozpoczęcia procesu sprawdzania poprawności danych.
|
|
Metoda ta zwraca wartość na podstawie której podejmowana jest decyzja, czy sprawdzanie
|
|
wartości zakończyło się sukcesem czy też nie. Dla modelu [CActiveRecord] sprawdzanie
|
|
poprawności może być również automatycznie wyzwolone, podczas wywołania metody [CActiveRecord::save()].
|
|
|
|
|
|
Podczas wywoływania metody [CModel::validate()] możemy podać parametr opisujący scenariusz.
|
|
Tylko te reguły sprawdzania poprawności, które pasują do podanego scenariusza będą
|
|
wywołane. Reguła sprawdzania poprawności ma zastosowanie do scenariusza jeśli
|
|
opcja `on` reguły nie jest ustawiona lub zawiera one określoną nazwę scenariusza.
|
|
Jeśli nie określimy scenariusza podczas wywoływania [CModel::validate()] tylko te
|
|
reguły będą wywołane, które nie posiadają ustawionej opcji `on`.
|
|
|
|
Na przykład, wywołujemy następujące wyrażenie, aby sprawdzić poprawność podczas
|
|
rejestracji użytkownika:
|
|
|
|
~~~
|
|
[php]
|
|
$model->scenario='register';
|
|
$model->validate();
|
|
~~~
|
|
|
|
> Note|Uwaga: właściwość [scenario|CModel::scenario] została udostępniona wraz z wersją 1.0.4.
|
|
> Metoda sprawdzania poprawności będzie używała wartości tej właściwości aby ustalić, które
|
|
> reguły muszą być używane do sprawdzania. W wersjach 1.0.2 oraz 1.0.3, musieliśmy używać następującego
|
|
> sposobu aby przeprowadzić sprawdzanie poprawności w zależności od scenariusza:
|
|
> ~~~
|
|
> [php]
|
|
> $model->validate('register');
|
|
> ~~~
|
|
|
|
|
|
Możemy zdeklarować reguły walidacji w klasie modelu formularza następująco:
|
|
|
|
~~~
|
|
[php]
|
|
public function rules()
|
|
{
|
|
return array(
|
|
array('username, password', 'required'),
|
|
array('password_repeat', 'required', 'on'=>'register'),
|
|
array('password', 'compare', 'on'=>'register'),
|
|
);
|
|
}
|
|
~~~
|
|
|
|
W rezultacie, pierwsza regułą będzie miała zastosowanie do wszystkich scenariuszy,
|
|
zaś następne dwie będą używane dla scenariusza rejestracji `register`.
|
|
|
|
> Note|Uwaga: sprawdzanie poprawności przy użyciu scenariuszy zostało udostępnione
|
|
wraz z wersją 1.0.1.
|
|
|
|
|
|
Zwracanie błędów sprawdzania poprawności (ang. Retrieving Validation Errors)
|
|
----------------------------
|
|
|
|
Możemy używać metody [CModel::hasErrors()] aby sprawdzić jeśli wystąpił błąd podczas
|
|
sprawdzania poprawności, jeśli tak, możemy użyć metody [CModel::getErrors()]
|
|
aby otrzymać komunikaty błędów. Obie metody mogą być używane dla wszystkich atrybutów
|
|
lub też dla wybranych atrybutów.
|
|
|
|
Etykiety atrybutów (ang. Attribute Labels)
|
|
----------------
|
|
|
|
Podczas projektowania formularza często potrzebujemy wyświetlić etykietę (tekst) dla
|
|
każdego pola wejściowego. Etykieta mówi użytkownikowi jakiego rodzaju informacji
|
|
oczekujemy podczas wprowadzania danych do pola. Chociaż możemy zapisać na sztywno
|
|
etykietę w widoku, dostarczy nam to większej elastyczności i będzie bardziej wygodne,
|
|
jeśli zapiszemy je w odpowiednim modelu.
|
|
|
|
Domyślnie [CModel] zwróci nazwę atrybutu jako etykietę. Można to dostosować do swoich
|
|
potrzeb poprzez nadpisanie metody [attributeLabels()|CModel::attributeLabels].
|
|
Jak zobaczymy w następnych punktach, zdefiniowanie etykiet w modelu umożliwia nam tworzenie
|
|
formularzy szybciej i lepiej.
|
|
|
|
<div class="revision">$Id: form.model.txt 1425 2009-09-28 03:28:17Z qiang.xue $</div> |