mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-04 15:24:07 +01:00
655 lines
26 KiB
Plaintext
655 lines
26 KiB
Plaintext
Wzorzec Active Record
|
|
=============
|
|
|
|
Chociaż Yii DAO może uporać się z wirtualnymi oraz bazodanowymi zadaniami, istnieje
|
|
szansa, że będziemy spędzać 90% naszego czasu pisząc wyrażenia SQL, które wykonują
|
|
operacje CRUD (create - tworzenie, read - czytanie, update - aktualizowanie oraz
|
|
delete - usuwanie). Trudno również jest zarządzać kodem, który jest pomieszany
|
|
z wyrażeniami SQL. Do rozwiązania tych problemów możemy użyć wzorca Rekordu Aktywnego (ang. Active Record).
|
|
|
|
|
|
Aktywny Rekord (AR) jest popularną techniką mapowania obiektowo-relacyjnego.
|
|
Każda klasa AR reprezentuje tabelę bazy danych (lub widok), których atrybuty reprezentowane są
|
|
poprzez atrybuty klasy AR a instancja AR reprezentuje wiersz w tej tabeli.
|
|
Wspólne operacje CRUD są zaimplementowane jako metody AR. W rezultacie, posiadamy dostęp do
|
|
naszych danych w bardziej obiektowo zorientowany sposób. Na przykład: możemy użyć poniższego kodu
|
|
aby wstawić nowy wiersz do tabeli `tbl_ost`:
|
|
|
|
~~~
|
|
[php]
|
|
$post=new Post;
|
|
$post->title='przykładowy post';
|
|
$post->content='zawartość postu';
|
|
$post->save();
|
|
~~~
|
|
|
|
W dalszej części opiszemy jak utworzyć AR i użyć go do wykonywania operacji CRUD.
|
|
W następnej sekcji pokażemy jak używać AR by radzić sobie z relacjami w bazach danych.
|
|
Dla uproszczeniami używamy w przykładach tej sekcji następującej tabeli bazy danych.
|
|
Zauważ, że jeśli używasz bazy danych MySQL, powinieneś zamienić `AUTOINCREMENT` na `AUTO_INCREMENT`
|
|
w poniższym kodzie SQL.
|
|
|
|
|
|
~~~
|
|
[sql]
|
|
CREATE TABLE tbl_post (
|
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
title VARCHAR(128) NOT NULL,
|
|
content TEXT NOT NULL,
|
|
create_time INTEGER NOT NULL
|
|
);
|
|
~~~
|
|
|
|
> Tip|Wskazówka: AR nie został pomyślany do rozwiązywania wszystkich zadań związanych z bazą danych.
|
|
Najlepiej używać go do modelowania tabel baz danych w konstrukcjach PHP oraz wykonywania
|
|
zapytań, które nie zawierają skomplikowanego kodu SQL. Dla tych skomplikowanych
|
|
scenariuszy powinno używać się Yii DAO.
|
|
|
|
|
|
Ustanawianie połączeń DB
|
|
--------------------------
|
|
|
|
AR jest zależy od połączenia DB gdy wykonuje operacje zależne od DB. Domyślnie,
|
|
zakłada, że komponent aplikacji `db` dostarcza wymaganej instancji [CDbConnection]
|
|
która reprezentuje połączenie z bazą danych. Poniżej znajduje się przykładowa konfiguracja
|
|
aplikacji
|
|
|
|
~~~
|
|
[php]
|
|
return array(
|
|
'components'=>array(
|
|
'db'=>array(
|
|
'class'=>'system.db.CDbConnection',
|
|
'connectionString'=>'sqlite:path/to/dbfile',
|
|
// włączenie cache'owania schematu celem zwiększenia wydajności
|
|
// 'schemaCachingDuration'=>3600,
|
|
),
|
|
),
|
|
);
|
|
~~~
|
|
|
|
> Tip|Wskazówka: Ponieważ Rekord Aktywny zależy od metadanych tabeli zawierających
|
|
informacje o kolumnach, zajmuje mu to nieco czasu, aby je odczytać oraz zanalizować.
|
|
Jeśli prawdopodobieństwo, że schemat twojej bazy danych jest małe, powinieneś
|
|
włączyć cache'owanie schematu bazy danych poprzez skonfigurowanie właściwości
|
|
[CDbConnection::schemaCachingDuration] poprzez przypisanie jej wartości większej niż 0.
|
|
|
|
Wsparcie dla AR jest ograniczone przez DBMS. Aktualnie, tylko następujące DBMS są wspierane:
|
|
|
|
- [MySQL 4.1 lub późniejsze](http://www.mysql.com)
|
|
- [PostgreSQL 7.3 lub późniejsze](http://www.postgres.com)
|
|
- [SQLite 2 oraz 3](http://www.sqlite.org)
|
|
- [Microsoft SQL Server 2000 lub nowsze](http://www.microsoft.com/sqlserver/)
|
|
- [Oracle](http://www.oracle.com)
|
|
|
|
> Note|Uwaga: Wsparcie dla Microsoft SQL Server jest dostępne od wersji 1.0.4;
|
|
> a wsparcie dla Oracle'a od wersji 1.0.5.
|
|
|
|
Jeśli chcesz używać komponentu aplikacji innego niż `db` lub jeśli chcesz pracować z wieloma
|
|
bazami danych używając AR, powinieneś nadpisać metodę [CActiveRecord::getDbConnection()].
|
|
Klasa [CActiveRecord] jest klasą bazową dla wszystkich klas AR.
|
|
|
|
> Tip|Wskazówka: Istnieją dwa sposoby pracowania z wieloma bazami danych z użyciem AR.
|
|
Jeśli schematy bazy danych różnią się, możesz utworzyć różne bazowe klasy AR z różniącymi się
|
|
implementacjami metody [getDbConnection()|CActiveRecord::getDbConnection]. W przeciwnym przypadku,
|
|
lepszym pomysłem jest dynamiczna zmiana statycznej zmiennej [CActiveRecord::db].
|
|
|
|
Definiowanie klasy AR
|
|
-----------------
|
|
|
|
Aby uzyskać dostęp do tabeli bazy danych musimy najpierw zdefiniować klasę AR poprzez
|
|
rozszerzenie [CActiveRecord]. Każda klasa AR reprezentuje jedną tabele bazy danych
|
|
a jedna instancja reprezentuje wiersz tej tabeli. Następujący przykład pokazuje minimalny
|
|
kod potrzebny aby klasa AR reprezentowała tabelę `tbl_ost`.
|
|
|
|
~~~
|
|
[php]
|
|
class Post extends CActiveRecord
|
|
{
|
|
public static function model($className=__CLASS__)
|
|
{
|
|
return parent::model($className);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
> Tip|Wskazówka: Ponieważ klasy AR są często używane w wielu różnych miejscach,
|
|
> możemy zaimportować zawartość folderu zawierającego klasy AR zamiast
|
|
> dołączać je jedna po drugiej. Na przykład, jeśli wszystkie nasze pliki zawierające klasy AR
|
|
> znajdują się w katalogu `protected/models`, możemy skonfigurować aplikację w następujący sposób:
|
|
> ~~~
|
|
> [php]
|
|
> return array(
|
|
> 'import'=>array(
|
|
> 'application.models.*',
|
|
> ),
|
|
> );
|
|
> ~~~
|
|
|
|
Domyślnie, nazwa klasy AR jest identyczna z nazwą tabeli w bazie danych. Jeśli nazwa
|
|
klasy różni się, nadpisz metodę [tableName()|CActiveRecord::tableName]. Metoda [model()|CActiveRecord::model]
|
|
jest zadeklarowana jako taka dla każdej klasy AR (wyjaśnienie tego nastąpi później).
|
|
|
|
> Info|Info: Aby móc używać [prefiksów tablic](/doc/guide/database.dao#using-table-prefix)
|
|
> wprowadzonych w wersji 1.1.0, metoda [tableName()|CActiveRecord::tableName] dla klasy AR
|
|
> może być nadpisana następująco,
|
|
> ~~~
|
|
> [php]
|
|
> public function tableName()
|
|
> {
|
|
> return '{{post}}';
|
|
> }
|
|
> ~~~
|
|
> Oznacza to, że zamiast zwracać pełną i dokładną nazwę tabeli, zwracamy jej nazwę bez
|
|
> prefiksu, dodatkowo zamkniętą w podwójnych nawiasach klamrowych.
|
|
|
|
Wartości kolumn wiersza tabeli są dostępne jako właściwości odpowiednich instancji klasy AR.
|
|
Na przykład, następujący kod ustawia kolumnę (atrybut) `title`:
|
|
|
|
~~~
|
|
[php]
|
|
$post=new Post;
|
|
$post->title='przykładowy post';
|
|
~~~
|
|
|
|
Chociaż nigdy bezpośrednio nie zdeklarowaliśmy właściwości `title` w klasie `tbl_ost`,
|
|
jest ona dostępna w powyższym kodzie. Dzieje się tak, ponieważ `title` jest kolumną
|
|
w tabeli `tbl_ost` a CActiveRecord czyni ją dostępną poprzez właściwość przy pomocy
|
|
magicznej metody PHP `__get()`. Jeśli w ten sam sposób spróbujemy uzyskać dostęp
|
|
do nieistniejącej kolumny wyjątek zostanie rzucony.
|
|
|
|
> Info|Info: W tym poradniku nazywamy kolumny przy użyciu notacji wielbłąda (np. `createTime`).
|
|
Czynimy tak, ze względu na to, że kolumny dostępne są w taki sam sposób jak zwykłe właściwości obiektu,
|
|
które również używają notacji wielbłąda. Chociaż, używanie notacji wielbłąda czyni nasz kod PHP
|
|
bardziej konsekwentnym w nazewnictwie, może to powodować problemy związane z wielkością liter w pewnych DBMS.
|
|
Na przykład, PostgreSQL traktuje domyślnie nazwy kolumn jako niezależne od wielkości liter i dlatego musimy
|
|
używać nazwy kolumn podanej w cudzysłowiu w warunkach zapytania jeśli kolumna zawiera pomieszane duże i małe litery.
|
|
Z tego powodu, mądrą decyzją jest nazywanie kolumn (a także tabel) jedynie przy użyciu małych liter (np. `create_time`)
|
|
w celu uniknięcia potencjalnych problemów z wielkością liter.
|
|
|
|
~~~
|
|
[php]
|
|
public function primaryKey()
|
|
{
|
|
return 'id';
|
|
// Dla złożonych kluczy głównych, zwróć następująca tablicę
|
|
// return array('pk1', 'pk2');
|
|
}
|
|
~~~
|
|
|
|
Tworzenie rekordu
|
|
---------------
|
|
|
|
Aby wstawić nowy wiersz do tabeli bazy danych, tworzymy nową instancję odpowiedniej
|
|
klasy AR, ustawiamy jej właściwości powiązane z kolumnami tabel i wołamy metodę
|
|
[save()|CActiveRecord::save] aby zakończyć wstawianie.
|
|
|
|
~~~
|
|
[php]
|
|
$post=new Post;
|
|
$post->title='przykładowy post';
|
|
$post->content='zawartość przykładowego postu';
|
|
$post->create_time=time();
|
|
$post->save();
|
|
~~~
|
|
|
|
Jeśli klucz główny tabeli jest autoinkrementowalny, po wstawieniu, instancja będzie
|
|
zawierała zaktualizowaną wartość klucza głównego. w powyższym przykładzie właściwość
|
|
`id` odpowiada wartości klucza głównego nowo wstawionego postu, mimo że nigdy nie zmienialiśmy
|
|
tej wartości bezpośrednio.
|
|
|
|
Jeśli kolumna została zdefiniowana wraz z jakąś statyczną, domyślną wartością (np.
|
|
łańcuch znaków, liczba) w schemacie tabeli bazy danych, odpowiadająca jej własność
|
|
w instancji AR będzie automatycznie posiadała tą wartość, gdy instancja zostanie utworzona.
|
|
Jednym ze sposobów zmiany tej wartości domyślnej jest bezpośrednie zdeklarowanie
|
|
właściwości w klasie AR:
|
|
|
|
~~~
|
|
[php]
|
|
class Post extends CActiveRecord
|
|
{
|
|
public $title='proszę wprowadź tytuł';
|
|
......
|
|
}
|
|
|
|
$post=new Post;
|
|
echo $post->title; // to wyświetli tekst: proszę wprowadź tytuł
|
|
~~~
|
|
|
|
Poczynając od wersji 1.0.2 atrybut może mieć przypisaną wartość typu [CDbExpression]
|
|
zanim rekord zostanie zapisany (zarówno podczas wstawiania jak i aktualizacji) do bazy danych.
|
|
Na przykład, w celu zapisania stempla czasu (ang. timestamp) zwracanego przez funkcję MYSQL
|
|
`NOW()`, możemy użyć następującego kodu:
|
|
|
|
~~~
|
|
[php]
|
|
$post=new Post;
|
|
$post->create_time=new CDbExpression('NOW()');
|
|
// $post->create_time='NOW()'; nie zadziała gdyż
|
|
// 'NOW()' będzie potraktowany jako łańcuch znaków
|
|
$post->save();
|
|
~~~
|
|
|
|
> Tip|Wskazówka: Chociaż AR umożliwia wykonywanie operacji na bazie danych pez potrzeby
|
|
pisania uciążliwych zapytań SQL, często chcemy wiedzieć jakie zapytanie SQL jest
|
|
wykonywane w tle przez AR. Możemy uzyskać tą informację poprzez włączenie w Yii
|
|
[funkcjonalności logowania](/doc/guide/topics.logging). Na przykład, możemy włączyć
|
|
[CWebLogRoute] w konfiguracji aplikacji, dzięki czemu bedziemy widzieć wykonywane zapytania
|
|
SQL na końcu każdej strony. Od wersji 1.0.5, możemy ustawić właściwość
|
|
[CDbConnection::enableParamLogging] jako true w konfiguracji aplikacji, tak, że wartości
|
|
parametrów powiązanych z instrukcją SQL będą również logowane.
|
|
|
|
Odczytywanie rekordu
|
|
--------------
|
|
|
|
Aby odczytać dane z tabeli bazodanowej wołany jedną z następujących metod `find`:
|
|
|
|
~~~
|
|
[php]
|
|
// znajduje pierwszy wiersz spełniający określone warunki
|
|
$post=Post::model()->find($condition,$params);
|
|
// znajduje wiersz o konkretnym kluczu głównym
|
|
$post=Post::model()->findByPk($postID,$condition,$params);
|
|
// znajduje wiersz o określonych wartościach atrybutów
|
|
$post=Post::model()->findByAttributes($attributes,$condition,$params);
|
|
// znajduje pierwszy wiersz używając określonego wyrażenia SQL
|
|
$post=Post::model()->findBySql($sql,$params);
|
|
~~~
|
|
|
|
Powyżej, wywołujemy metodę `find` wraz z `Post::model()`. Pamiętaj, że ta statyczna metoda `model()`
|
|
jest wymagana dla każdej klasy AR. Metoda ta zwraca instancję A, która jest używana
|
|
by otrzymać dostęp do metod na poziomie klasy w kontekście obiektu (coś podobnego
|
|
do statycznych metod klas).
|
|
|
|
Jeśli metoda `find` znajdzie wiersz spełniający warunki zapytania, zwróci ona instancję
|
|
`tbl_ost`, której właściwości będą zawierać odpowiadające kolumnom wartości wiersza tabeli.
|
|
Możemy wtedy czytać załadowane wartości tak jak to robimy w przypadku właściwości obiektu,
|
|
na przykład, `echo $post->title;`.
|
|
|
|
Metoda `find` zwróci wartość null, jeśli nie znajdzie niczego w bazie danych, co spełniałoby
|
|
dane warunki zapytania.
|
|
|
|
Podczas wywoływania metody `find` używamy `$condition` (warunków) oraz `$params` (parametrów)
|
|
aby określić warunki zapytania. Tutaj `$condition` może być ciągiem znaków reprezentujących
|
|
klauzulę `WHERE` w wyrażeniu SQL a `$params` jest tablicą parametrów, których wartości
|
|
powinny być przypięte do placeholderów w `$condition`. Na przykład:
|
|
|
|
~~~
|
|
[php]
|
|
// znajdź wiersz z postID=10
|
|
$post=Post::model()->find('postID=:postID', array(':postID'=>10));
|
|
~~~
|
|
|
|
> Note|Uwaga: W powyższym przykładzie będziemy musieli unikać referencji do kolumny `postID`
|
|
dla pewnych DBMS. Na przykład, jesli używamy PostrgreSQL, będziemy musieli zapisać warunek jako `"postID"=:postID`,
|
|
ze względu na to, że PostgreSQL domyślnie rozróżnia wielkość liter kolumn.
|
|
|
|
Możemy również używać `$condition` do zdefiniowania bardziej rozbudowanych warunków
|
|
zapytań. Zamiast łańcuchem znaków, `$condition` może być instancją [CDbCriteria],
|
|
która pozwala na określenie warunków innych niż klauzula `WHERE`. Na przykład:
|
|
|
|
~~~
|
|
[php]
|
|
$criteria=new CDbCriteria;
|
|
$criteria->select='title'; // wybierz tylko kolumnę 'title'
|
|
$criteria->condition='postID=:postID';
|
|
$criteria->params=array(':postID'=>10);
|
|
$post=Post::model()->find($criteria); // $params nie jest wymagane
|
|
~~~
|
|
|
|
Zauważ, że kiedy używamy [CDbCriteria] jako warunków zapytania, wartość parametru `$params`
|
|
jest niepotrzebna, gdyż może być ona zdefiniowana w [CDbCriteria], tak jak pokazano powyżej.
|
|
|
|
Alternatywnym sposobem do [CDbCriteria] jest przekazanie tablicy do metody `find`.
|
|
Klucze i wartości tablicy odpowiadają nazwom i wartościom właściwości kryteriów.
|
|
Powyższy przykład może zostać przepisany w następujący sposób:
|
|
|
|
~~~
|
|
[php]
|
|
$post=Post::model()->find(array(
|
|
'select'=>'title',
|
|
'condition'=>'postID=:postID',
|
|
'params'=>array(':postID'=>10),
|
|
));
|
|
~~~
|
|
|
|
> Info|Info: Kiedy warunki zapytania polegają ma porównywaniu kolumn
|
|
z określonymi wartościami, możemy użyć [findByAttributes()|CActiveRecord::findByAttributes].
|
|
Parametrami `$attributes` zostaje wtedy tablica wartości indeksowana poprzez nazwy kolumn.
|
|
W części frameworków, to zadanie może zostać zrealizowane poprzez wywołanie metod
|
|
podobnych do `findByNameAndTitle`. Chociaż to podejście wygląda bardzo atrakcyjnie
|
|
często powoduje zamieszanie, konflikty oraz problemy np. z czułością na wielkość liter
|
|
w nazwach kolumn.
|
|
|
|
Kiedy więcej wierszy danych pasuje do określonych warunków zapytania, możemy
|
|
dostarczyć je wszystkie razem używając następujących metod `findAll`, z których
|
|
każda ma swój odpowiednik w metodzie `find`, tak jak było to wcześniej opisane.
|
|
|
|
~~~
|
|
[php]
|
|
// znajduje wszystkie wiersze spełniające określone warunki
|
|
$posts=Post::model()->findAll($condition,$params);
|
|
// znajduje wszystkie wiersze o konkretnym kluczu głównym
|
|
$posts=Post::model()->findAllByPk($postIDs,$condition,$params);
|
|
// znajduje wiersze o określonych wartościach atrybutów
|
|
$posts=Post::model()->findAllByAttributes($attributes,$condition,$params);
|
|
// znajduje wiersze używając określonego wyrażenia SQL
|
|
$posts=Post::model()->findAllBySql($sql,$params);
|
|
~~~
|
|
|
|
Jeśli nic nie pasuje do warunków zapytania, `findAll` zwróci pustą tablicę.
|
|
Różni się tym od metody `find`, która zwróci wartość null jeśli nic nie zostało znalezione.
|
|
|
|
|
|
Poza opisanymi powyżej metodami `find` and `findAll`, dla wygody dostarczono następujących metod:
|
|
|
|
~~~
|
|
[php]
|
|
// zwraca ilość wierszy spełniających określone warunki
|
|
$n=Post::model()->count($condition,$params);
|
|
// zwraca ilość wierszy używanych w określonym wyrażeniu SQL
|
|
$n=Post::model()->countBySql($sql,$params);
|
|
// sprawdza czy istnieje przynajmniej jeden wiersz spełniający określone warunki
|
|
$exists=Post::model()->exists($condition,$params);
|
|
~~~
|
|
|
|
Aktualizowanie rekordu
|
|
---------------
|
|
|
|
Po tym jak instancja AR została wypełniona wartościami kolumn, możemy zmienić je
|
|
i zapisać je z powrotem w tabeli bazodanowej.
|
|
|
|
~~~
|
|
[php]
|
|
$post=Post::model()->findByPk(10);
|
|
$post->title='nowy tytuł postu';
|
|
$post->save(); // zapisz zmiany do bazy danych
|
|
~~~
|
|
|
|
Jak widać, używamy tej samej metody [save()|CActiveRecord::save] do wykonania operacji
|
|
wstawiania jak i aktualizowania. Jeśli instancja AR została utworzona za pomocą
|
|
operatora `new`, wywołanie [save()|CActiveRecord::save] spowoduje wstawienie nowego wiersza
|
|
do tabeli bazy danych; jeśli instancja AR jest rezultatem wywołania metody `find`
|
|
lub `findAll`, wywołanie [save()|CActiveRecord::save] spowoduje zaktualizowanie istniejącego
|
|
wiersza w tabeli. Oczywiście, możemy użyć [CActiveRecord::isNewRecord] aby powiedzieć
|
|
czy instancja AR jest nowa czy też nie.
|
|
|
|
Istnieje również możliwość aktualizacji jednego lub więcej wiersz w tabeli bazy danych
|
|
bez konieczności ich wcześniejszego wczytywania. AR dostarcza w tym celu następujących,
|
|
pomocnych metod na poziomie klasy:
|
|
|
|
~~~
|
|
[php]
|
|
// aktualizuje wiersze spełniający określone warunki
|
|
Post::model()->updateAll($attributes,$condition,$params);
|
|
// aktualizuje wiersze pasujące do określonych warunki oraz klucza(y) głównego(ych)
|
|
Post::model()->updateByPk($pk,$attributes,$condition,$params);
|
|
// aktualizuje licznik kolumn w wierszach spełniających określone warunki
|
|
Post::model()->updateCounters($counters,$condition,$params);
|
|
~~~
|
|
|
|
W powyższych przykładach, `$attributes` jest tablicą wartości kolumn indeksowanych
|
|
przez nazwy kolumn; `$counters` jest tablicą zwiększających się wartości indeksowanych przez
|
|
nazwy kolumn; `$condition` oraz `$params` są opisane w poprzedniej sekcji.
|
|
|
|
Usuwanie rekordu
|
|
---------------
|
|
|
|
Możemy również usunąć wiersz danych jeśli instancja AR została wypełniona tym wierszem.
|
|
|
|
~~~
|
|
[php]
|
|
$post=Post::model()->findByPk(10); // zakładamy, że istnieje post, którego ID wynosi 10
|
|
$post->delete(); // usuń wiersz danych z tabeli bazy danych
|
|
~~~
|
|
|
|
Zauważ, że po usunięciu, instancja AR pozostaje niezmieniona, ale odpowiadający jej
|
|
wiersz w tabeli bazodanowej już nie istnieje.
|
|
|
|
Następujące metody klasowe zostały dostarczone aby móc usuwać wiersze bez konieczności ich
|
|
wcześniejszego wczytywania:
|
|
|
|
~~~
|
|
[php]
|
|
// usuń wiersze spełniające określone warunki
|
|
Post::model()->deleteAll($condition,$params);
|
|
// usuń wiersze pasujące do określonych warunków oraz klucza(y) głównego(ych)
|
|
Post::model()->deleteByPk($pk,$condition,$params);
|
|
~~~
|
|
|
|
Walidacja danych
|
|
---------------
|
|
|
|
Często podczas wstawiania lub aktualizowania wiersza mamy potrzebę sprawdzić
|
|
czy wartości kolumny spełniają pewne warunki. Jest to szczególnie ważne, jeśli
|
|
wartości kolumn dostarczane są przez użytkowników końcowych. Uogólniające, nigdy
|
|
nie powinniśmy ufać niczemu przychodzącemu od strony klienta.
|
|
|
|
AR automatycznie dokonuje walidacji podczas wywoływania metody [save()|CActiveRecord::save].
|
|
Walidacja bazuje na regułach określonych w metodzie [rules()|CModel::rules] klasy AR.
|
|
Aby uzyskać więcej szczegółów jak określać reguły walidacji, zobacz sekcję
|
|
[Deklarowanie reguł walidacji](/doc/guide/form.model#declaring-validation-rules).
|
|
Poniżej znajduje się typowy przepływ podczas zapisywania rekordu.
|
|
|
|
~~~
|
|
[php]
|
|
if($post->save())
|
|
{
|
|
// dane są poprawne oraz zostały szczęśliwie zapisane/zaktualizowane
|
|
}
|
|
else
|
|
{
|
|
// dane są niepoprawne. Wywołaj getErrors() aby otrzymać komunikaty błędów
|
|
}
|
|
~~~
|
|
|
|
Kiedy dane używane do wstawiania lub aktualizowania są dostarczane przez użytkownika
|
|
końcowego za pomocą formularza HTML, potrzebujemy przypisać je do odpowiadających im
|
|
właściwości AR. Możemy to zrobić następująco:
|
|
|
|
~~~
|
|
[php]
|
|
$post->title=$_POST['title'];
|
|
$post->content=$_POST['content'];
|
|
$post->save();
|
|
~~~
|
|
|
|
Jeśli mamy wiele kolumn, będziemy widzieli długą listę takich przypisań. Można to
|
|
zmniejszyć poprzez używanie właściwości [attributes|CActiveRecord::attributes]
|
|
w sposób jaki pokazano poniżej. Więcej szczegółów można znaleźć w sekcji
|
|
[Zabezpieczaniu przypisań atrybutów](/doc/guide/form.model#securing-attribute-assignments)
|
|
oraz sekcji [Tworzeniu akcji](/doc/guide/form.action).
|
|
|
|
~~~
|
|
[php]
|
|
// zakładamy, że $_POST['Post'] jest tablicą wartości kolumn indeksowanych przez
|
|
// nazwy kolumn
|
|
$post->attributes=$_POST['Post'];
|
|
$post->save();
|
|
~~~
|
|
|
|
Porównywanie rekordów
|
|
-----------------
|
|
|
|
Tak jak wiersze tabel, instancje AR są jednoznacznie identyfikowane przez ich wartości
|
|
kluczy głównych. Dlatego też, porównując dwie instancje AR, musimy porównać jedynie ich
|
|
wartości kluczy głównych, zakładając, że należą do tej samej klasy AR. Jednakże
|
|
najprostszym sposobem jest wywołanie [CActiveRecord::equals()].
|
|
|
|
> Info|Info: W odróżnieniu do implementacji AR w innych frameworkach, Yii wspiera
|
|
klucze złożone w AR. Klucze złożone zawierają więcej niż jedną kolumnę. Odpowiednio,
|
|
wartości kluczy głównych reprezentowane są w Yii pod postacią tablicy. Właściwość
|
|
[primaryKey|CActiveRecord::primaryKey] zawiera wartość klucza głównego instancji AR.
|
|
|
|
Dostosowywanie (do własnych potrzeb)
|
|
-------------
|
|
|
|
[CActiveRecord] dostarcza kilku metod "wypełniaczy", które mogą być nadpisane
|
|
w klasach potomnych aby dostosować ich sterowanie kolejnością zadań (ang. workflow).
|
|
|
|
- [beforeValidate|CModel::beforeValidate] oraz
|
|
[afterValidate|CModel::afterValidate]: są wywoływane przed (ang. before) i po (ang. after) walidacji.
|
|
|
|
- [beforeSave|CActiveRecord::beforeSave] oraz
|
|
[afterSave|CActiveRecord::afterSave]: są wywoływane przed i po zapisie instancji AR.
|
|
|
|
- [beforeDelete|CActiveRecord::beforeDelete] oraz
|
|
[afterDelete|CActiveRecord::afterDelete]: są wywoływane przed i po usunięciu instancji AR.
|
|
|
|
- [afterConstruct|CActiveRecord::afterConstruct]: jest wywoływana dla każdej instancji AR
|
|
utworzonej za pomocą operatora `new`.
|
|
|
|
- [beforeFind|CActiveRecord::beforeFind]: jest wywoływana zanim instancja AR zostanie
|
|
użyta do wykonania zapytania (np. `find()`, `findAll()`). Dostępna od wersji 1.0.9.
|
|
|
|
- [afterFind|CActiveRecord::afterFind]: jest wywoływana dla każdej instancji AR utworzonej
|
|
jako rezultat zapytania.
|
|
|
|
Używanie transakcji w AR
|
|
-------------------------
|
|
|
|
Każda instancja AR zawiera właściwość nazwaną [dbConnection|CActiveRecord::dbConnection]
|
|
która jest instancją [CDbConnection]. Możemy wtedy używać funkcjonalności
|
|
[transakcji](/doc/guide/database.dao#using-transactions) dostarczanej przez Yii DAO
|
|
jeśli występuje taka potrzeba podczas pracy z AR:
|
|
|
|
~~~
|
|
[php]
|
|
$model=Post::model();
|
|
$transaction=$model->dbConnection->beginTransaction();
|
|
try
|
|
{
|
|
// szukanie i zapisywanie są dwoma krokami, które mogą być przychodzić za pomocą
|
|
// innych żądań, dlatego też używamy transakcji aby być pewnymi logiki oraz spójności danych
|
|
$post=$model->findByPk(10);
|
|
$post->title='nowy tytuł postu';
|
|
$post->save();
|
|
$transaction->commit();
|
|
}
|
|
catch(Exception $e)
|
|
{
|
|
$transaction->rollBack();
|
|
}
|
|
~~~
|
|
|
|
Nazwane podzbiory (ang. Named Scopes)
|
|
------------
|
|
|
|
> Note|Uwaga: Wsparcie dla nazwanych podzbiorów zostało udostępnione wraz z wersją 1.0.5.
|
|
> Pierwotny pomysł na nazwane podzbiory pochodzi z Ruby on Rails.
|
|
|
|
*Nazwany podzbiór* reprezentuje *nazwane* kryteria zapytania, które mogą być połączone z innymi nazwanymi podzbiorami
|
|
i zastosowane do zapytania generowanego przez rekord aktywny.
|
|
|
|
Nazwane podzbiory deklarowane są głównie w metodzie [CActiveRecord::scopes()] jako pary nazwa-kryterium.
|
|
Następujący kod definiuje dwa nazwane podzbiory, `opublikowane` oraz `najnowsze` w klasie modelu `tbl_ost`:
|
|
|
|
~~~
|
|
[php]
|
|
class Post extends CActiveRecord
|
|
{
|
|
......
|
|
public function scopes()
|
|
{
|
|
return array(
|
|
'published'=>array(
|
|
'condition'=>'status=1',
|
|
),
|
|
'recently'=>array(
|
|
'order'=>'create_time DESC',
|
|
'limit'=>5,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
Każdy nazwany podzbiór jest definiowany jako tablica, która może zostać użyta do inicjalizacji instancji klasy [CDbCriteria].
|
|
Na przykład, nazwany zbiór `najnowsze` określa właściwość `order` jako `createTime DESC` oraz nadaje właściwości `limit` wartość 5,
|
|
które przetłumaczone na kryteria zapytania, powinny zwrócić ostatnich 5 postów.
|
|
|
|
Nazwane podzbiory używane są najczęściej jako modyfikatory dla wywołań metody `find`. Kilka nazwanych podzbiorów może zostać ze sobą połączonych
|
|
i w rezultacie tego zwrócić bardziej ograniczony zbiór wynikowy. Na przykład, aby znaleźć ostatnio opublikowane posty, możemy użyć następującego kodu:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->obublikowane()->najnowsze()->findAll();
|
|
~~~
|
|
|
|
Ogólnie rzecz biorąc, nazwane podzbiory musza pojawić się po lewej stronie od wywołania metody `find`. Każde z nich dostarcza kryteria zapytania ,
|
|
które są łączone z pozostałymi kryteriami, włączając w to te, które zostały przekazane do metody `find`. Efekt końcowy jest podobny do tego,
|
|
jakby do zapytania dodano listę filtrów.
|
|
|
|
Poczynając od wersji 1.0.6, nazwane podzbiory mogą być również używane wraz z metodami
|
|
`update` oraz `delete`. Na przykład, następujący kod usunie wszystkie aktualnie opublikowane posty:
|
|
|
|
~~~
|
|
[php]
|
|
Post::model()->published()->recently()->delete();
|
|
~~~
|
|
|
|
> Note|Uwaga: Nazwane podzbiory mogą być tylko używane wyłącznie z poziomu metod klasowych.
|
|
Oznacza to, że muszą one być wywołane w przy użyciu `NazwaKlasy::model()`.
|
|
|
|
|
|
### Parametryzowane nazwane podzbiory (ang. Parameterized Named Scopes)
|
|
|
|
Nazwane podzbiory mogą by sparametryzowane. Na przykład, chcemy dostosować liczbę postów określoną w nazwanym podzbiorze `najnowsze`
|
|
Aby to zrobić, zamiast deklarować nazwany podzbiór w metodzie [CActiveRecord::scopes], musimy zdefiniować nową metodę,
|
|
której nazwa jest taka sama jak jak nazwa podzbioru:
|
|
|
|
~~~
|
|
[php]
|
|
public function najnowsze($limit=5)
|
|
{
|
|
$this->getDbCriteria()->mergeWith(array(
|
|
'order'=>'create_time DESC',
|
|
'limit'=>$limit,
|
|
));
|
|
return $this;
|
|
}
|
|
~~~
|
|
|
|
Następnie, możemy używać następującego wyrażenia, aby uzyskać 3 ostatnie opublikowane posty:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->opublikowane()->najnowsze(3)->findAll();
|
|
~~~
|
|
|
|
Jeśli w powyższym kodzie nie przekazalibyśmy 3, otrzymalibyśmy domyślnie 5 ostatnio opublikowanych postów.
|
|
|
|
### Domyślne nazwane podzbiory
|
|
|
|
Klasa modelu może posiadać domyślne nazwane podzbiory, które będą stosowane dla wszystkich zapytań
|
|
(włączająć w to zapytania relacyjne) dla modelu. Na przykład, strona wspierająca wielojęzyczność|
|
|
może chcież wyświetlać zawartość w aktualnie wybranym języku. Ponieważ, możemy mieć wiele
|
|
zapytań związanych z zawartością strony, możemy zdecydować się na zdefiniowanie
|
|
domyśnie nazwanych podzbiorów, aby rozwiązać ten problem. Aby to zrobić, nadpisujemy
|
|
metodę [CActiveRecord::defaultScope] w następujący sposób:
|
|
|
|
|
|
~~~
|
|
[php]
|
|
class Content extends CActiveRecord
|
|
{
|
|
public function defaultScope()
|
|
{
|
|
return array(
|
|
'condition'=>"language='".Yii::app()->language."'",
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
Od teraz, nastepujące wywołanie metody będzie automatycznie używało powyżej zdefiniowanych
|
|
kreteriów zapytania:
|
|
|
|
~~~
|
|
[php]
|
|
$contents=Content::model()->findAll();
|
|
~~~
|
|
|
|
Zauważ, że domyślne nazwane podzbiory mają zastosowanie tylko dla zapytań `SELECT`. Są one ignorowane dla zapytań `INSERT`, `UPDATE` oraz `DELETE`.
|
|
|
|
<div class="revision">$Id: database.ar.txt 2771 2010-12-24 14:48:44Z alexander.makarow $</div> |