mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-06 00:04:07 +01:00
489 lines
23 KiB
Plaintext
489 lines
23 KiB
Plaintext
Relacyjny Rekord Aktywny
|
|
========================
|
|
|
|
Dotychczas zobaczyliśmy jak używać Rekordu Aktywnego (AR) aby wybierać dane z jednej
|
|
tabeli bazodanowej. W tej sekcji opiszemy jak używać AR aby złączyć kilka powiązanych
|
|
tabel baz danych i zwrócić połączony zbiór danych.
|
|
|
|
W celu używania relacyjnego AR, wymagane jest, aby relacja dla obcego, klucza głównego
|
|
była dobrze zdefiniowana pomiędzy tabelami, które będą łączone. AR polega na metadanych
|
|
tych relacji gdy decyduje jak połączyć tabele.
|
|
|
|
> Note|Uwaga: Poczynając od wersji 1.0.1, możesz używać relacyjnego AR nawet jeśli
|
|
> nie zdefiniowałeś kluczy obcych w swojej bazie danych.
|
|
|
|
Dla uproszczenia, będziemy używali schematu bazy danych pokazanego na następnym
|
|
diagramie zależności encji (ER) celem zilustrowania przykładów w tej sekcji.
|
|
|
|

|
|
|
|
> Info|Info: Wsparcie dla ograniczeń kluczy obcych różni sie w różnych DBMS.
|
|
>
|
|
> SQLite nie wspiera ograniczeń kluczy obcych, lecz wciąż możesz deklarować
|
|
> ograniczenia, podczas tworzenia tabel. AR może wykorzystać te deklaracje
|
|
> aby prawidłowo wspierać relacyjne zapytania.
|
|
>
|
|
> MySQL wspiera ograniczenia kluczy obcych gdy używany jest silnik InnoDB,
|
|
> w przeciwieństwie do silnika MyISAM. Zalecamy zatem używanie InnoDB dla twoich
|
|
> baz danych MySQL.
|
|
> Podczas używania MyISAM, możesz wykorzystać następujący trik
|
|
> aby móc wykonywać relacyjne zapytania przy użyciu AR:
|
|
> ~~~
|
|
> [sql]
|
|
> CREATE TABLE Foo
|
|
> (
|
|
> id INTEGER NOT NULL PRIMARY KEY
|
|
> );
|
|
> CREATE TABLE bar
|
|
> (
|
|
> id INTEGER NOT NULL PRIMARY KEY,
|
|
> fooID INTEGER
|
|
> COMMENT 'CONSTRAINT FOREIGN KEY (fooID) REFERENCES Foo(id)'
|
|
> );
|
|
> ~~~
|
|
> Powyżej użyliśmy słowa kluczowego `COMMENT` aby opisać ograniczenia klucza obcego
|
|
> co może zostać przeczytanie przez AR aby rozponać opisywaną relację.
|
|
|
|
Deklarowanie relacji
|
|
----------------------
|
|
|
|
Zanim użyjemy AR aby wykonać relacyjne zapytanie, musimy powiedzieć AR jak
|
|
jedna klasa AR jest powiązana z drugą.
|
|
|
|
Relacja pomiędzy dwoma klasami AR jest bezpośrednio związana z relacją pomiędzy
|
|
tabelami bazy danych reprezentowanych przez klasę AR. Z punkty widzenia bazy danych
|
|
relacja pomiędzy dwoma tabelami A i B może występować w 3 wariantach:
|
|
jeden-do-wielu (ang. one-to-many; np. `User` i `Post`), jeden-do-jednego (ang. one-to-one;
|
|
np. `User` i `Profile`) oraz wiele-do-wielu (ang. many-to-many; np. `Category` i `Post`).
|
|
W AR występują cztery rodzaje relacji:
|
|
|
|
- `BELONGS_TO` (należy do): jeśli relacja pomiędzy tabelą A i B to jeden-do-jednego,
|
|
wtedy B należy do A (np. post `Post` należy do użytkownika `User`);
|
|
|
|
- `HAS_MANY` (posiada wiele): jeśli relacja pomiędzy tabelą A i B to jeden-do-wielu
|
|
wtedy A ma wiele B (np. użytkownik `User` ma wiele postów `Post`);
|
|
|
|
- `HAS_ONE` (posiada jedną): to jest specjalny przypadek relacji `HAS_MANY` gdzie A posiada
|
|
co najwyżej jedno B (np. użytkownik `User` ma co najwyżej jeden profil `Profile`);
|
|
|
|
- `MANY_MANY` (wiele do wielu): odpowiada relacji bazodanowej wiele-do-wielu.
|
|
Aby rozbić relację wiele-do-wielu na jeden-do-wielu potrzebna jest tablica asocjacyjna,
|
|
gdyż wiele DBMS nie wspiera bezpośrednio relacji wiele-do-wielu.
|
|
W naszym przykładzie schemat bazy danych `PostCategory` zostanie użyty w tym celu.
|
|
W terminologii AR, możemy wytłumaczyć. W terminologii AR, możemy wytłumaczyć relację
|
|
wiele-do-wielu jako kombinację `BELONGS_TO` oraz `HAS_MANY`. Na przykład,
|
|
post `Post` należy do wielu kategorii `Category` a kategoria `Category` posiada wiele postów `Post`.
|
|
|
|
Deklarowanie relacji w AR wymaga nadpisania metody [relations()|CActiveRecord::relations]
|
|
z [CActiveRecord]. Metoda zwraca tablicę konfiguracji relacji. Każdy element tablicy
|
|
reprezentuje pojedynczą relację zapisaną w następującym formacie:
|
|
|
|
~~~
|
|
[php]
|
|
'NazwaZmiennej'=>array('TypRelacji', 'NazwaKlasy', 'KluczObcy', ...dodatkowe opcje)
|
|
~~~
|
|
|
|
gdzie `NazwaZmiennej` jest nazwą relacji; `TypRelacji` specyfikuje typ relacji i posiada
|
|
jedną z czterech stałych wartości:
|
|
`self::BELONGS_TO`, `self::HAS_ONE`, `self::HAS_MANY` oraz
|
|
`self::MANY_MANY`; `NazwaKlasy` jest nazwą klasy AR powiązanej z tą klasą; oraz
|
|
`KluczObcy` określa klucz(e) obcy(e) powiązane z tą relacją. Dodatkowe opcje moga być
|
|
określone na końcu każdej relacji (więcej szczegółów w dalszej części).
|
|
|
|
Następujący kod pokazuje jak możemy zadeklarować relację dla klasy użytkownika `User`
|
|
oraz postu `Post`.
|
|
|
|
~~~
|
|
[php]
|
|
class Post extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'author'=>array(self::BELONGS_TO, 'User', 'authorID'),
|
|
'categories'=>array(self::MANY_MANY, 'Category', 'PostCategory(postID, categoryID)'),
|
|
);
|
|
}
|
|
}
|
|
|
|
class User extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'posts'=>array(self::HAS_MANY, 'Post', 'authorID'),
|
|
'profile'=>array(self::HAS_ONE, 'Profile', 'ownerID'),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
> Info|Info: Klucz obcy może być kluczem złożonym, zawierającym dwie lub więcej kolumn.
|
|
W takim przypadku, powinniśmy złączyć nazwy kolumn dla kluczy obcych rozdzielając je spacją
|
|
lub przecinkiem. Dla typu relacji `MANY_MANY`, nazwa tablicy asocjacyjnej musi
|
|
również być określona w kluczach obcych. Na przykład, relacja kategorii `categories` w `Post`
|
|
jest określona przez klucz obcy `PostCategory(postID, categoryID)`.
|
|
|
|
Deklaracja relacji w klasie AR domyślnie dodaje właściwość do klasy
|
|
dla każdej relacji. Po tym jak zapytanie relacyjne jest wykonywane, odpowiadająca
|
|
właściwość będzie wypełniona odpowiadającymi instancjami AR. Na przykład, jeśli `$author`
|
|
reprezentuje instancję AR `User`, możemy użyć `$author->posts` aby dostać się do
|
|
powiązanych instancji `Post`.
|
|
|
|
Wykonywanie zapytań relacyjnych
|
|
---------------------------
|
|
Najprostszym sposobem wykonania relacyjnego zapytania jest odczytanie relacyjnej
|
|
właściwości instancji AR. Jeśli właściwość nie była wcześniej odczytywana,
|
|
relacyjne zapytanie zostanie zainicjalizowane za pomocą którego złączymy dwie połączone z sobą
|
|
tabele i odfiltrujemy dane przy użyciu klucza głównego aktualnej instancji AR. Wynik zapytania
|
|
będzie zapisany we właściwości jako instancja(e) powiązanej klasy AR. Jest to znane
|
|
jako technika *opóźnionego ładowania* (ang. *lazy loading* approach), np. zapytanie
|
|
relacyjne jest wykonywane tylko wtedy, gdy powiązane obiekty są odczytywane są po
|
|
raz pierwszy. Poniższy przykład pokazuje jak używać tej techniki:
|
|
|
|
~~~
|
|
[php]
|
|
// zwróć post, którego ID wynosi 10
|
|
$post=Post::model()->findByPk(10);
|
|
// zwróć autora posta: tutaj będzie wykonane zapytanie relacyjne
|
|
$author=$post->author;
|
|
~~~
|
|
|
|
> Info|Info: Jeśli nie istnieją żadne powiązane instancje dla relacji,
|
|
odpowiednie właściwości mogą być pustą tablicą bądź wartością null.
|
|
Dla relacji `BELONGS_TO` oraz `HAS_ONE` wynikiem jest wartość null; dla
|
|
`HAS_MANY` oraz `MANY_MANY` jest to pusta tablica.
|
|
|
|
Zauważ, że relacje `HAS_MANY` oraz `MANY_MANY` zwracają tablicę obiektów, dlatego
|
|
będziesz musiał przejść w pętli przez zwrócony wynik, jeśli będziesz chciał uzyskać dostęp
|
|
do jakiejkolwiek właściwości obiektu. W przeciwnym przypadku, możesz uzyskać błąd: "Próbujesz
|
|
skorzystać z właściwości elementu nie będącego obiektem" (ang. "Trying to get property of non-object" errors).
|
|
|
|
Technika opóźnionego ładowania jest bardzo poręczne w użyciu, ale nie zawsze jest wydajne
|
|
we wszystkich scenariuszach. Dla przykładu, jeśli chcemy uzyskać dostęp do informacje o autorze dla
|
|
`N` postów, używając techniki opóźnionego ładowania wykonamy `N` zapytań z użyciem join.
|
|
W tych warunkach powinniśmy uciec się do tak zwanej techniki *gorliwego ładowania*
|
|
(ang. eager loading approach).
|
|
|
|
Technika gorliwego ładowania zwraca powiązane instancje AR razem z główną(ymi) instancją(ami) AR.
|
|
Osiągamy to poprzez użycie metody [with()|CActiveRecord::with] razem z jedną z metod AR
|
|
[find|CActiveRecord::find] lub [findAll|CActiveRecord::findAll]. Na przykład,
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('author')->findAll();
|
|
~~~
|
|
|
|
Powyższy kod zwróci tablicę instancji `Post`. W odróżnieniu od techniki leniwego ładowania
|
|
właściwość `author` w każdej instancji `Post` jest już wypełniona odpowiednią instancją
|
|
`User` przed jej pierwszym odczytaniem. Zamiast wywoływania zapytania z join dla
|
|
każdego posty, technika gorliwego ładowania zwraca wszystkie posty razem wraz z autorami
|
|
za pomocą jednego zapytania z join!
|
|
|
|
Możemy używać jednocześnie wielu nazw relacji w metodzie [with()|CActiveRecord::with]
|
|
a technika gorliwego łączenia dostarczy nam je z powrotem za jednym razem.
|
|
Na przykład, następujący kod zwróci posty razem z ich autorami oraz kategoriami:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('author','categories')->findAll();
|
|
~~~
|
|
|
|
Możemy również korzystać z zagnieżdżonych gorliwych ładowań. Zamiast listy nazw relacji,
|
|
przekazujemy w postaci hierarchicznej nazwy relacji do metody [with()|CActiveRecord::with]
|
|
w następujący sposób:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with(
|
|
'author.profile',
|
|
'author.posts',
|
|
'categories')->findAll();
|
|
~~~
|
|
|
|
Powyższy przykład zwróci wszystkie posty razem z ich autorami oraz kategoriami.
|
|
Zwróci również dla każdego autora profil oraz posty.
|
|
|
|
> Note|Uwaga: Sposób użycia metody [with()|CActiveRecord::with] został zmieniony
|
|
> wraz z wersją 1.0.2. Proszę przeczytać z uwagą odpowiadającą jej dokumentację API.
|
|
|
|
Implementacja AR w Yii jest bardzo wydajna. Jeśli gorliwie ładujemy hierarchię powiązanych
|
|
obiektów zawierających `N`-relacji `HAS_MANY` lub `MANY_MANY`, aby uzyskać pożądany rezultat
|
|
zostanie wykonanych `N+1` zapytań SQL. Oznacza to, że w ostatnim przykładzie
|
|
potrzebujemy wykonać 3 zapytania SQL, ponieważ występują tam właściwości `posts`
|
|
oraz `categories`. Pozostałe frameworki mają bardziej radykalne podejście używając
|
|
tylko jedno zapytanie SQL. Na pierwszy rzut oka, to radykalne podejście wygląda bardziej
|
|
wydajnie, ponieważ parsowanych i wykonywanych przez DBMS jest mniej zapytań.
|
|
W rzeczywistości jest to niepraktyczne z dwóch powodów. Po pierwsze, występowanie
|
|
wielu powtarzających kolumn danych w rezultacie zapytania, co zajmuje dodatkowy czas
|
|
podczas przetwarzania oraz przesyłania. Po drugie, liczba wierszy w zwracanych wzrasta
|
|
wykładniczo wraz z zwiększającą się ilością tabel, co czyni je po prostu trudnymi
|
|
w zarządzaniu wraz ze wzrostem ilości relacji.
|
|
|
|
Od wersji 1.0.2, możesz również zmusić zapytanie relacyjne do wykonania tylko jednego
|
|
zapytania SQL. Po prostu dołącz wywołanie metody [together()|CActiveFinder::together]
|
|
po [with()|CActiveRecord::with]. Na przykład,
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with(
|
|
'author.profile',
|
|
'author.posts',
|
|
'categories')->together()->findAll();
|
|
~~~
|
|
|
|
Powyższe zapytanie wykona się w jednym zapytaniu SQL. Bez wywołania metody
|
|
[together|CActiveFinder::together], wymagane będą trzy zapytania SQL: jedno
|
|
łączące tabele `Post`, `User` oraz `Profile`, drugie połączy tabele `User` i `Post`
|
|
a trzecie połączy tabele `Post`, `PostCategory` oraz `Category`.
|
|
|
|
Opcje zapytań relacyjnych
|
|
------------------------
|
|
|
|
Wspominaliśmy, że dodatkowe opcje mogą być dookreślone w deklaracji relacji.
|
|
Opcje te zapisane jako pary nazw i wartości używane są w celu dostosowania do własnych
|
|
potrzeb zapytań relacyjnych. Poniżej znajdziemy ich zestawienie.
|
|
|
|
- `select`: lista kolumn, które będą zwrócone dla powiązanej klasy AR.
|
|
Domyślną wartością jest '*', oznaczająca wszystkie kolumny. Nazwy kolumn powinny zostać
|
|
rozróżnione za pomocą `aliasToken` (tokenu aliasu) jeśli pojawiają się w wyrażeniu (np.
|
|
`COUNT(??.name) AS nameCount`).
|
|
|
|
- `condition`: klauzula `WHERE`. Domyślnie pusta. Zauważ, że referencje do kolumn
|
|
powinny zostać rozróżnione przy użyciu `aliasToken` (np. `??.id=10`).
|
|
|
|
- `params`: parametry, które zostaną przypięte do wygenerowanego wyrażenia SQL.
|
|
Powinny zostać przekazane jako tablica par nazwa-wartość. Opcja ta jest dostępna
|
|
od wersji 1.0.3.
|
|
|
|
- `on`: klauzula `ON`. Warunki określone tutaj będą dołączone do warunków złączenia
|
|
przy użyciu operatora `AND`. Zauważ, ze referencje kolumn muszą zostać ujednoznacznione poprzez użycie
|
|
`tokenu aliasu` (np. `??.id=10`). Opcja ta jest dostępna od wersji 1.0.2.
|
|
|
|
- `order`: klauzula `ORDER BY`. Domyślnie pusta. Zauważ, że referencje do kolumn
|
|
powinny zostać rozróżnione przy użyciu `aliasToken` (np. `??.age
|
|
DESC`).
|
|
|
|
- `with`: lista obiektów potomnych, które powinny zostać załadowane wraz z tym
|
|
obiektem. Należy pamiętać, że niewłaściwe użycie tej opcji może skutkować utworzeniem
|
|
nigdy niekończącej się pętli.
|
|
|
|
- `joinType`: typ złączenia dla relacji. Domyślna wartość to `LEFT OUTER JOIN`.
|
|
|
|
- `aliasToken`: symbol zastępczy (ang. placeholder) dla prefiksu kolumny. Zostanie zastąpiony
|
|
przez odpowiedni alias tabeli w celu rozróżnienia referencji do kolumn.
|
|
Domyślnie `'??.'`.
|
|
|
|
- `alias`: alias dla tablicy powiązanej z relacją. Opcja ta jest dostępna
|
|
od wersji 1.0.1. Domyślnie posiada wartość null, co oznacza iż alias tablicy
|
|
jest generowany automatycznie. Opcja ta różni się od `aliasToken` gdyż ta ostatnia
|
|
jest tylko symbolem zastępczym i będzie zastąpiona przez aktualny alias tabeli.
|
|
|
|
- `together`: jeśli tabele powiązane mają zostać zmuszone do połączenia się razem
|
|
z główną tabelą. Opcja ta ma znaczenie dla relacji HAS_MANY oraz MANY_MANY.
|
|
Jeśli opcja ta nie jest ustawiona lub ma wartość false, każda relacja HAS_MANY
|
|
lub MANY_MANY będzie posiadała własne wyrażenie JOIN w celu zwiększenia wydajności.
|
|
Opcja ta jest dostępna od wersji 1.0.3.
|
|
|
|
- `group`: klauzula `GROUP BY`. Domyślnie pusta. Zauważ, że referencje do kolumn
|
|
muszą zostać rozróżnione przy użyciu `aliasToken` (np. `??.age`).
|
|
|
|
- `having`: klauzula `HAVING`. Domyślnie pusta. Zauważ, że reference do kolumn
|
|
muszą zostać rozróżnione przy użyciu `aliasToken` (np. `??.age`). Uwaga, opcja ta jest dostępna
|
|
od wersji 1.0.1.
|
|
|
|
- `index`: nazwa kolumny, której wartość powinna zostać użyta jako klucz tablicy, która zawiera powiązane obiekty.
|
|
Bez ustawienia tej opcji, tablica obiektów powiązanych będzie używała indeksu całkowitoliczbowego rozpoczynającego się od liczby zero.
|
|
Opcja ta może zotać ustawiona tylko dla relacji `HAS_MANY` oraz `MANY_MANY`. Opcja ta jest dostępna od wesji 1.0.7.
|
|
|
|
Dodatkowo, następujące opcje są dostępne dla wybranych relacji podczas opóźnionego
|
|
ładowania:
|
|
|
|
- `limit`: ogranicza ilość zwracanych wierszy. Opcja ta nie ma zastosowania
|
|
dla relacji `BELONGS_TO`.
|
|
|
|
- `offset`: offset dla zwracanych wierszy. Opcja ta nie ma zastosowania
|
|
dla relacji `BELONGS_TO`.
|
|
|
|
Poniżej zmodyfikujemy deklarację relacji `posts` w klasie `User` poprzez
|
|
wybranie części z powyższych opcji.
|
|
|
|
~~~
|
|
[php]
|
|
class User extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'posts'=>array(self::HAS_MANY, 'Post', 'authorID'
|
|
'order'=>'??.createTime DESC',
|
|
'with'=>'categories'),
|
|
'profile'=>array(self::HAS_ONE, 'Profile', 'ownerID'),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
Teraz jeśli odczytamy `$author->posts`, powinniśmy otrzymać autorów postów
|
|
posortowanych malejąco wg czasu ich utworzenia. Każda instancja postu będzie
|
|
również posiadała wczytaną odpowiadającą jej kategorię.
|
|
|
|
> Info|Info: Kiedy nazwa kolumny pojawia się w dwóch lub więcej tabelach, które są złączane,
|
|
należy je rozróżnić. Robi się to za pomocą dodania prefiksu do nazwy kolumny zawierającego
|
|
nazwę tabeli kolumny. Na przykład, `id` staje się `Team.id`. Jednakże w relacyjnych
|
|
zapytaniach AR nie mamy takiej możliwości ponieważ wyrażenia SQL są generowane automatycznie
|
|
przez AR, które dodaje systematycznie do każdej tabeli alias. Dlatego też,
|
|
w celu uniknięcia konfliktu nazw kolumn używamy symbolów zastępczych do wskazania
|
|
występowania kolumn, które powinny zostać rozróżnione. AR zastąpi symbole zastępcze
|
|
odpowiednim aliasem tabeli i prawidłowo rozróżni kolumny.
|
|
and properly disambiguate the column.
|
|
|
|
|
|
Dynamiczne opcje pytań relacyjnych
|
|
--------------------------------
|
|
|
|
Wraz z wersją 1.0.2 możemy używać dynamicznych opcji zapytań relacyjnych zarówno w metodzie
|
|
[with()|CActiveRecord::with] jak i opcji `with`. Dynamiczne opcje nadpiszą istniejące
|
|
opcje tak jak zostało to określone w metodzie [relations()|CActiveRecord::relations].
|
|
Na przykład, dla powyższego modelu `User`, jeśli chcemy używać techniki gorliwego ładowania
|
|
aby zwrócić posty należące do autora sortujące jest rosnąco
|
|
(opcja `order` w specyfikacji relacji jest malejąca), możemy zrobić to następująco:
|
|
|
|
~~~
|
|
[php]
|
|
User::model()->with(array(
|
|
'posts'=>array('order'=>'??.createTime DESC'),
|
|
'profile',
|
|
))->findAll();
|
|
~~~
|
|
|
|
Poczynając od wersji 1.0.5, opcje dynamicznych zapytań mogą być również używane dla leniwego ładowania
|
|
w celu wykonywania zapytań relacyjnych. Aby to zrobić, powinniśmy wywołać metodę, której nazwa jest identyczna
|
|
z nazwą relacji oraz przekazać opcje dynamicznego zapytania jako parametr metody.
|
|
Na przykład, następujący kod zwróci posty użytkownika, których `status` wynosi 1:
|
|
|
|
~~~
|
|
[php]
|
|
$user=User::model()->findByPk(1);
|
|
$posts=$user->posts(array('condition'=>'status=1'));
|
|
~~~
|
|
|
|
|
|
|
|
Zapytania statystyczne (ang. Statistical Query)
|
|
-----------------
|
|
|
|
> Note|Uwaga: Zapytania statystyczne są wspierane od wersji 1.0.4
|
|
|
|
Poza zapytaniami relacyjnymi opisanymi powyżej, Yii wspiera również tak zwane zapytania statystyczne (lub zapytania agregacyjne). Odnoszą się one do uzyskiwania agregowanych
|
|
informacji o powiązanych obiektach, takich jak liczba komentarzy dla każdego postu, średni ocena dla każdego produktu, itp. Zapytania statystyczne mogą być wykonywane dla obiektów
|
|
wskazywanych w relacji `HAS_MANY` (np. post ma wiele komentarzy) lub `MANY_MANY` (np. post należy do wielu kategorii a kategoria ma wiele postów).
|
|
|
|
Wykonywania zapytań statystycznych jest bardzo podobne do wykonywania opisanych wcześniej zapytań relacyjnych. Najpierw musimy zdefiniować zapytanie statystyczne w metodzie
|
|
[relations()|CActiveRecord::relations] klasy [CActiveRecord] tak jak robimy to dla zapytań relacyjnych.
|
|
|
|
|
|
~~~
|
|
[php]
|
|
class Post extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'commentCount'=>array(self::STAT, 'Comment', 'postID'),
|
|
'categoryCount'=>array(self::STAT, 'Category', 'PostCategory(postID, categoryID)'),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
Powyżej zadeklarowaliśmy dwa zapytania statystyczne: `commentCount` oblicza ilość komentarzy należących do postu a `categoryCount` oblicza ilość kategorii do których należy post.
|
|
Zauważ, że relacja pomiędzy `Post` a `Comment` jest relacją `HAS_MANY`, podczas gdy relacja pomiędzy `Post` a `Category` jest relacją `MANY_MANY`
|
|
(wraz z łączącą je tabelą `PostCategory`). Jak możemy zauważyć, deklaracja jest bardzo podobna do tych z relacji opisywanych we wcześniejszych podpunktach.
|
|
Jedyną różnicą jest to, że typem relacji jest tutaj `STAT`.
|
|
|
|
Przy użyciu powyższej deklaracji możemy otrzymać ilość komentarzy dla postu przy użyciu wyrażenia `$post->commentCount`. Jeśli użyjemy tej właściwości po raz pierwszy,
|
|
wyrażenie SQL zostanie wywołane w ukryciu w celu uzyskania pożądanego rezultatu. Jak już wiemy, jest to tak zwane *leniwe ładowanie*.
|
|
Możemy również używać *gorliwego ładowania* jeśli chcemy dowiedzieć uzyskać ilość komentarzy dla wielu postów.
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('commentCount', 'categoryCount')->findAll();
|
|
~~~
|
|
|
|
Powyższe wyrażenie wykona trzy zapytania SQL aby zwrócić wszystkie posty razem z policzonymi komentarzami oraz policzonymi kategoriami. Używając podejścia leniwego ładowania
|
|
skończylibyśmy z `2*N+1` zapytaniami SQL, gdzie `N` to ilość postów.
|
|
|
|
Domyślnie zapytanie statystyczne użyje wyrażenia `COUNT` do obliczeń (i w ten sposób policzymy komentarze oraz kategorie w powyższych przykładach). Możemy je dostosować
|
|
do własnych potrzeb poprzez określenie dodatkowych opcji podczas deklarowania go w metodzie [relations()|CActiveRecord::relations]. Podsumowanie dostępnych opcji znajduje się poniżej.
|
|
|
|
|
|
- `select`: wyrażenie statystyczne. Domyślnie `COUNT(*)`, co oznacza liczenie obiektów potomnych.
|
|
|
|
- `defaultValue`: wartość jaka będzie przypisana do tych rekordów, dla których nie zostaną zwrócone rezultaty zapytania statystycznego. Na przykład, jeśli post nie posiada
|
|
żadnych komentarzy, jego `commentCount` otrzyma tą wartość. Wartość domyślna dla tej opcji to zero.
|
|
|
|
- `condition`: klauzula `WHERE`. Domyślnie jest pusta.
|
|
|
|
- `params`: parametry, które mają zostać związane z wygenerowanym zapytaniem SQL. Powinny być one przekazane jako tablica par nazwa-wartość.
|
|
|
|
- `order`: klauzula `ORDER BY`. Domyślnie jest pusta.
|
|
|
|
- `group`: klauzula `GROUP BY`. Domyślnie jest pusta.
|
|
|
|
- `having`: klauzula `HAVING`. Domyślnie jest pusta.
|
|
|
|
|
|
Relacyjne zapytania z nazwanymi podzbiorami
|
|
----------------------------------
|
|
|
|
> Note|Uwaga: Wsparcie dla nazwanych podzbiorów zostało wprowadzone wraz z wersją 1.0.5.
|
|
|
|
Relacyjne zapytania mogą być wykonywane w połączeniu z [nazwanymi zbiorami](/doc/guide/database.ar#named-scopes).
|
|
Rozróżniamy dwa przypadki. W pierwszym, nazwany podzbiór stosowany jest do głównego modelu. W drugim, nazwany podzbiór stosowany jest
|
|
do powiązanego modelu.
|
|
|
|
Następujący kod pokazuje, jak zastosować nazwane podzbiory do głównego modelu.
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->published()->recently()->with('comments')->findAll();
|
|
~~~
|
|
|
|
Przypomina to bardziej nierelacyjne zapytania. Jedyna różnica polega na tym, że mamy wywołanie metody `with()`
|
|
po łańcuchu wywołań nazwanych podzbiorów. Zapytanie zwróci ostatnio opublikowane posty wraz z ich komentarzami.
|
|
|
|
Następujący kod pokazuje, jak zastosować nazwane podzbiory do modelu powiązanego.
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('comments:recently:approved')->findAll();
|
|
~~~
|
|
|
|
Powyższe zapytanie zwróci wszystkie posty wraz z zatwierdzonymi komentarzami. Zauważ, że `comments` jest referencją
|
|
do nazwy relacji, a `recently` oraz `approved` referuje do dwóch nazwanych podzbiorów w klasie modelu `Comment`.
|
|
Nazwa relacji oraz nazwane podzbiory powinny być rozdzielone dwukropkiem.
|
|
|
|
Nazwane podzbiory mogą również zostać zdefiniowane w opcji `with` reguły relacyjnej zadeklarowanej
|
|
w [CActiveRecord::relations()]. W następnym przykładzie, jeśli odczytamy `$user->posts`,
|
|
zwróci nam wszystkie zatwierdzone komentarze (*approved*) postu.
|
|
|
|
~~~
|
|
[php]
|
|
class User extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'posts'=>array(self::HAS_MANY, 'Post', 'authorID',
|
|
'with'=>'comments:approved'),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
> Note|Uwaga: Nazwane podzbiory zastosowane dla modelów w relacji muszą zostać określone
|
|
w [CActiveRecord::scopes]. W rezultacie, nie mogą one zostać sparametryzowane.
|
|
|
|
|
|
<div class="revision">$Id: database.arr.txt 1248 2009-07-15 19:40:44Z qiang.xue $</div> |