Files
yii/docs/guide/pl/form.model.txt
2009-12-13 16:48:49 +00:00

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>