From 087703d2256e92a393a1652f01d9251f79976c9e Mon Sep 17 00:00:00 2001 From: Bizley Date: Mon, 17 Aug 2015 14:40:30 +0200 Subject: [PATCH 1/8] db-active-record 25% --- docs/guide-pl/README.md | 10 +- docs/guide-pl/db-active-record.md | 1413 ++++++++++++++++++++++++ docs/guide-pl/intro-upgrade-from-v1.md | 12 +- 3 files changed, 1424 insertions(+), 11 deletions(-) create mode 100644 docs/guide-pl/db-active-record.md diff --git a/docs/guide-pl/README.md b/docs/guide-pl/README.md index d5c238a4e4..3aaf0a2ce7 100644 --- a/docs/guide-pl/README.md +++ b/docs/guide-pl/README.md @@ -75,9 +75,9 @@ Kluczowe koncepcje Praca z bazami danych --------------------- -* [Obiekt dostępu do danych (DAO)](db-dao.md): Łączenie z bazą, podstawowe zapytania, transakcje i manipulacja schematem. +* [Obiekty dostępu do danych (DAO)](db-dao.md): Łączenie z bazą, podstawowe zapytania, transakcje i manipulacja schematem. * [Konstruktor kwerend](db-query-builder.md): Zapytania do bazy danych z użyciem warstwy abstrakcyjnej. -* [Rekord aktywny](db-active-record.md): ORM Rekordu aktywnego, otrzymywanie i manipulacja rekordami oraz definiowanie relacji. +* [Active Record](db-active-record.md): Active Record ORM, otrzymywanie i manipulacja rekordami oraz definiowanie relacji. * [Migracje](db-migrations.md): Użycie systemu kontroli wersji na twoich bazach danych podczas tworzenia aplikacji w grupie. * [Sphinx](db-sphinx.md) * [Redis](db-redis.md) @@ -203,7 +203,7 @@ Uwagi do polskiego tłumaczenia przewodnika ------------------------------------------ Niektóre z użytych w tym przewodniku programistycznych nazw zostały celowo spolszczone, w przypadku, gdy -w literaturze popularnej nie występują ich polskie odpowiedniki. Mam nadzieję, że czytelnik wybaczy mi okazjonalne -"settery", "gettery" i "traity", które umieszczam tutaj licząc na powszechne zrozumienie tych terminów w polskiej -społeczności programistycznej. Jednocześnie spolszczenia/tłumaczenia niektórych terminów, jak "Fixtures", odmawiam na razie +w literaturze popularnej nie występują ich polskie odpowiedniki. Mam nadzieję, że czytelnik wybaczy okazjonalne +"settery", "gettery" i "traity", które umieszczamy tutaj licząc na powszechne zrozumienie tych terminów w polskiej +społeczności programistycznej. Jednocześnie spolszczenia/tłumaczenia niektórych terminów, jak "Fixtures", odmawiamy na razie całkowicie, licząc na to, że język polski w końcu nadgoni lub wchłonie, w ten, czy inny sposób, techniczne nowości. diff --git a/docs/guide-pl/db-active-record.md b/docs/guide-pl/db-active-record.md new file mode 100644 index 0000000000..fd75d331e6 --- /dev/null +++ b/docs/guide-pl/db-active-record.md @@ -0,0 +1,1413 @@ +Active Record +============= + +[Active Record](http://en.wikipedia.org/wiki/Active_record_pattern) zapewnia zorientowany obiektowo interfejs dostępu i manipulacji danymi +zapisanymi w bazie danych. Klasa typu Active Record jest powiązana z tabelą bazodanową, a instacja tej klasy odpowiada pojedynczemu wierszowi +w tabeli - *atrybut* obiektu Active Record reprezentuje wartość konkretnej kolumny w tym wierszu. Zamiast pisać bezpośrednie kwerendy bazy danych, +można skorzystać z atrybutów i metod klasy Active Record. + +Dla przykładu, załóżmy, że `Customer` jest klasą Active Record, powiązaną z tabelą `customer` i `name` jest kolumną w tabeli `customer`. +Aby dodać nowy wiersz do tabeli `customer`, należy wykonać następujący kod: + +```php +$customer = new Customer(); +$customer->name = 'Qiang'; +$customer->save(); +``` + +Kod z przykładu jest odpowiednikiem poniższej komendy SQL dla MySQL, która jest mniej intuicyjna, bardziej podatna na błędy i, co bardzo +prawdopodobne, niekompatybilna z innymi rodzajami baz danych: + +```php +$db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [ + ':name' => 'Qiang', +])->execute(); +``` + +Yii zapewnia wsparcie Active Record dla następujących typów relacyjnych baz danych: + +* MySQL 4.1 lub nowszy: poprzez [[yii\db\ActiveRecord]] +* PostgreSQL 7.3 lub nowszy: poprzez [[yii\db\ActiveRecord]] +* SQLite 2 i 3: poprzez [[yii\db\ActiveRecord]] +* Microsoft SQL Server 2008 lub nowszy: poprzez [[yii\db\ActiveRecord]] +* Oracle: poprzez [[yii\db\ActiveRecord]] +* CUBRID 9.3 lub nowszy: poprzez [[yii\db\ActiveRecord]] (Zwróć uwagę, że z powodu [błędu](http://jira.cubrid.org/browse/APIS-658) + w rozszerzeniu PDO cubrid, umieszczanie wartości w cudzysłowie nie będzie działać, zatem wymagane jest zainstalowanie CUBRID 9.3 zarówno + jako klienta jak i serwer) +* Sphinx: poprzez [[yii\sphinx\ActiveRecord]], wymaga rozszerzenia `yii2-sphinx` +* ElasticSearch: poprzez [[yii\elasticsearch\ActiveRecord]], wymaga rozszerzenia `yii2-elasticsearch` + +Dodatkowo Yii wspiera również Active Record dla następujących baz danych typu NoSQL: + +* Redis 2.6.12 lub nowszy: poprzez [[yii\redis\ActiveRecord]], wymaga rozszerzenia `yii2-redis` +* MongoDB 1.3.0 lub nowszy: poprzez [[yii\mongodb\ActiveRecord]], wymaga rozszerzenia `yii2-mongodb` + +W tej sekcji przewodnika opiszemy sposób użycia Active Record dla baz relacyjnych, jednakże większość zagadnień można zastosować również dla NoSQL. + + +## Deklarowanie klas Active Record + +Na początek zadeklaruj klasę typu Active Record rozszerzając [[yii\db\ActiveRecord]]. Ponieważ każda klasa Active Record +jest powiązana z tabelą bazy danych, należy nadpisać metodę [[yii\db\ActiveRecord::tableName()|tableName()]], aby wskazać +odpowiednią tabelę. + +W poniższym przykładzie deklarujemy klasę Active Record nazwaną `Customer` dla tabeli `customer` w bazie danych. + +```php +namespace app\models; + +use yii\db\ActiveRecord; + +class Customer extends ActiveRecord +{ + const STATUS_INACTIVE = 0; + const STATUS_ACTIVE = 1; + + /** + * @return string nazwa tabeli powiązanej z klasą ActiveRecord. + */ + public static function tableName() + { + return 'customer'; + } +} +``` + +Instancje Active Record są traktowane jak [modele](structure-models.md). Z tego powodu zwykle dodajemy klasy Active Record +do przestrzeni nazw `app\models` (lub innej, przeznaczonej dla klas modeli). + +Dzięki temu, że [[yii\db\ActiveRecord]] rozszerza [[yii\base\Model]], dziedziczy *wszystkie* funkcjonalności [modelu](structure-models.md), +takie jak atrybuty, zasady walidacji, serializację danych itd. + + +## Łączenie się z bazą danych + +Domyślnie Active Record używa [komponentu aplikacji](structure-application-components.md) `db` jako [[yii\db\Connection|łącznika z bazą]], +do uzyskania dostępu i manipulowania danymi z bazy danych. Jak zostało to już wyjaśnione w sekcji [Obiekty dostępu do danych (DAO)](db-dao.md), +możesz skonfigurować komponent `db` w pliku konfiguracyjnym aplikacji jak poniżej: + +```php +return [ + 'components' => [ + 'db' => [ + 'class' => 'yii\db\Connection', + 'dsn' => 'mysql:host=localhost;dbname=testdb', + 'username' => 'demo', + 'password' => 'demo', + ], + ], +]; +``` + +Jeśli chcesz użyć innego połączenia do bazy danych niż za pomocą komponentu `db`, musisz nadpisać metodę [[yii\db\ActiveRecord::getDb()|getDb()]]: + +```php +class Customer extends ActiveRecord +{ + // ... + + public static function getDb() + { + // użyj komponentu aplikacji "db2" + return \Yii::$app->db2; + } +} +``` + + +## Kwerendy + +Po zadeklarowaniu klasy Active Record, możesz użyć jej do pobrania danych z powiązanej tabeli bazy danych. +Proces ten zwykle sprowadza się do następujących trzech kroków: + +1. Stworzenie nowego obiektu kwerendy za pomocą metody [[yii\db\ActiveRecord::find()]]; +2. Zbudowanie obiektu kwerendy za pomocą [metod konstruktora kwerend](db-query-builder.md#building-queries); +3. Wywołanie [metod kwerendy](db-query-builder.md#query-methods) w celu uzyskania danych jako instancji klasy Active Record. + +Jak widać, procedura jest bardzo podobna do tej używanej przy [konstruktorze kwerend](db-query-builder.md). Jedyna różnica jest taka, że +zamiast użycia operatora `new` do stworzenia obiektu kwerendy, wywołujemy metodę [[yii\db\ActiveRecord::find()]], która zwraca +nowy obiekt kwerendy klasy [[yii\db\ActiveQuery]]. + +Poniżej znajdziesz kilka przykładów pokazujących jak używać Active Query do pobierania danych: + +```php +// zwraca pojedynczego klienta o ID 123 +// SELECT * FROM `customer` WHERE `id` = 123 +$customer = Customer::find() + ->where(['id' => 123]) + ->one(); + +// zwraca wszystkich aktywnych klientów posortowanych po ID +// SELECT * FROM `customer` WHERE `status` = 1 ORDER BY `id` +$customers = Customer::find() + ->where(['status' => Customer::STATUS_ACTIVE]) + ->orderBy('id') + ->all(); + +// zwraca liczbę aktywnych klientów +// SELECT COUNT(*) FROM `customer` WHERE `status` = 1 +$count = Customer::find() + ->where(['status' => Customer::STATUS_ACTIVE]) + ->count(); + +// zwraca wszystkich klientów w tablicy zaindeksowanej wg ID +// SELECT * FROM `customer` +$customers = Customer::find() + ->indexBy('id') + ->all(); +``` + +W powyższych przykładach `$customer` jest obiektem typu `Customer`, a `$customers` jest tablicą obiektów typu `Customer`. W obu przypadkach +dane pobrane są z tabeli `customer`. + +> Info: Dzięki temu, że [[yii\db\ActiveQuery]] rozszerza klasę [[yii\db\Query]], możesz użyć *wszystkich* metod dotyczących kwerend i ich budowania + opisanych w sekcji [Konstruktor kwerend](db-query-builder.md). + +Ponieważ zwykle kwerendy korzystają z zapytań zawierających klucz główny lub też zestaw wartości dla kilku kolumn, Yii udostępnia dwie skrótowe metody, +pozwalające na szybsze ich użycie: + +- [[yii\db\ActiveRecord::findOne()]]: zwraca pojedynczą instancję klasy Active Record, zawierającą dane z pierwszego pobranego odpowiadającego zapytaniu +wiersza danych. +- [[yii\db\ActiveRecord::findAll()]]: zwraca tablicę instancji klasy Active Record zawierających *wszystkie* wyniki zapytania. + +Obie metody mogą przyjmować jeden z następujących formatów parametrów: + +- wartość skalarna: wartość jest traktowana jako wartość klucza głównego, który należy odszukać. Yii automatycznie ustali, która kolumna jest kluczem + głównym, odczytując informacje ze schematu bazy. +- tablica wartości skalarnych: tablica jest traktowana jako lista poszukiwanych wartości klucza głównego. +- tablica asocjacyjna: klucze tablicy są poszukiwanymi nazwami kolumn a wartości tablicy są odpowiadającymi im wartościami kolumn. Po więcej + szczegółów zajrzyj do rozdziału [Format asocjacyjny](db-query-builder.md#hash-format). + +Poniższy kod pokazuje jak mogę być użyte opisane metody: + +```php +// zwraca pojedynczego klienta o ID 123 +// SELECT * FROM `customer` WHERE `id` = 123 +$customer = Customer::findOne(123); + +// zwraca klientów o ID 100, 101, 123 i 124 +// SELECT * FROM `customer` WHERE `id` IN (100, 101, 123, 124) +$customers = Customer::findAll([100, 101, 123, 124]); + +// zwraca aktywnego klienta o ID 123 +// SELECT * FROM `customer` WHERE `id` = 123 AND `status` = 1 +$customer = Customer::findOne([ + 'id' => 123, + 'status' => Customer::STATUS_ACTIVE, +]); + +// zwraca wszystkich nieaktywnych klientów +// SELECT * FROM `customer` WHERE `status` = 0 +$customers = Customer::findAll([ + 'status' => Customer::STATUS_INACTIVE, +]); +``` + +> Uwaga: Ani metoda [[yii\db\ActiveRecord::findOne()]] ani [[yii\db\ActiveQuery::one()]] nie dodaje `LIMIT 1` do wygenerowanej + kwerendy SQL. Jeśli zapytanie może zwrócić więcej niż jeden wiersz danych, należy wywołać bezpośrednio `limit(1)`, w celu zwiększenia + wydajności aplikacji, np. `Customer::find()->limit(1)->one()`. + +Oprócz korzystania z metod konstruktora kwerend możesz również użyć surowych zapytań SQL w celu pobrania danych do obiektu Active Record za +pomocą metody [[yii\db\ActiveRecord::findBySql()]]: + +```php +// zwraca wszystkich nieaktywnych klientów +$sql = 'SELECT * FROM customer WHERE status=:status'; +$customers = Customer::findBySql($sql, [':status' => Customer::STATUS_INACTIVE])->all(); +``` + +Nie wywołuj dodatkowych metod konstruktora kwerend po wywołaniu [[yii\db\ActiveRecord::findBySql()|findBySql()]], ponieważ zostaną one pominięte. + + +## Dostęp do danych + +Jak wspomniano wyżej, dane pobrane z bazy danych są dostępne w obiekcie Active Record i każdy wiersz wyniku zapytania odpowiada pojedynczej +instancji Active Record. Możesz odczytać wartości kolumn odwołując się do atrybutów obiektu Active Record, dla przykładu: + +```php +// "id" i "email" są nazwami kolumn w tabeli "customer" +$customer = Customer::findOne(123); +$id = $customer->id; +$email = $customer->email; +``` + +> Uwaga: nazwy atrybutów Active Record odpowiadają nazwom powiązanych z nimi kolumn z uwzględnieniem wielkości liter. + Yii automatycznie definiuje atrybut Active Record dla każdej kolumny powiązanej tabeli. + NIE należy definiować ich własnoręcznie. + +Ponieważ atrybuty Active Record nazywane są zgodnie z nazwami kolumn, możesz natknąć się na kod PHP typu +`$customer->first_name`, gdzie podkreślniki używane są do oddzielenia poszczególnych słów w nazwach atrybutów, jeśli kolumny tabeli nazywane są +właśnie w ten sposób. Jeśli masz wątpliwości dotyczące spojności takiego stylu programowania, powinieneś zmienić odpowiednio nazwy kolumn tabeli +(używając np. camelCase). + + +### Transformacja danych + +Często zdarza się, że dane wprowadzane i/lub wyświetlane zapisane są w formacie różniącym się od tego używanego w bazie danych. Dla przykładu, +w bazie danych przechowywane są daty urodzin klientów jako uniksowe znaczniki czasu, podczas gdy w większości przypadków pożądana forma zapisu daty +to `'RRRR/MM/DD'`. Aby osiągnąć ten cel, można zdefiniować metody *transformujące dane* w klasie `Customer` +Active Record class like the following: + +```php +class Customer extends ActiveRecord +{ + // ... + + public function getBirthdayText() + { + return date('Y/m/d', $this->birthday); + } + + public function setBirthdayText($value) + { + $this->birthday = strtotime($value); + } +} +``` + +Od tego momentu, w kodzie PHP, zamiast odwołać się do `$customer->birthday`, można użyć `$customer->birthdayText`, co pozwala na +wprowadzenie i wyświetlenie daty urodzin klienta w formacie `'RRRR/MM/DD'`. + +> Wskazówka: Powyższy przykład pokazuje podstawowy sposób transformacji danych. Podczas zwyczajowej pracy z formularzami danych można skorzystać z +> [DateValidator](tutorial-core-validators.md#date) i [[yii\jui\DatePicker|DatePicker]], co jest prostsze w użyciu i posiadające więcej możliwości. + + +### Pobieranie danych jako tablice + +Pobieranie danych jako obiekty Active Record jest wygodne i elastyczne, ale nie zawsze pożądane, zwłaszcza kiedy konieczne jest +uzyskanie ogromnej liczby danych, z powodu użycia sporej ilości pamięci. W takim przypadku można pobrać dane jako tablicę PHP wywołując metodę +[[yii\db\ActiveQuery::asArray()|asArray()]] przed wykonaniem kwerendy: + +```php +// zwraca wszystkich klientów +// każdy klient jest zwracany w postaci tablicy asocjacyjnej +$customers = Customer::find() + ->asArray() + ->all(); +``` + +> Uwaga: Powyższy sposób zwiększa wydajność aplikacji i pozwala na zmniejszenie zużycia pamięci, ale ponieważ jest on znacznie bliższy niskiej warstwie + abstrakcji DB, traci się większość funkcjonalności Active Record. Bardzo ważną różnicą jest zwracany typ danych dla wartości kolumn. Kiedy dane zwracane + są jako obiekt Active Record, wartości kolumn są automatycznie odpowiednio rzutowane zgodnie z typem kolumny; przy danych zwracanych jako tablice + wartości kolumn są zawsze typu string (jako rezultat zapytania PDO bez żadnego przetworzenia), niezależnie od typu kolumny. + + +### Pobieranie danych seriami + +W sekcji [Konstruktor kwerend](db-query-builder.md) wyjaśniliśmy, że można użyć *kwerendy serii*, aby zmniejszyć zużycie pamięci przy pobieraniu +dużej ilości danych z bazy. Tej samej techniki można użyć w przypadku Active Record. Dla przykładu: + +```php +// pobiera dziesięciu klientów na raz +foreach (Customer::find()->batch(10) as $customers) { + // $customers jest tablicą dziesięciu lub mniej obiektów Customer +} + +// pobiera dziesięciu klientów na raz i iteruje po nich pojedynczo +foreach (Customer::find()->each(10) as $customer) { + // $customer jest obiektem Customer +} + +// kwerenda seryjna z gorliwym ładowaniem +foreach (Customer::find()->with('orders')->each() as $customer) { + // $customer jest obiektem Customer +} +``` + + +## Zapisywanie danych + +Używając Active Record możesz w łatwy sposób zapisać dane w bazie, w następujących krokach: + +1. Przygotowanie instancji Active Record +2. Przypisanie nowych wartości do atrybutów Active Record +3. Wywołanie metody [[yii\db\ActiveRecord::save()]] w celu zapisania danych w bazie. + +Przykład: + +```php +// dodaj nowy wiersz danych +$customer = new Customer(); +$customer->name = 'James'; +$customer->email = 'james@example.com'; +$customer->save(); + +// zaktualizuj istniejący wiersz danych +$customer = Customer::findOne(123); +$customer->email = 'james@newexample.com'; +$customer->save(); +``` + +Metoda [[yii\db\ActiveRecord::save()|save()]] może zarówno dodawać jak i aktualizować wiersz danych, w zależności od stanu instacji Active Record. +Jeśli instancja została dopiero utworzona poprzez operator `new`, wywołanie [[yii\db\ActiveRecord::save()|save()]] spowoduje dodanie nowego wiersza. +Jeśli instacja jest wynikiem użycia kwerendy, wywołanie [[yii\db\ActiveRecord::save()|save()]] zaktualizuje wiersz danych powiązanych z instancją. + +Można odróżnić dwa stany instancji Active Record sprawdzając wartość jej właściwości [[yii\db\ActiveRecord::isNewRecord|isNewRecord]]. Jest ona także +używana przez [[yii\db\ActiveRecord::save()|save()]] w poniższy sposób: + +```php +public function save($runValidation = true, $attributeNames = null) +{ + if ($this->getIsNewRecord()) { + return $this->insert($runValidation, $attributeNames); + } else { + return $this->update($runValidation, $attributeNames) !== false; + } +} +``` + +> Wskazówka: Możesz również wywołać [[yii\db\ActiveRecord::insert()|insert()]] lub [[yii\db\ActiveRecord::update()|update()]] bezpośrednio, aby dodać lub + uaktualnić wiersz. + + +### Walidacja danych + +Because [[yii\db\ActiveRecord]] extends from [[yii\base\Model]], it shares the same [data validation](input-validation.md) feature. +You can declare validation rules by overriding the [[yii\db\ActiveRecord::rules()|rules()]] method and perform +data validation by calling the [[yii\db\ActiveRecord::validate()|validate()]] method. + +When you call [[yii\db\ActiveRecord::save()|save()]], by default it will call [[yii\db\ActiveRecord::validate()|validate()]] +automatically. Only when the validation passes, will it actually save the data; otherwise it will simply return false, +and you can check the [[yii\db\ActiveRecord::errors|errors]] property to retrieve the validation error messages. + +> Tip: If you are certain that your data do not need validation (e.g., the data comes from trustable sources), + you can call `save(false)` to skip the validation. + + +### Massive Assignment + +Like normal [models](structure-models.md), Active Record instances also enjoy the [massive assignment feature](structure-models.md#massive-assignment). +Using this feature, you can assign values to multiple attributes of an Active Record instance in a single PHP statement, +like shown below. Do remember that only [safe attributes](structure-models.md#safe-attributes) can be massively assigned, though. + +```php +$values = [ + 'name' => 'James', + 'email' => 'james@example.com', +]; + +$customer = new Customer(); + +$customer->attributes = $values; +$customer->save(); +``` + + +### Updating Counters + +It is a common task to increment or decrement a column in a database table. We call such columns as counter columns. +You can use [[yii\db\ActiveRecord::updateCounters()|updateCounters()]] to update one or multiple counter columns. +For example, + +```php +$post = Post::findOne(100); + +// UPDATE `post` SET `view_count` = `view_count` + 1 WHERE `id` = 100 +$post->updateCounters(['view_count' => 1]); +``` + +> Note: If you use [[yii\db\ActiveRecord::save()]] to update a counter column, you may end up with inaccurate result, + because it is likely the same counter is being saved by multiple requests which read and write the same counter value. + + +### Dirty Attributes + +When you call [[yii\db\ActiveRecord::save()|save()]] to save an Active Record instance, only *dirty attributes* +are being saved. An attribute is considered *dirty* if its value has been modified since it was loaded from DB or +saved to DB most recently. Note that data validation will be performed regardless if the Active Record +instance has dirty attributes or not. + +Active Record automatically maintains the list of dirty attributes. It does so by maintaining an older version of +the attribute values and comparing them with the latest one. You can call [[yii\db\ActiveRecord::getDirtyAttributes()]] +to get the attributes that are currently dirty. You can also call [[yii\db\ActiveRecord::markAttributeDirty()]] +to explicitly mark an attribute as dirty. + +If you are interested in the attribute values prior to their most recent modification, you may call +[[yii\db\ActiveRecord::getOldAttributes()|getOldAttributes()]] or [[yii\db\ActiveRecord::getOldAttribute()|getOldAttribute()]]. + +> Note: The comparison of old and new values will be done using the `===` operator so a value will be considered dirty +> even if it has the same value but a different type. This is often the case when the model receives user input from +> HTML forms where every value is represented as a string. +> To ensure the correct type for e.g. integer values you may apply a [validation filter](input-validation.md#data-filtering): +> `['attributeName', 'filter', 'filter' => 'intval']`. + +### Default Attribute Values + +Some of your table columns may have default values defined in the database. Sometimes, you may want to pre-populate your +Web form for an Active Record instance with these default values. To avoid writing the same default values again, +you can call [[yii\db\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] to populate the DB-defined default values +into the corresponding Active Record attributes: + +```php +$customer = new Customer(); +$customer->loadDefaultValues(); +// $customer->xyz will be assigned the default value declared when defining the "xyz" column +``` + + +### Updating Multiple Rows + +The methods described above all work on individual Active Record instances, causing inserting or updating of individual +table rows. To update multiple rows simultaneously, you should call [[yii\db\ActiveRecord::updateAll()|updateAll()]], instead, +which is a static method. + +```php +// UPDATE `customer` SET `status` = 1 WHERE `email` LIKE `%@example.com%` +Customer::updateAll(['status' => Customer::STATUS_ACTIVE], ['like', 'email', '@example.com']); +``` + +Similarly, you can call [[yii\db\ActiveRecord::updateAllCounters()|updateAllCounters()]] to update counter columns of +multiple rows at the same time. + +```php +// UPDATE `customer` SET `age` = `age` + 1 +Customer::updateAllCounters(['age' => 1]); +``` + + +## Deleting Data + +To delete a single row of data, first retrieve the Active Record instance corresponding to that row and then call +the [[yii\db\ActiveRecord::delete()]] method. + +```php +$customer = Customer::findOne(123); +$customer->delete(); +``` + +You can call [[yii\db\ActiveRecord::deleteAll()]] to delete multiple or all rows of data. For example, + +```php +Customer::deleteAll(['status' => Customer::STATUS_INACTIVE]); +``` + +> Note: Be very careful when calling [[yii\db\ActiveRecord::deleteAll()|deleteAll()]] because it may totally + erase all data from your table if you make a mistake in specifying the condition. + + +## Active Record Life Cycles + +It is important to understand the life cycles of Active Record when it is used for different purposes. +During each life cycle, a certain sequence of methods will be invoked, and you can override these methods +to get a chance to customize the life cycle. You can also respond to certain Active Record events triggered +during a life cycle to inject your custom code. These events are especially useful when you are developing +Active Record [behaviors](concept-behaviors.md) which need to customize Active Record life cycles. + +In the following, we will summarize various Active Record life cycles and the methods/events that are involved +in the life cycles. + + +### New Instance Life Cycle + +When creating a new Active Record instance via the `new` operator, the following life cycle will happen: + +1. class constructor; +2. [[yii\db\ActiveRecord::init()|init()]]: triggers an [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] event. + + +### Querying Data Life Cycle + +When querying data through one of the [querying methods](#querying-data), each newly populated Active Record will +undergo the following life cycle: + +1. class constructor. +2. [[yii\db\ActiveRecord::init()|init()]]: triggers an [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] event. +3. [[yii\db\ActiveRecord::afterFind()|afterFind()]]: triggers an [[yii\db\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]] event. + + +### Saving Data Life Cycle + +When calling [[yii\db\ActiveRecord::save()|save()]] to insert or update an Active Record instance, the following +life cycle will happen: + +1. [[yii\db\ActiveRecord::beforeValidate()|beforeValidate()]]: triggers + an [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] event. If the method returns false + or [[yii\base\ModelEvent::isValid]] is false, the rest of the steps will be skipped. +2. Performs data validation. If data validation fails, the steps after Step 3 will be skipped. +3. [[yii\db\ActiveRecord::afterValidate()|afterValidate()]]: triggers + an [[yii\db\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]] event. +4. [[yii\db\ActiveRecord::beforeSave()|beforeSave()]]: triggers + an [[yii\db\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] + or [[yii\db\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]] event. If the method returns false + or [[yii\base\ModelEvent::isValid]] is false, the rest of the steps will be skipped. +5. Performs the actual data insertion or updating; +6. [[yii\db\ActiveRecord::afterSave()|afterSave()]]: triggers + an [[yii\db\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] + or [[yii\db\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]] event. + + +### Deleting Data Life Cycle + +When calling [[yii\db\ActiveRecord::delete()|delete()]] to delete an Active Record instance, the following +life cycle will happen: + +1. [[yii\db\ActiveRecord::beforeDelete()|beforeDelete()]]: triggers + an [[yii\db\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]] event. If the method returns false + or [[yii\base\ModelEvent::isValid]] is false, the rest of the steps will be skipped. +2. perform the actual data deletion +3. [[yii\db\ActiveRecord::afterDelete()|afterDelete()]]: triggers + an [[yii\db\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]] event. + + +> Note: Calling any of the following methods will NOT initiate any of the above life cycles: +> +> - [[yii\db\ActiveRecord::updateAll()]] +> - [[yii\db\ActiveRecord::deleteAll()]] +> - [[yii\db\ActiveRecord::updateCounters()]] +> - [[yii\db\ActiveRecord::updateAllCounters()]] + + +## Working with Transactions + +There are two ways of using [transactions](db-dao.md#performing-transactions) while working with Active Record. + +The first way is to explicitly enclose Active Record method calls in a transactional block, like shown below, + +```php +$customer = Customer::findOne(123); + +Customer::getDb()->transaction(function($db) use ($customer) { + $customer->id = 200; + $customer->save(); + // ...other DB operations... +}); + +// or alternatively + +$transaction = Customer::getDb()->beginTransaction(); +try { + $customer->id = 200; + $customer->save(); + // ...other DB operations... + $transaction->commit(); +} catch(\Exception $e) { + $transaction->rollBack(); + throw $e; +} +``` + +The second way is to list the DB operations that require transactional support in the [[yii\db\ActiveRecord::transactions()]] +method. For example, + +```php +class Customer extends ActiveRecord +{ + public function transactions() + { + return [ + 'admin' => self::OP_INSERT, + 'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE, + // the above is equivalent to the following: + // 'api' => self::OP_ALL, + ]; + } +} +``` + +The [[yii\db\ActiveRecord::transactions()]] method should return an array whose keys are [scenario](structure-models.md#scenarios) +names and values the corresponding operations that should be enclosed within transactions. You should use the following +constants to refer to different DB operations: + +* [[yii\db\ActiveRecord::OP_INSERT|OP_INSERT]]: insertion operation performed by [[yii\db\ActiveRecord::insert()|insert()]]; +* [[yii\db\ActiveRecord::OP_UPDATE|OP_UPDATE]]: update operation performed by [[yii\db\ActiveRecord::update()|update()]]; +* [[yii\db\ActiveRecord::OP_DELETE|OP_DELETE]]: deletion operation performed by [[yii\db\ActiveRecord::delete()|delete()]]. + +Use `|` operators to concatenate the above constants to indicate multiple operations. You may also use the shortcut +constant [[yii\db\ActiveRecord::OP_ALL|OP_ALL]] to refer to all three operations above. + + +## Optimistic Locks + +Optimistic locking is a way to prevent conflicts that may occur when a single row of data is being +updated by multiple users. For example, both user A and user B are editing the same wiki article +at the same time. After user A saves his edits, user B clicks on the "Save" button in an attempt to +save his edits as well. Because user B was actually working on an outdated version of the article, +it would be desirable to have a way to prevent him from saving the article and show him some hint message. + +Optimistic locking solves the above problem by using a column to record the version number of each row. +When a row is being saved with an outdated version number, a [[yii\db\StaleObjectException]] exception +will be thrown, which prevents the row from being saved. Optimistic locking is only supported when you +update or delete an existing row of data using [[yii\db\ActiveRecord::update()]] or [[yii\db\ActiveRecord::delete()]], +respectively. + +To use optimistic locking, + +1. Create a column in the DB table associated with the Active Record class to store the version number of each row. + The column should be of big integer type (in MySQL it would be `BIGINT DEFAULT 0`). +2. Override the [[yii\db\ActiveRecord::optimisticLock()]] method to return the name of this column. +3. In the Web form that takes user inputs, add a hidden field to store the current version number of the row being updated. Be sure your version attribute has input validation rules and validates successfully. +4. In the controller action that updates the row using Active Record, try and catch the [[yii\db\StaleObjectException]] + exception. Implement necessary business logic (e.g. merging the changes, prompting staled data) to resolve the conflict. + +For example, assume the version column is named as `version`. You can implement optimistic locking with the code like +the following. + +```php +// ------ view code ------- + +use yii\helpers\Html; + +// ...other input fields +echo Html::activeHiddenInput($model, 'version'); + + +// ------ controller code ------- + +use yii\db\StaleObjectException; + +public function actionUpdate($id) +{ + $model = $this->findModel($id); + + try { + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); + } else { + return $this->render('update', [ + 'model' => $model, + ]); + } + } catch (StaleObjectException $e) { + // logic to resolve the conflict + } +} +``` + + +## Working with Relational Data + +Besides working with individual database tables, Active Record is also capable of bringing together related data, +making them readily accessible through the primary data. For example, the customer data is related with the order +data because one customer may have placed one or multiple orders. With appropriate declaration of this relation, +you may be able to access a customer's order information using the expression `$customer->orders` which gives +back the customer's order information in terms of an array of `Order` Active Record instances. + + +### Declaring Relations + +To work with relational data using Active Record, you first need to declare relations in Active Record classes. +The task is as simple as declaring a *relation method* for every interested relation, like the following, + +```php +class Customer extends ActiveRecord +{ + public function getOrders() + { + return $this->hasMany(Order::className(), ['customer_id' => 'id']); + } +} + +class Order extends ActiveRecord +{ + public function getCustomer() + { + return $this->hasOne(Customer::className(), ['id' => 'customer_id']); + } +} +``` + +In the above code, we have declared an `orders` relation for the `Customer` class, and a `customer` relation +for the `Order` class. + +Each relation method must be named as `getXyz`. We call `xyz` (the first letter is in lower case) the *relation name*. +Note that relation names are *case sensitive*. + +While declaring a relation, you should specify the following information: + +- the multiplicity of the relation: specified by calling either [[yii\db\ActiveRecord::hasMany()|hasMany()]] + or [[yii\db\ActiveRecord::hasOne()|hasOne()]]. In the above example you may easily read in the relation + declarations that a customer has many orders while an order only has one customer. +- the name of the related Active Record class: specified as the first parameter to + either [[yii\db\ActiveRecord::hasMany()|hasMany()]] or [[yii\db\ActiveRecord::hasOne()|hasOne()]]. + A recommended practice is to call `Xyz::className()` to get the class name string so that you can receive + IDE auto-completion support as well as error detection at compiling stage. +- the link between the two types of data: specifies the column(s) through which the two types of data are related. + The array values are the columns of the primary data (represented by the Active Record class that you are declaring + relations), while the array keys are the columns of the related data. + + An easy rule to remember this is, as you see in the example above, you write the column that belongs to the related + Active Record directly next to it. You see there that `customer_id` is a property of `Order` and `id` is a property + of `Customer`. + + +### Accessing Relational Data + +After declaring relations, you can access relational data through relation names. This is just like accessing +an object [property](concept-properties.md) defined by the relation method. For this reason, we call it *relation property*. +For example, + +```php +// SELECT * FROM `customer` WHERE `id` = 123 +$customer = Customer::findOne(123); + +// SELECT * FROM `order` WHERE `customer_id` = 123 +// $orders is an array of Order objects +$orders = $customer->orders; +``` + +> Info: When you declare a relation named `xyz` via a getter method `getXyz()`, you will be able to access + `xyz` like an [object property](concept-properties.md). Note that the name is case sensitive. + +If a relation is declared with [[yii\db\ActiveRecord::hasMany()|hasMany()]], accessing this relation property +will return an array of the related Active Record instances; if a relation is declared with +[[yii\db\ActiveRecord::hasOne()|hasOne()]], accessing the relation property will return the related +Active Record instance or null if no related data is found. + +When you access a relation property for the first time, a SQL statement will be executed, like shown in the +above example. If the same property is accessed again, the previous result will be returned without re-executing +the SQL statement. To force re-executing the SQL statement, you should unset the relation property +first: `unset($customer->orders)`. + +> Note: While this concept looks similar to the [object property](concept-properties.md) feature, there is an +> important difference. For normal object properties the property value is of the same type as the defining getter method. +> A relation method however returns an [[yii\db\ActiveQuery]] instance, while accessing a relation property will either +> return a [[yii\db\ActiveRecord]] instance or an array of these. +> +> ```php +> $customer->orders; // is an array of `Order` objects +> $customer->getOrders(); // returns an ActiveQuery instance +> ``` +> +> This is useful for creating customized queries, which is described in the next section. + + +### Dynamic Relational Query + +Because a relation method returns an instance of [[yii\db\ActiveQuery]], you can further build this query +using query building methods before performing DB query. For example, + +```php +$customer = Customer::findOne(123); + +// SELECT * FROM `order` WHERE `subtotal` > 200 ORDER BY `id` +$orders = $customer->getOrders() + ->where(['>', 'subtotal', 200]) + ->orderBy('id') + ->all(); +``` + +Unlike accessing a relation property, each time you perform a dynamic relational query via a relation method, +a SQL statement will be executed, even if the same dynamic relational query was performed before. + +Sometimes you may even want to parametrize a relation declaration so that you can more easily perform +dynamic relational query. For example, you may declare a `bigOrders` relation as follows, + +```php +class Customer extends ActiveRecord +{ + public function getBigOrders($threshold = 100) + { + return $this->hasMany(Order::className(), ['customer_id' => 'id']) + ->where('subtotal > :threshold', [':threshold' => $threshold]) + ->orderBy('id'); + } +} +``` + +Then you will be able to perform the following relational queries: + +```php +// SELECT * FROM `order` WHERE `subtotal` > 200 ORDER BY `id` +$orders = $customer->getBigOrders(200)->all(); + +// SELECT * FROM `order` WHERE `subtotal` > 100 ORDER BY `id` +$orders = $customer->bigOrders; +``` + + +### Relations via a Junction Table + +In database modelling, when the multiplicity between two related tables is many-to-many, +a [junction table](https://en.wikipedia.org/wiki/Junction_table) is usually introduced. For example, the `order` +table and the `item` table may be related via a junction table named `order_item`. One order will then correspond +to multiple order items, while one product item will also correspond to multiple order items. + +When declaring such relations, you would call either [[yii\db\ActiveQuery::via()|via()]] or [[yii\db\ActiveQuery::viaTable()|viaTable()]] +to specify the junction table. The difference between [[yii\db\ActiveQuery::via()|via()]] and [[yii\db\ActiveQuery::viaTable()|viaTable()]] +is that the former specifies the junction table in terms of an existing relation name while the latter directly +the junction table. For example, + +```php +class Order extends ActiveRecord +{ + public function getItems() + { + return $this->hasMany(Item::className(), ['id' => 'item_id']) + ->viaTable('order_item', ['order_id' => 'id']); + } +} +``` + +or alternatively, + +```php +class Order extends ActiveRecord +{ + public function getOrderItems() + { + return $this->hasMany(OrderItem::className(), ['order_id' => 'id']); + } + + public function getItems() + { + return $this->hasMany(Item::className(), ['id' => 'item_id']) + ->via('orderItems'); + } +} +``` + +The usage of relations declared with a junction table is the same as that of normal relations. For example, + +```php +// SELECT * FROM `order` WHERE `id` = 100 +$order = Order::findOne(100); + +// SELECT * FROM `order_item` WHERE `order_id` = 100 +// SELECT * FROM `item` WHERE `item_id` IN (...) +// returns an array of Item objects +$items = $order->items; +``` + + +### Lazy Loading and Eager Loading + +In [Accessing Relational Data](#accessing-relational-data), we explained that you can access a relation property +of an Active Record instance like accessing a normal object property. A SQL statement will be executed only when +you access the relation property the first time. We call such relational data accessing method *lazy loading*. +For example, + +```php +// SELECT * FROM `customer` WHERE `id` = 123 +$customer = Customer::findOne(123); + +// SELECT * FROM `order` WHERE `customer_id` = 123 +$orders = $customer->orders; + +// no SQL executed +$orders2 = $customer->orders; +``` + +Lazy loading is very convenient to use. However, it may suffer from a performance issue when you need to access +the same relation property of multiple Active Record instances. Consider the following code example. How many +SQL statements will be executed? + +```php +// SELECT * FROM `customer` LIMIT 100 +$customers = Customer::find()->limit(100)->all(); + +foreach ($customers as $customer) { + // SELECT * FROM `order` WHERE `customer_id` = ... + $orders = $customer->orders; +} +``` + +As you can see from the code comment above, there are 101 SQL statements being executed! This is because each +time you access the `orders` relation property of a different `Customer` object in the for-loop, a SQL statement +will be executed. + +To solve this performance problem, you can use the so-called *eager loading* approach as shown below, + +```php +// SELECT * FROM `customer` LIMIT 100; +// SELECT * FROM `orders` WHERE `customer_id` IN (...) +$customers = Customer::find() + ->with('orders') + ->limit(100) + ->all(); + +foreach ($customers as $customer) { + // no SQL executed + $orders = $customer->orders; +} +``` + +By calling [[yii\db\ActiveQuery::with()]], you instruct Active Record to bring back the orders for the first 100 +customers in one single SQL statement. As a result, you reduce the number of the executed SQL statements from 101 to 2! + +You can eagerly load one or multiple relations. You can even eagerly load *nested relations*. A nested relation is a relation +that is declared within a related Active Record class. For example, `Customer` is related with `Order` through the `orders` +relation, and `Order` is related with `Item` through the `items` relation. When querying for `Customer`, you can eagerly +load `items` using the nested relation notation `orders.items`. + +The following code shows different usage of [[yii\db\ActiveQuery::with()|with()]]. We assume the `Customer` class +has two relations `orders` and `country`, while the `Order` class has one relation `items`. + +```php +// eager loading both "orders" and "country" +$customers = Customer::find()->with('orders', 'country')->all(); +// equivalent to the array syntax below +$customers = Customer::find()->with(['orders', 'country'])->all(); +// no SQL executed +$orders= $customers[0]->orders; +// no SQL executed +$country = $customers[0]->country; + +// eager loading "orders" and the nested relation "orders.items" +$customers = Customer::find()->with('orders.items')->all(); +// access the items of the first order of the first customer +// no SQL executed +$items = $customers[0]->orders[0]->items; +``` + +You can eagerly load deeply nested relations, such as `a.b.c.d`. All parent relations will be eagerly loaded. +That is, when you call [[yii\db\ActiveQuery::with()|with()]] using `a.b.c.d`, you will eagerly load +`a`, `a.b`, `a.b.c` and `a.b.c.d`. + +> Info: In general, when eagerly loading `N` relations among which `M` relations are defined with a + [junction table](#junction-table), a total number of `N+M+1` SQL statements will be executed. + Note that a nested relation `a.b.c.d` counts as 4 relations. + +When eagerly loading a relation, you can customize the corresponding relational query using an anonymous function. +For example, + +```php +// find customers and bring back together their country and active orders +// SELECT * FROM `customer` +// SELECT * FROM `country` WHERE `id` IN (...) +// SELECT * FROM `order` WHERE `customer_id` IN (...) AND `status` = 1 +$customers = Customer::find()->with([ + 'country', + 'orders' => function ($query) { + $query->andWhere(['status' => Order::STATUS_ACTIVE]); + }, +])->all(); +``` + +When customizing the relational query for a relation, you should specify the relation name as an array key +and use an anonymous function as the corresponding array value. The anonymous function will receive a `$query` parameter +which represents the [[yii\db\ActiveQuery]] object used to perform the relational query for the relation. +In the code example above, we are modifying the relational query by appending an additional condition about order status. + +> Note: If you call [[yii\db\Query::select()|select()]] while eagerly loading relations, you have to make sure +> the columns referenced in the relation declarations are being selected. Otherwise, the related models may not +> be loaded properly. For example, +> +> ```php +> $orders = Order::find()->select(['id', 'amount'])->with('customer')->all(); +> // $orders[0]->customer is always null. To fix the problem, you should do the following: +> $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all(); +> ``` + + +### Joining with Relations + +> Note: The content described in this subsection is only applicable to relational databases, such as + MySQL, PostgreSQL, etc. + +The relational queries that we have described so far only reference the primary table columns when +querying for the primary data. In reality we often need to reference columns in the related tables. For example, +we may want to bring back the customers who have at least one active order. To solve this problem, we can +build a join query like the following: + +```php +// SELECT `customer`.* FROM `customer` +// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id` +// WHERE `order`.`status` = 1 +// +// SELECT * FROM `order` WHERE `customer_id` IN (...) +$customers = Customer::find() + ->select('customer.*') + ->leftJoin('order', '`order`.`customer_id` = `customer`.`id`') + ->where(['order.status' => Order::STATUS_ACTIVE]) + ->with('orders') + ->all(); +``` + +> Note: It is important to disambiguate column names when building relational queries involving JOIN SQL statements. + A common practice is to prefix column names with their corresponding table names. + +However, a better approach is to exploit the existing relation declarations by calling [[yii\db\ActiveQuery::joinWith()]]: + +```php +$customers = Customer::find() + ->joinWith('orders') + ->where(['order.status' => Order::STATUS_ACTIVE]) + ->all(); +``` + +Both approaches execute the same set of SQL statements. The latter approach is much cleaner and drier, though. + +By default, [[yii\db\ActiveQuery::joinWith()|joinWith()]] will use `LEFT JOIN` to join the primary table with the +related table. You can specify a different join type (e.g. `RIGHT JOIN`) via its third parameter `$joinType`. If +the join type you want is `INNER JOIN`, you can simply call [[yii\db\ActiveQuery::innerJoinWith()|innerJoinWith()]], instead. + +Calling [[yii\db\ActiveQuery::joinWith()|joinWith()]] will [eagerly load](#lazy-eager-loading) the related data by default. +If you do not want to bring in the related data, you can specify its second parameter `$eagerLoading` as false. + +Like [[yii\db\ActiveQuery::with()|with()]], you can join with one or multiple relations; you may customize the relation +queries on-the-fly; you may join with nested relations; and you may mix the use of [[yii\db\ActiveQuery::with()|with()]] +and [[yii\db\ActiveQuery::joinWith()|joinWith()]]. For example, + +```php +$customers = Customer::find()->joinWith([ + 'orders' => function ($query) { + $query->andWhere(['>', 'subtotal', 100]); + }, +])->with('country') + ->all(); +``` + +Sometimes when joining two tables, you may need to specify some extra conditions in the `ON` part of the JOIN query. +This can be done by calling the [[yii\db\ActiveQuery::onCondition()]] method like the following: + +```php +// SELECT `customer`.* FROM `customer` +// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id` AND `order`.`status` = 1 +// +// SELECT * FROM `order` WHERE `customer_id` IN (...) +$customers = Customer::find()->joinWith([ + 'orders' => function ($query) { + $query->onCondition(['order.status' => Order::STATUS_ACTIVE]); + }, +])->all(); +``` + +This above query brings back *all* customers, and for each customer it brings back all active orders. +Note that this differs from our earlier example which only brings back customers who have at least one active order. + +> Info: When [[yii\db\ActiveQuery]] is specified with a condition via [[yii\db\ActiveQuery::onCondition()|onCondition()]], + the condition will be put in the `ON` part if the query involves a JOIN query. If the query does not involve + JOIN, the on-condition will be automatically appended to the `WHERE` part of the query. + + +### Inverse Relations + +Relation declarations are often reciprocal between two Active Record classes. For example, `Customer` is related +to `Order` via the `orders` relation, and `Order` is related back to `Customer` via the `customer` relation. + +```php +class Customer extends ActiveRecord +{ + public function getOrders() + { + return $this->hasMany(Order::className(), ['customer_id' => 'id']); + } +} + +class Order extends ActiveRecord +{ + public function getCustomer() + { + return $this->hasOne(Customer::className(), ['id' => 'customer_id']); + } +} +``` + +Now consider the following piece of code: + +```php +// SELECT * FROM `customer` WHERE `id` = 123 +$customer = Customer::findOne(123); + +// SELECT * FROM `order` WHERE `customer_id` = 123 +$order = $customer->orders[0]; + +// SELECT * FROM `customer` WHERE `id` = 123 +$customer2 = $order->customer; + +// displays "not the same" +echo $customer2 === $customer ? 'same' : 'not the same'; +``` + +We would think `$customer` and `$customer2` are the same, but they are not! Actually they do contain the same +customer data, but they are different objects. When accessing `$order->customer`, an extra SQL statement +is executed to populate a new object `$customer2`. + +To avoid the redundant execution of the last SQL statement in the above example, we should tell Yii that +`customer` is an *inverse relation* of `orders` by calling the [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] method +like shown below: + +```php +class Customer extends ActiveRecord +{ + public function getOrders() + { + return $this->hasMany(Order::className(), ['customer_id' => 'id'])->inverseOf('customer'); + } +} +``` + +With this modified relation declaration, we will have: + +```php +// SELECT * FROM `customer` WHERE `id` = 123 +$customer = Customer::findOne(123); + +// SELECT * FROM `order` WHERE `customer_id` = 123 +$order = $customer->orders[0]; + +// No SQL will be executed +$customer2 = $order->customer; + +// displays "same" +echo $customer2 === $customer ? 'same' : 'not the same'; +``` + +> Note: Inverse relations cannot be defined for relations involving a [junction table](#junction-table). + That is, if a relation is defined with [[yii\db\ActiveQuery::via()|via()]] or [[yii\db\ActiveQuery::viaTable()|viaTable()]], + you should not call [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] further. + + +## Saving Relations + +When working with relational data, you often need to establish relationships between different data or destroy +existing relationships. This requires setting proper values for the columns that define the relations. Using Active Record, +you may end up writing the code like the following: + +```php +$customer = Customer::findOne(123); +$order = new Order(); +$order->subtotal = 100; +// ... + +// setting the attribute that defines the "customer" relation in Order +$order->customer_id = $customer->id; +$order->save(); +``` + +Active Record provides the [[yii\db\ActiveRecord::link()|link()]] method that allows you to accomplish this task more nicely: + +```php +$customer = Customer::findOne(123); +$order = new Order(); +$order->subtotal = 100; +// ... + +$order->link('customer', $customer); +``` + +The [[yii\db\ActiveRecord::link()|link()]] method requires you to specify the relation name and the target Active Record +instance that the relationship should be established with. The method will modify the values of the attributes that +link two Active Record instances and save them to the database. In the above example, it will set the `customer_id` +attribute of the `Order` instance to be the value of the `id` attribute of the `Customer` instance and then save it +to the database. + +> Note: You cannot link two newly created Active Record instances. + +The benefit of using [[yii\db\ActiveRecord::link()|link()]] is even more obvious when a relation is defined via +a [junction table](#junction-table). For example, you may use the following code to link an `Order` instance +with an `Item` instance: + +```php +$order->link('items', $item); +``` + +The above code will automatically insert a row in the `order_item` junction table to relate the order with the item. + +> Info: The [[yii\db\ActiveRecord::link()|link()]] method will NOT perform any data validation while + saving the affected Active Record instance. It is your responsibility to validate any input data before + calling this method. + +The opposite operation to [[yii\db\ActiveRecord::link()|link()]] is [[yii\db\ActiveRecord::unlink()|unlink()]] +which breaks an existing relationship between two Active Record instances. For example, + +```php +$customer = Customer::find()->with('orders')->all(); +$customer->unlink('orders', $customer->orders[0]); +``` + +By default, the [[yii\db\ActiveRecord::unlink()|unlink()]] method will set the foreign key value(s) that specify +the existing relationship to be null. You may, however, choose to delete the table row that contains the foreign key value +by passing the `$delete` parameter as true to the method. + +When a junction table is involved in a relation, calling [[yii\db\ActiveRecord::unlink()|unlink()]] will cause +the foreign keys in the junction table to be cleared, or the deletion of the corresponding row in the junction table +if `$delete` is true. + + +## Cross-Database Relations + +Active Record allows you to declare relations between Active Record classes that are powered by different databases. +The databases can be of different types (e.g. MySQL and PostgreSQL, or MS SQL and MongoDB), and they can run on +different servers. You can use the same syntax to perform relational queries. For example, + +```php +// Customer is associated with the "customer" table in a relational database (e.g. MySQL) +class Customer extends \yii\db\ActiveRecord +{ + public static function tableName() + { + return 'customer'; + } + + public function getComments() + { + // a customer has many comments + return $this->hasMany(Comment::className(), ['customer_id' => 'id']); + } +} + +// Comment is associated with the "comment" collection in a MongoDB database +class Comment extends \yii\mongodb\ActiveRecord +{ + public static function collectionName() + { + return 'comment'; + } + + public function getCustomer() + { + // a comment has one customer + return $this->hasOne(Customer::className(), ['id' => 'customer_id']); + } +} + +$customers = Customer::find()->with('comments')->all(); +``` + +You can use most of the relational query features that have been described in this section. + +> Note: Usage of [[yii\db\ActiveQuery::joinWith()|joinWith()]] is limited to databases that allow cross-database JOIN queries. + For this reason, you cannot use this method in the above example because MongoDB does not support JOIN. + + +## Customizing Query Classes + +By default, all Active Record queries are supported by [[yii\db\ActiveQuery]]. To use a customized query class +in an Active Record class, you should override the [[yii\db\ActiveRecord::find()]] method and return an instance +of your customized query class. For example, + +```php +namespace app\models; + +use yii\db\ActiveRecord; +use yii\db\ActiveQuery; + +class Comment extends ActiveRecord +{ + public static function find() + { + return new CommentQuery(get_called_class()); + } +} + +class CommentQuery extends ActiveQuery +{ + // ... +} +``` + +Now whenever you are performing a query (e.g. `find()`, `findOne()`) or defining a relation (e.g. `hasOne()`) +with `Comment`, you will be working with an instance of `CommentQuery` instead of `ActiveQuery`. + +> Tip: In big projects, it is recommended that you use customized query classes to hold most query-related code + so that the Active Record classes can be kept clean. + +You can customize a query class in many creative ways to improve your query building experience. For example, +you can define new query building methods in a customized query class: + +```php +class CommentQuery extends ActiveQuery +{ + public function active($state = true) + { + return $this->andWhere(['active' => $state]); + } +} +``` + +> Note: Instead of calling [[yii\db\ActiveQuery::where()|where()]], you usually should call + [[yii\db\ActiveQuery::andWhere()|andWhere()]] or [[yii\db\ActiveQuery::orWhere()|orWhere()]] to append additional + conditions when defining new query building methods so that any existing conditions are not overwritten. + +This allows you to write query building code like the following: + +```php +$comments = Comment::find()->active()->all(); +$inactiveComments = Comment::find()->active(false)->all(); +``` + +You can also use the new query building methods when defining relations about `Comment` or performing relational query: + +```php +class Customer extends \yii\db\ActiveRecord +{ + public function getActiveComments() + { + return $this->hasMany(Comment::className(), ['customer_id' => 'id'])->active(); + } +} + +$customers = Customer::find()->with('activeComments')->all(); + +// or alternatively + +$customers = Customer::find()->with([ + 'comments' => function($q) { + $q->active(); + } +])->all(); +``` + +> Info: In Yii 1.1, there is a concept called *scope*. Scope is no longer directly supported in Yii 2.0, + and you should use customized query classes and query methods to achieve the same goal. + + +## Selecting extra fields + +When Active Record instance is populated from query results, its attributes are filled up by corresponding column +values from received data set. + +You are able to fetch additional columns or values from query and store it inside the Active Record. +For example, assume we have a table named 'room', which contains information about rooms available in the hotel. +Each room stores information about its geometrical size using fields 'length', 'width', 'height'. +Imagine we need to retrieve list of all available rooms with their volume in descendant order. +So you can not calculate volume using PHP, because we need to sort the records by its value, but you also want 'volume' +to be displayed in the list. +To achieve the goal, you need to declare an extra field in your 'Room' Active Record class, which will store 'volume' value: + +```php +class Room extends \yii\db\ActiveRecord +{ + public $volume; + + // ... +} +``` + +Then you need to compose a query, which calculates volume of the room and performs the sort: + +```php +$rooms = Room::find() + ->select([ + '{{room}}.*', // select all columns + '([[length]] * [[width]].* [[height]]) AS volume', // calculate a volume + ]) + ->orderBy('volume DESC') // apply sort + ->all(); + +foreach ($rooms as $room) { + echo $room->volume; // contains value calculated by SQL +} +``` + +Ability to select extra fields can be exceptionally useful for aggregation queries. +Assume you need to display a list of customers with the count of orders they have made. +First of all, you need to declare a `Customer` class with 'orders' relation and extra field for count storage: + +```php +class Customer extends \yii\db\ActiveRecord +{ + public $ordersCount; + + // ... + + public function getOrders() + { + return $this->hasMany(Order::className(), ['customer_id' => 'id']); + } +} +``` + +Then you can compose a query, which joins the orders and calculates their count: + +```php +$customers = Customer::find() + ->select([ + '{{customer}}.*', // select all customer fields + 'COUNT({{order}}.id) AS ordersCount' // calculate orders count + ]) + ->joinWith('orders') // ensure table junction + ->groupBy('{{customer}}.id') // group the result to ensure aggregation function works + ->all(); +``` diff --git a/docs/guide-pl/intro-upgrade-from-v1.md b/docs/guide-pl/intro-upgrade-from-v1.md index 48aa65d432..19d167ef9b 100644 --- a/docs/guide-pl/intro-upgrade-from-v1.md +++ b/docs/guide-pl/intro-upgrade-from-v1.md @@ -373,15 +373,15 @@ $sql = $command->sql; $rows = $command->queryAll(); ``` -Co najlepsze, taki sposób tworzenia kwerend może być również wykorzystany przy pracy z [Rekordem aktywnym](db-active-record.md). +Co najlepsze, taki sposób tworzenia kwerend może być również wykorzystany przy pracy z [Active Record](db-active-record.md). Po więcej szczegółów udaj się do sekcji [Konstruktor kwerend](db-query-builder.md). -Rekord aktywny --------------- +Active Record +------------- -Yii 2.0 wprowadza sporo zmian w [Rekordach aktywnych](db-active-record.md). Dwie najbardziej znaczące to +Yii 2.0 wprowadza sporo zmian w mechanizmie [Active Record](db-active-record.md). Dwie najbardziej znaczące to konstruowanie kwerend i obsługa relacji. Klasa `CDbCriteria` z 1.1 została zastąpiona przez [[yii\db\ActiveQuery]] w Yii 2. Klasa ta rozszerza [[yii\db\Query]], dzięki czemu @@ -444,8 +444,8 @@ Zwróć jednak uwagę na to, że przy dodawaniu parametrów do konstruktora moż W nowym rekordzie aktywnym znajdziesz wiele innych zmian i udogodnień. Aby zapoznać się z nimi, przejdź do sekcji [Rekord aktywny](db-active-record.md). -Zachowania rekordu aktywnego ----------------------------- +Zachowania Active Record +------------------------ W 2.0 zrezygnowaliśmy z bazowej klasy zachowania `CActiveRecordBehavior`. Jeśli chcesz stworzyć zachowanie dla rekordu aktywnego, musisz rozszerzyć bezpośrednio klasę `yii\base\Behavior`. Jeśli klasa zachowania ma reagować na zdarzenia, powinna nadpisywać metodę `events()`, jak zaprezentowano poniżej: From 787da2e5a027968ec0ebafd84e6d3a34eb50a085 Mon Sep 17 00:00:00 2001 From: Bizley Date: Tue, 18 Aug 2015 09:51:14 +0200 Subject: [PATCH 2/8] db-active-record 33% --- docs/guide-pl/README.md | 6 +- docs/guide-pl/db-active-record.md | 128 +++++++++++++++--------------- 2 files changed, 65 insertions(+), 69 deletions(-) diff --git a/docs/guide-pl/README.md b/docs/guide-pl/README.md index 3aaf0a2ce7..a7c00abd34 100644 --- a/docs/guide-pl/README.md +++ b/docs/guide-pl/README.md @@ -40,7 +40,7 @@ Struktura aplikacji * [Moduły](structure-modules.md) * [Filtry](structure-filters.md) * [Widżety](structure-widgets.md) -* [Zasoby (Assets)](structure-assets.md) +* [Assety (Assets)](structure-assets.md) * [Rozszerzenia](structure-extensions.md) @@ -63,8 +63,8 @@ Kluczowe koncepcje * [Komponenty](concept-components.md) * [Właściwości](concept-properties.md) -* [Zdarzenia (Events)](concept-events.md) -* [Zachowania (Behaviors)](concept-behaviors.md) +* [Events](concept-events.md) +* [Behaviors](concept-behaviors.md) * [Konfiguracje](concept-configurations.md) * [Aliasy](concept-aliases.md) * [Autoładowanie klas](concept-autoloading.md) diff --git a/docs/guide-pl/db-active-record.md b/docs/guide-pl/db-active-record.md index fd75d331e6..4b217f787d 100644 --- a/docs/guide-pl/db-active-record.md +++ b/docs/guide-pl/db-active-record.md @@ -161,7 +161,7 @@ W powyższych przykładach `$customer` jest obiektem typu `Customer`, a `$custom dane pobrane są z tabeli `customer`. > Info: Dzięki temu, że [[yii\db\ActiveQuery]] rozszerza klasę [[yii\db\Query]], możesz użyć *wszystkich* metod dotyczących kwerend i ich budowania - opisanych w sekcji [Konstruktor kwerend](db-query-builder.md). +> opisanych w sekcji [Konstruktor kwerend](db-query-builder.md). Ponieważ zwykle kwerendy korzystają z zapytań zawierających klucz główny lub też zestaw wartości dla kilku kolumn, Yii udostępnia dwie skrótowe metody, pozwalające na szybsze ich użycie: @@ -203,9 +203,9 @@ $customers = Customer::findAll([ ]); ``` -> Uwaga: Ani metoda [[yii\db\ActiveRecord::findOne()]] ani [[yii\db\ActiveQuery::one()]] nie dodaje `LIMIT 1` do wygenerowanej - kwerendy SQL. Jeśli zapytanie może zwrócić więcej niż jeden wiersz danych, należy wywołać bezpośrednio `limit(1)`, w celu zwiększenia - wydajności aplikacji, np. `Customer::find()->limit(1)->one()`. +> Note: Ani metoda [[yii\db\ActiveRecord::findOne()]] ani [[yii\db\ActiveQuery::one()]] nie dodaje `LIMIT 1` do wygenerowanej +> kwerendy SQL. Jeśli zapytanie może zwrócić więcej niż jeden wiersz danych, należy wywołać bezpośrednio `limit(1)`, w celu zwiększenia +> wydajności aplikacji, np. `Customer::find()->limit(1)->one()`. Oprócz korzystania z metod konstruktora kwerend możesz również użyć surowych zapytań SQL w celu pobrania danych do obiektu Active Record za pomocą metody [[yii\db\ActiveRecord::findBySql()]]: @@ -231,9 +231,9 @@ $id = $customer->id; $email = $customer->email; ``` -> Uwaga: nazwy atrybutów Active Record odpowiadają nazwom powiązanych z nimi kolumn z uwzględnieniem wielkości liter. - Yii automatycznie definiuje atrybut Active Record dla każdej kolumny powiązanej tabeli. - NIE należy definiować ich własnoręcznie. +> Note: nazwy atrybutów Active Record odpowiadają nazwom powiązanych z nimi kolumn z uwzględnieniem wielkości liter. +> Yii automatycznie definiuje atrybut Active Record dla każdej kolumny powiązanej tabeli. +> NIE należy definiować ich własnoręcznie. Ponieważ atrybuty Active Record nazywane są zgodnie z nazwami kolumn, możesz natknąć się na kod PHP typu `$customer->first_name`, gdzie podkreślniki używane są do oddzielenia poszczególnych słów w nazwach atrybutów, jeśli kolumny tabeli nazywane są @@ -268,7 +268,7 @@ class Customer extends ActiveRecord Od tego momentu, w kodzie PHP, zamiast odwołać się do `$customer->birthday`, można użyć `$customer->birthdayText`, co pozwala na wprowadzenie i wyświetlenie daty urodzin klienta w formacie `'RRRR/MM/DD'`. -> Wskazówka: Powyższy przykład pokazuje podstawowy sposób transformacji danych. Podczas zwyczajowej pracy z formularzami danych można skorzystać z +> Tip: Powyższy przykład pokazuje podstawowy sposób transformacji danych. Podczas zwyczajowej pracy z formularzami danych można skorzystać z > [DateValidator](tutorial-core-validators.md#date) i [[yii\jui\DatePicker|DatePicker]], co jest prostsze w użyciu i posiadające więcej możliwości. @@ -286,10 +286,10 @@ $customers = Customer::find() ->all(); ``` -> Uwaga: Powyższy sposób zwiększa wydajność aplikacji i pozwala na zmniejszenie zużycia pamięci, ale ponieważ jest on znacznie bliższy niskiej warstwie - abstrakcji DB, traci się większość funkcjonalności Active Record. Bardzo ważną różnicą jest zwracany typ danych dla wartości kolumn. Kiedy dane zwracane - są jako obiekt Active Record, wartości kolumn są automatycznie odpowiednio rzutowane zgodnie z typem kolumny; przy danych zwracanych jako tablice - wartości kolumn są zawsze typu string (jako rezultat zapytania PDO bez żadnego przetworzenia), niezależnie od typu kolumny. +> Note: Powyższy sposób zwiększa wydajność aplikacji i pozwala na zmniejszenie zużycia pamięci, ale ponieważ jest on znacznie bliższy niskiej warstwie +> abstrakcji DB, traci się większość funkcjonalności Active Record. Bardzo ważną różnicą jest zwracany typ danych dla wartości kolumn. Kiedy dane zwracane +> są jako obiekt Active Record, wartości kolumn są automatycznie odpowiednio rzutowane zgodnie z typem kolumny; przy danych zwracanych jako tablice +> wartości kolumn są zawsze typu string (jako rezultat zapytania PDO bez żadnego przetworzenia), niezależnie od typu kolumny. ### Pobieranie danych seriami @@ -356,29 +356,29 @@ public function save($runValidation = true, $attributeNames = null) } ``` -> Wskazówka: Możesz również wywołać [[yii\db\ActiveRecord::insert()|insert()]] lub [[yii\db\ActiveRecord::update()|update()]] bezpośrednio, aby dodać lub - uaktualnić wiersz. +> Tip: Możesz również wywołać [[yii\db\ActiveRecord::insert()|insert()]] lub [[yii\db\ActiveRecord::update()|update()]] bezpośrednio, aby dodać lub +> uaktualnić wiersz. ### Walidacja danych -Because [[yii\db\ActiveRecord]] extends from [[yii\base\Model]], it shares the same [data validation](input-validation.md) feature. -You can declare validation rules by overriding the [[yii\db\ActiveRecord::rules()|rules()]] method and perform -data validation by calling the [[yii\db\ActiveRecord::validate()|validate()]] method. +Dzięki temu, że [[yii\db\ActiveRecord]] rozszerza klasę [[yii\base\Model]], korzysta z tych samych mechanizmów [walidacji danych](input-validation.md). +Możesz definiować zasady walidacji nadpisując metodę [[yii\db\ActiveRecord::rules()|rules()]] i uruchamiać procedurę walidacji wywołując metodę +[[yii\db\ActiveRecord::validate()|validate()]]. -When you call [[yii\db\ActiveRecord::save()|save()]], by default it will call [[yii\db\ActiveRecord::validate()|validate()]] -automatically. Only when the validation passes, will it actually save the data; otherwise it will simply return false, -and you can check the [[yii\db\ActiveRecord::errors|errors]] property to retrieve the validation error messages. +Wywołanie [[yii\db\ActiveRecord::save()|save()]] automatycznie wywołuje również [[yii\db\ActiveRecord::validate()|validate()]]. +Dopiero po pomyślnym przejściu walidacji rozpocznie się proces zapisywania danych; w przeciwnym wypadku zostanie zwrócona flaga false, a komunikaty o +błędach walidacji można odczytać sprawdzając właściwość [[yii\db\ActiveRecord::errors|errors]]. -> Tip: If you are certain that your data do not need validation (e.g., the data comes from trustable sources), - you can call `save(false)` to skip the validation. +> Tip: Jeśli masz pewność, że dane nie potrzebują przechodzić procesu walidacji (np. pochodzą z zaufanych źródeł), możesz wywołać `save(false)`, +> aby go pominąć. -### Massive Assignment +### Masowe przypisywanie -Like normal [models](structure-models.md), Active Record instances also enjoy the [massive assignment feature](structure-models.md#massive-assignment). -Using this feature, you can assign values to multiple attributes of an Active Record instance in a single PHP statement, -like shown below. Do remember that only [safe attributes](structure-models.md#safe-attributes) can be massively assigned, though. +Tak jak w zwyczajnych [modelach](structure-models.md), instancje Active Record posiadają również mechanizm [masowego przypisywania](structure-models.md#massive-assignment). +Funkcjonalność ta umożliwia przypisanie wartości wielu atrybutom Active Record za pomocą pojedynczej instrukcji PHP, jak pokazano to poniżej. +Należy jednak pamiętać, że w ten sposób mogą być przypisane tylko [bezpieczne atrybuty](structure-models.md#safe-attributes). ```php $values = [ @@ -393,11 +393,11 @@ $customer->save(); ``` -### Updating Counters +### Aktualizowanie liczników -It is a common task to increment or decrement a column in a database table. We call such columns as counter columns. -You can use [[yii\db\ActiveRecord::updateCounters()|updateCounters()]] to update one or multiple counter columns. -For example, +Jednym z częstych zadań jest zmniejszanie lub zwiększanie wartości kolumny w tabeli bazy danych. Takie kolumny nazywamy licznikami. +Metoda [[yii\db\ActiveRecord::updateCounters()|updateCounters()]] służy do aktualizacji jednego lub wielu liczników. +Przykład: ```php $post = Post::findOne(100); @@ -406,58 +406,54 @@ $post = Post::findOne(100); $post->updateCounters(['view_count' => 1]); ``` -> Note: If you use [[yii\db\ActiveRecord::save()]] to update a counter column, you may end up with inaccurate result, - because it is likely the same counter is being saved by multiple requests which read and write the same counter value. +> Note: Jeśli używasz [[yii\db\ActiveRecord::save()]] do aktualizacji licznika, możesz otrzymać nieprawidłowe rezultaty, ponieważ jest możliwe, że + ten sam licznik zostanie odczytany i zapisany jednocześnie przez wiele zapytań. -### Dirty Attributes +### Brudne atrybuty -When you call [[yii\db\ActiveRecord::save()|save()]] to save an Active Record instance, only *dirty attributes* -are being saved. An attribute is considered *dirty* if its value has been modified since it was loaded from DB or -saved to DB most recently. Note that data validation will be performed regardless if the Active Record -instance has dirty attributes or not. +Kiedy wywołujesz [[yii\db\ActiveRecord::save()|save()]], aby zapisać instancję Active Record, tylko *brudne atrybuty* są zapisywane. +Atrybut uznawany jest za *brudny* jeśli jego wartość została zmodyfikowana od momentu pobrania z bazy danych lub ostatniego zapisu. +Pamiętaj, że walidacja danych zostanie przeprowadzona niezależnie od tego, czy instancja Active Record zawiera brudne atrybuty czy też nie. -Active Record automatically maintains the list of dirty attributes. It does so by maintaining an older version of -the attribute values and comparing them with the latest one. You can call [[yii\db\ActiveRecord::getDirtyAttributes()]] -to get the attributes that are currently dirty. You can also call [[yii\db\ActiveRecord::markAttributeDirty()]] -to explicitly mark an attribute as dirty. +Active Record automatycznie tworzy listę brudnych atrybutów, poprzez porównanie starej wartości atrybutu do aktualnej. Możesz wywołać metodę +[[yii\db\ActiveRecord::getDirtyAttributes()]], aby otrzymać najnowszą listę brudnych atrybutów. Dodatkowo można wywołać +[[yii\db\ActiveRecord::markAttributeDirty()]], aby oznaczyć konkretny atrybut jako brudny. -If you are interested in the attribute values prior to their most recent modification, you may call -[[yii\db\ActiveRecord::getOldAttributes()|getOldAttributes()]] or [[yii\db\ActiveRecord::getOldAttribute()|getOldAttribute()]]. +Jeśli chcesz sprawdzić wartość atrybutu sprzed ostatniej zmiany, możesz wywołać [[yii\db\ActiveRecord::getOldAttributes()|getOldAttributes()]] lub +[[yii\db\ActiveRecord::getOldAttribute()|getOldAttribute()]]. -> Note: The comparison of old and new values will be done using the `===` operator so a value will be considered dirty -> even if it has the same value but a different type. This is often the case when the model receives user input from -> HTML forms where every value is represented as a string. -> To ensure the correct type for e.g. integer values you may apply a [validation filter](input-validation.md#data-filtering): +> Note: Porównanie starej i nowej wartości atrybutu odbywa się za pomocą operatora `===`, zatem atrybut zostanie uznany za brudny nawet jeśli +> ma tą samą wartość, ale jest innego typu. Taka sytuacja zdarza się często, kiedy model jest aktualizowany danymi pochodzącymi z formularza +> HTML, gdzie każda wartość jest reprezentowana jako string. +> Aby upewnić się, że wartości będą odpowiednich typów, np. integer, możesz zaaplikować [filtr walidacji](input-validation.md#data-filtering): > `['attributeName', 'filter', 'filter' => 'intval']`. -### Default Attribute Values -Some of your table columns may have default values defined in the database. Sometimes, you may want to pre-populate your -Web form for an Active Record instance with these default values. To avoid writing the same default values again, -you can call [[yii\db\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] to populate the DB-defined default values -into the corresponding Active Record attributes: +### Domyślne wartości atrybutów + +Niektóre z kolumn tabeli bazy danych mogą mieć przypisane domyślne wartości w bazie danych. W przypadku, gdy chcesz wypełnić takimi wartościami +internetowy formularz dla instancji Active Record, zamiast ponownie ustalać wszystkie domyślne wartości możesz wywołać metodę +[[yii\db\ActiveRecord::loadDefaultValues()|loadDefaultValues()]], która przypisze wszystkie domyślne wartości odpowiednim atrybutom: ```php $customer = new Customer(); $customer->loadDefaultValues(); -// $customer->xyz will be assigned the default value declared when defining the "xyz" column +// $customer->xyz otrzyma domyślną wartość, zadeklarowaną przy definiowaniu kolumny "xyz" ``` -### Updating Multiple Rows +### Aktualizowanie wielu wierszy jednocześnie -The methods described above all work on individual Active Record instances, causing inserting or updating of individual -table rows. To update multiple rows simultaneously, you should call [[yii\db\ActiveRecord::updateAll()|updateAll()]], instead, -which is a static method. +Metody przedstawione powyżej działają na pojedynczych instancjach Active Record, dodając lub aktualizując indywidualne wiersze tabeli. +Aby uaktualnić wiele wierszy jednocześnie, należy wywołać statyczną metodę [[yii\db\ActiveRecord::updateAll()|updateAll()]]. ```php // UPDATE `customer` SET `status` = 1 WHERE `email` LIKE `%@example.com%` Customer::updateAll(['status' => Customer::STATUS_ACTIVE], ['like', 'email', '@example.com']); ``` -Similarly, you can call [[yii\db\ActiveRecord::updateAllCounters()|updateAllCounters()]] to update counter columns of -multiple rows at the same time. +W podobny sposób można wywołać [[yii\db\ActiveRecord::updateAllCounters()|updateAllCounters()]], aby uaktualnić liczniki wielu wierszy w tym samym czasie. ```php // UPDATE `customer` SET `age` = `age` + 1 @@ -465,27 +461,27 @@ Customer::updateAllCounters(['age' => 1]); ``` -## Deleting Data +## Usuwanie danych -To delete a single row of data, first retrieve the Active Record instance corresponding to that row and then call -the [[yii\db\ActiveRecord::delete()]] method. +Aby usunąć pojedynczy wiersz danych, utwórz najpierw instancję Active Record odpowiadającą temu wierszowi, a następnie wywołaj metodę +[[yii\db\ActiveRecord::delete()]]. ```php $customer = Customer::findOne(123); $customer->delete(); ``` -You can call [[yii\db\ActiveRecord::deleteAll()]] to delete multiple or all rows of data. For example, +Możesz również wywołać [[yii\db\ActiveRecord::deleteAll()]], aby usunąć kilka lub wszystkie wiersze danych. Dla przykładu: ```php Customer::deleteAll(['status' => Customer::STATUS_INACTIVE]); ``` -> Note: Be very careful when calling [[yii\db\ActiveRecord::deleteAll()|deleteAll()]] because it may totally - erase all data from your table if you make a mistake in specifying the condition. +> Note: Należy być bardzo ostrożnym przy wywoływaniu [[yii\db\ActiveRecord::deleteAll()|deleteAll()]], ponieważ w efekcie można całkowicie usunąć +> wszystkie dane z tabeli bazy, jeśli popełni się błąd przy ustalaniu warunków dla metody. -## Active Record Life Cycles +## Cykl życia Active Record It is important to understand the life cycles of Active Record when it is used for different purposes. During each life cycle, a certain sequence of methods will be invoked, and you can override these methods From 646c640c16b545f9b0276e3d144ce2701f181436 Mon Sep 17 00:00:00 2001 From: Bizley Date: Tue, 18 Aug 2015 15:49:58 +0200 Subject: [PATCH 3/8] db-active-record 50% EN formatting fixed --- docs/guide-pl/db-active-record.md | 158 ++++++++++++++---------------- docs/guide/db-active-record.md | 8 +- 2 files changed, 76 insertions(+), 90 deletions(-) diff --git a/docs/guide-pl/db-active-record.md b/docs/guide-pl/db-active-record.md index 4b217f787d..1e4dbfb40e 100644 --- a/docs/guide-pl/db-active-record.md +++ b/docs/guide-pl/db-active-record.md @@ -7,7 +7,7 @@ w tabeli - *atrybut* obiektu Active Record reprezentuje wartość konkretnej kol można skorzystać z atrybutów i metod klasy Active Record. Dla przykładu, załóżmy, że `Customer` jest klasą Active Record, powiązaną z tabelą `customer` i `name` jest kolumną w tabeli `customer`. -Aby dodać nowy wiersz do tabeli `customer`, należy wykonać następujący kod: +Aby dodać nowy wiersz do tabeli `customer` wystarczy wykonać następujący kod: ```php $customer = new Customer(); @@ -483,69 +483,58 @@ Customer::deleteAll(['status' => Customer::STATUS_INACTIVE]); ## Cykl życia Active Record -It is important to understand the life cycles of Active Record when it is used for different purposes. -During each life cycle, a certain sequence of methods will be invoked, and you can override these methods -to get a chance to customize the life cycle. You can also respond to certain Active Record events triggered -during a life cycle to inject your custom code. These events are especially useful when you are developing -Active Record [behaviors](concept-behaviors.md) which need to customize Active Record life cycles. +Istotnym elementem pracy z Yii jest zrozumienie cyklu życia Active Record w zależności od metodyki jego użycia. +Podczas każdego cyklu wykonywane są określone sekwencje metod i aby dopasować go do własnych potrzeb, wytarczy je nadpisać. +Można również śledzić i odpowiadać na eventy Active Record uruchamiane podczas cyklu życia, aby wstrzyknąć swój własny kod. +Takie eventy są szczególnie użyteczne podczas tworzenia wpływających na cykl życia [behaviorów](concept-behaviors.md) Active Record. -In the following, we will summarize various Active Record life cycles and the methods/events that are involved -in the life cycles. +Poniżej znajdziesz wyszczególnione cykle życia Active Record wraz z metodami/eventami, które są w nie zaangażowane. -### New Instance Life Cycle +### Cykl życia nowej instancji -When creating a new Active Record instance via the `new` operator, the following life cycle will happen: +Podczas tworzenia nowej instancji Active Record za pomocą operatora `new`, zachodzi następujący cykl: -1. class constructor; -2. [[yii\db\ActiveRecord::init()|init()]]: triggers an [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] event. +1. Konstruktor klasy. +2. [[yii\db\ActiveRecord::init()|init()]]: uruchamia event [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]]. -### Querying Data Life Cycle +### Cykl życia przy pobieraniu danych -When querying data through one of the [querying methods](#querying-data), each newly populated Active Record will -undergo the following life cycle: +Podczas pobierania danych za pomocą jednej z [metod kwerendy](#querying-data), każdy świeżo wypełniony obiekt Active Record przechodzi następujący cykl: -1. class constructor. -2. [[yii\db\ActiveRecord::init()|init()]]: triggers an [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] event. -3. [[yii\db\ActiveRecord::afterFind()|afterFind()]]: triggers an [[yii\db\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]] event. +1. Konstruktor klasy. +2. [[yii\db\ActiveRecord::init()|init()]]: uruchamia event [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]]. +3. [[yii\db\ActiveRecord::afterFind()|afterFind()]]: uruchamia event [[yii\db\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]]. -### Saving Data Life Cycle +### Cykl życia przy zapisywaniu danych -When calling [[yii\db\ActiveRecord::save()|save()]] to insert or update an Active Record instance, the following -life cycle will happen: +Podczas wywołania [[yii\db\ActiveRecord::save()|save()]], w celu dodania lub uaktualnienia danych instancji Active Record, zachodzi następujący cykl: -1. [[yii\db\ActiveRecord::beforeValidate()|beforeValidate()]]: triggers - an [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] event. If the method returns false - or [[yii\base\ModelEvent::isValid]] is false, the rest of the steps will be skipped. -2. Performs data validation. If data validation fails, the steps after Step 3 will be skipped. -3. [[yii\db\ActiveRecord::afterValidate()|afterValidate()]]: triggers - an [[yii\db\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]] event. -4. [[yii\db\ActiveRecord::beforeSave()|beforeSave()]]: triggers - an [[yii\db\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] - or [[yii\db\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]] event. If the method returns false - or [[yii\base\ModelEvent::isValid]] is false, the rest of the steps will be skipped. -5. Performs the actual data insertion or updating; -6. [[yii\db\ActiveRecord::afterSave()|afterSave()]]: triggers - an [[yii\db\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] - or [[yii\db\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]] event. +1. [[yii\db\ActiveRecord::beforeValidate()|beforeValidate()]]: uruchamia event [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]]. + Jeśli metoda zwróci false lub właściwość [[yii\base\ModelEvent::isValid]] ma wartość false, kolejne kroki są pomijane. +2. Proces walidacji danych. Jeśli proces zakończy się niepowodzeniem, kolejne kroki po kroku 3. są pomijane. +3. [[yii\db\ActiveRecord::afterValidate()|afterValidate()]]: uruchamia event [[yii\db\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]]. +4. [[yii\db\ActiveRecord::beforeSave()|beforeSave()]]: uruchamia event [[yii\db\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] lub + [[yii\db\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]]. Jeśli metoda zwróci false lub właściwość [[yii\base\ModelEvent::isValid]] ma + wartość false, kolejne kroki są pomijane. +5. Proces właściwego dodawania lub aktulizowania danych. +6. [[yii\db\ActiveRecord::afterSave()|afterSave()]]: uruchamia event [[yii\db\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] lub + [[yii\db\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]]. -### Deleting Data Life Cycle +### Cykl życia przy usuwaniu danych -When calling [[yii\db\ActiveRecord::delete()|delete()]] to delete an Active Record instance, the following -life cycle will happen: +Podczas wywołania [[yii\db\ActiveRecord::delete()|delete()]], w celu usunięcia danych instancji Active Record instance, zachodzi następujący cykl: -1. [[yii\db\ActiveRecord::beforeDelete()|beforeDelete()]]: triggers - an [[yii\db\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]] event. If the method returns false - or [[yii\base\ModelEvent::isValid]] is false, the rest of the steps will be skipped. -2. perform the actual data deletion -3. [[yii\db\ActiveRecord::afterDelete()|afterDelete()]]: triggers - an [[yii\db\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]] event. +1. [[yii\db\ActiveRecord::beforeDelete()|beforeDelete()]]: uruchamia event [[yii\db\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]]. + Jeśli metoda zwróci false lub właściwość [[yii\base\ModelEvent::isValid]] ma wartość false, kolejne kroki są pomijane. +2. Proces właściwego usuwania danych. +3. [[yii\db\ActiveRecord::afterDelete()|afterDelete()]]: uruchamia event [[yii\db\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]]. -> Note: Calling any of the following methods will NOT initiate any of the above life cycles: +> Note: Wywołanie poniższych metod NIE uruchomi żadnego z powyższych cykli: > > - [[yii\db\ActiveRecord::updateAll()]] > - [[yii\db\ActiveRecord::deleteAll()]] @@ -553,11 +542,11 @@ life cycle will happen: > - [[yii\db\ActiveRecord::updateAllCounters()]] -## Working with Transactions +## Praca z transakcjami -There are two ways of using [transactions](db-dao.md#performing-transactions) while working with Active Record. +Są dwa sposoby użycia [transakcji](db-dao.md#performing-transactions) podczas pracy z Active Record. -The first way is to explicitly enclose Active Record method calls in a transactional block, like shown below, +Pierwszy zakłada bezpośrednie ujęcie wywołań metod Active Record w blok transakcji, jak pokazano to poniżej: ```php $customer = Customer::findOne(123); @@ -565,16 +554,16 @@ $customer = Customer::findOne(123); Customer::getDb()->transaction(function($db) use ($customer) { $customer->id = 200; $customer->save(); - // ...other DB operations... + // ...inne operacje bazodanowe... }); -// or alternatively +// lub alternatywnie $transaction = Customer::getDb()->beginTransaction(); try { $customer->id = 200; $customer->save(); - // ...other DB operations... + // ...inne operacje bazodanowe... $transaction->commit(); } catch(\Exception $e) { $transaction->rollBack(); @@ -582,8 +571,8 @@ try { } ``` -The second way is to list the DB operations that require transactional support in the [[yii\db\ActiveRecord::transactions()]] -method. For example, +Drugi sposób polega na utworzeniu listy operacji bazodanowych, które wymagają transakcji za pomocą metody [[yii\db\ActiveRecord::transactions()]]. +Dla przykładu: ```php class Customer extends ActiveRecord @@ -593,61 +582,58 @@ class Customer extends ActiveRecord return [ 'admin' => self::OP_INSERT, 'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE, - // the above is equivalent to the following: + // powyższy zapis jest odpowiednikiem następującego skróconego: // 'api' => self::OP_ALL, ]; } } ``` -The [[yii\db\ActiveRecord::transactions()]] method should return an array whose keys are [scenario](structure-models.md#scenarios) -names and values the corresponding operations that should be enclosed within transactions. You should use the following -constants to refer to different DB operations: +Metoda [[yii\db\ActiveRecord::transactions()]] powinna zwracać tablicę, której klucze są nazwami [scenariuszy](structure-models.md#scenarios) +a wartości to operacje bazodanowe, które powinny być objęte transakcją. Używaj następujących stałych do określenia typu operacji: -* [[yii\db\ActiveRecord::OP_INSERT|OP_INSERT]]: insertion operation performed by [[yii\db\ActiveRecord::insert()|insert()]]; -* [[yii\db\ActiveRecord::OP_UPDATE|OP_UPDATE]]: update operation performed by [[yii\db\ActiveRecord::update()|update()]]; -* [[yii\db\ActiveRecord::OP_DELETE|OP_DELETE]]: deletion operation performed by [[yii\db\ActiveRecord::delete()|delete()]]. +* [[yii\db\ActiveRecord::OP_INSERT|OP_INSERT]]: operacja dodawania wykonywana za pomocą [[yii\db\ActiveRecord::insert()|insert()]]; +* [[yii\db\ActiveRecord::OP_UPDATE|OP_UPDATE]]: operacja aktualizacji wykonywana za pomocą [[yii\db\ActiveRecord::update()|update()]]; +* [[yii\db\ActiveRecord::OP_DELETE|OP_DELETE]]: operacja usuwania wykonywana za pomocą [[yii\db\ActiveRecord::delete()|delete()]]. -Use `|` operators to concatenate the above constants to indicate multiple operations. You may also use the shortcut -constant [[yii\db\ActiveRecord::OP_ALL|OP_ALL]] to refer to all three operations above. +Używaj operatora `|`, aby podać więcej niż jedną operację za pomocą powyższych stałych. Możesz również użyć stałej dla skróconej definicji +wszystkich trzech powyższych operacji [[yii\db\ActiveRecord::OP_ALL|OP_ALL]]. -## Optimistic Locks +## Optymistyczna blokada -Optimistic locking is a way to prevent conflicts that may occur when a single row of data is being -updated by multiple users. For example, both user A and user B are editing the same wiki article -at the same time. After user A saves his edits, user B clicks on the "Save" button in an attempt to -save his edits as well. Because user B was actually working on an outdated version of the article, -it would be desirable to have a way to prevent him from saving the article and show him some hint message. +Optymistyczne blokowanie jest jednym ze sposobów uniknięcia konfliktów, które mogą wystąpić, kiedy pojedynczy wiersz danych jest aktualizowany przez +kilku użytkowników. Dla przykładu, użytkownik A i użytkownik B edytują artykuł wiki w tym samym czasie - po tym jak użytkownik A zapisał już swoje +zmiany, użytkownik B klika przycisk "Zapisz", aby również wykonać identyczną operację. Ponieważ użytkownik B pracował w rzeczywistości na "starej" wersji +artykułu, byłoby wskazane powstrzymać go przed nadpisaniem wersji użytkownika A i wyświelić jakiś komunikat z wyjaśnieniem sytuacji. -Optimistic locking solves the above problem by using a column to record the version number of each row. -When a row is being saved with an outdated version number, a [[yii\db\StaleObjectException]] exception -will be thrown, which prevents the row from being saved. Optimistic locking is only supported when you -update or delete an existing row of data using [[yii\db\ActiveRecord::update()]] or [[yii\db\ActiveRecord::delete()]], -respectively. +Optymistyczne blokowanie rozwiązuje ten problem za pomocą dodatkowej kolumny w bazie przechowującej numer wersji każdego wiersza. +Kiedy taki wiersz jest zapisywany z wcześniejszym numerem wersji niż aktualna rzucany jest wyjątek [[yii\db\StaleObjectException]], który powstrzymuje +zapis wiersza. Optymistyczne blokowanie może być użyte tylko przy aktualizacji lub usuwaniu istniejącego wiersza za pomocą odpowiednio +[[yii\db\ActiveRecord::update()]] lub [[yii\db\ActiveRecord::delete()]]. -To use optimistic locking, +Aby skorzystać z optymistycznej blokady: -1. Create a column in the DB table associated with the Active Record class to store the version number of each row. - The column should be of big integer type (in MySQL it would be `BIGINT DEFAULT 0`). -2. Override the [[yii\db\ActiveRecord::optimisticLock()]] method to return the name of this column. -3. In the Web form that takes user inputs, add a hidden field to store the current version number of the row being updated. Be sure your version attribute has input validation rules and validates successfully. -4. In the controller action that updates the row using Active Record, try and catch the [[yii\db\StaleObjectException]] - exception. Implement necessary business logic (e.g. merging the changes, prompting staled data) to resolve the conflict. +1. Stwórz kolumnę w tabeli bazy danych powiązaną z klasą Active Record do przechowywania numeru wersji każdego wiersza. + Kolumna powinna być typu big integer (w MySQL `BIGINT DEFAULT 0`). +2. Nadpisz metodę [[yii\db\ActiveRecord::optimisticLock()]], aby zwrócić nazwę tej kolumny. +3. W formularzu pobierającym dane od użytkownika, dodaj ukryte pole, gdzie przechowasz aktualny numer wersji uaktualnianego wiersza. + Upewnij się, że atrybut wersji ma dodaną zasadę walidacji i przechodzi poprawnie proces walidacji. +4. W akcji kontrolera uaktualniającej wiersz za pomocą Active Record, użyj bloku try-catch, aby wyłapać wyjątek [[yii\db\StaleObjectException]]. + Zaimplemetuj odpowiednią logikę biznesową (np. scalenie zmian, wyświetlenie komunikatu o nieaktualnej wersji, itp.), aby rozwiązać konflikt. -For example, assume the version column is named as `version`. You can implement optimistic locking with the code like -the following. +Dla przykładu, załóżmy, że kolumna wersji nazywa się `version`. Implementację optymistycznego blokowania można wykonać za pomocą następującego kodu: ```php -// ------ view code ------- +// ------ kod widoku ------- use yii\helpers\Html; -// ...other input fields +// ...inne pola formularza echo Html::activeHiddenInput($model, 'version'); -// ------ controller code ------- +// ------ kod kontrolera ------- use yii\db\StaleObjectException; @@ -664,7 +650,7 @@ public function actionUpdate($id) ]); } } catch (StaleObjectException $e) { - // logic to resolve the conflict + // logika rozwiązująca konflikt } } ``` diff --git a/docs/guide/db-active-record.md b/docs/guide/db-active-record.md index 17b5e9f9c6..9c19331365 100644 --- a/docs/guide/db-active-record.md +++ b/docs/guide/db-active-record.md @@ -512,7 +512,7 @@ in the life cycles. When creating a new Active Record instance via the `new` operator, the following life cycle will happen: -1. class constructor; +1. Class constructor. 2. [[yii\db\ActiveRecord::init()|init()]]: triggers an [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] event. @@ -521,7 +521,7 @@ When creating a new Active Record instance via the `new` operator, the following When querying data through one of the [querying methods](#querying-data), each newly populated Active Record will undergo the following life cycle: -1. class constructor. +1. Class constructor. 2. [[yii\db\ActiveRecord::init()|init()]]: triggers an [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] event. 3. [[yii\db\ActiveRecord::afterFind()|afterFind()]]: triggers an [[yii\db\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]] event. @@ -541,7 +541,7 @@ life cycle will happen: an [[yii\db\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] or [[yii\db\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]] event. If the method returns false or [[yii\base\ModelEvent::isValid]] is false, the rest of the steps will be skipped. -5. Performs the actual data insertion or updating; +5. Performs the actual data insertion or updating. 6. [[yii\db\ActiveRecord::afterSave()|afterSave()]]: triggers an [[yii\db\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] or [[yii\db\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]] event. @@ -555,7 +555,7 @@ life cycle will happen: 1. [[yii\db\ActiveRecord::beforeDelete()|beforeDelete()]]: triggers an [[yii\db\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]] event. If the method returns false or [[yii\base\ModelEvent::isValid]] is false, the rest of the steps will be skipped. -2. perform the actual data deletion +2. Performs the actual data deletion. 3. [[yii\db\ActiveRecord::afterDelete()|afterDelete()]]: triggers an [[yii\db\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]] event. From be48c7861bf526b074388396175da58f6e2d9e14 Mon Sep 17 00:00:00 2001 From: Bizley Date: Wed, 19 Aug 2015 14:40:45 +0200 Subject: [PATCH 4/8] db-active-record [PL] --- docs/guide-pl/db-active-record.md | 502 ++++++++++++++---------------- 1 file changed, 237 insertions(+), 265 deletions(-) diff --git a/docs/guide-pl/db-active-record.md b/docs/guide-pl/db-active-record.md index 1e4dbfb40e..b03aa84699 100644 --- a/docs/guide-pl/db-active-record.md +++ b/docs/guide-pl/db-active-record.md @@ -7,7 +7,7 @@ w tabeli - *atrybut* obiektu Active Record reprezentuje wartość konkretnej kol można skorzystać z atrybutów i metod klasy Active Record. Dla przykładu, załóżmy, że `Customer` jest klasą Active Record, powiązaną z tabelą `customer` i `name` jest kolumną w tabeli `customer`. -Aby dodać nowy wiersz do tabeli `customer` wystarczy wykonać następujący kod: +Aby dodać nowy wiersz do tabeli `customer`, wystarczy wykonać następujący kod: ```php $customer = new Customer(); @@ -31,7 +31,7 @@ Yii zapewnia wsparcie Active Record dla następujących typów relacyjnych baz d * SQLite 2 i 3: poprzez [[yii\db\ActiveRecord]] * Microsoft SQL Server 2008 lub nowszy: poprzez [[yii\db\ActiveRecord]] * Oracle: poprzez [[yii\db\ActiveRecord]] -* CUBRID 9.3 lub nowszy: poprzez [[yii\db\ActiveRecord]] (Zwróć uwagę, że z powodu [błędu](http://jira.cubrid.org/browse/APIS-658) +* CUBRID 9.3 lub nowszy: poprzez [[yii\db\ActiveRecord]] (zwróć uwagę, że z powodu [błędu](http://jira.cubrid.org/browse/APIS-658) w rozszerzeniu PDO cubrid, umieszczanie wartości w cudzysłowie nie będzie działać, zatem wymagane jest zainstalowanie CUBRID 9.3 zarówno jako klienta jak i serwer) * Sphinx: poprzez [[yii\sphinx\ActiveRecord]], wymaga rozszerzenia `yii2-sphinx` @@ -82,9 +82,9 @@ takie jak atrybuty, zasady walidacji, serializację danych itd. ## Łączenie się z bazą danych -Domyślnie Active Record używa [komponentu aplikacji](structure-application-components.md) `db` jako [[yii\db\Connection|łącznika z bazą]], -do uzyskania dostępu i manipulowania danymi z bazy danych. Jak zostało to już wyjaśnione w sekcji [Obiekty dostępu do danych (DAO)](db-dao.md), -możesz skonfigurować komponent `db` w pliku konfiguracyjnym aplikacji jak poniżej: +Domyślnie Active Record używa [komponentu aplikacji](structure-application-components.md) `db` jako [[yii\db\Connection|połączenia z bazą danych]], +do uzyskania dostępu i manipulowania jej danymi. Jak zostało to już wyjaśnione w sekcji [Obiekty dostępu do danych (DAO)](db-dao.md), +komponent `db` można skonfigurować w pliku konfiguracyjnym aplikacji jak poniżej: ```php return [ @@ -178,7 +178,7 @@ Obie metody mogą przyjmować jeden z następujących formatów parametrów: - tablica asocjacyjna: klucze tablicy są poszukiwanymi nazwami kolumn a wartości tablicy są odpowiadającymi im wartościami kolumn. Po więcej szczegółów zajrzyj do rozdziału [Format asocjacyjny](db-query-builder.md#hash-format). -Poniższy kod pokazuje jak mogę być użyte opisane metody: +Poniższy kod pokazuje, jak mogą być użyte opisane metody: ```php // zwraca pojedynczego klienta o ID 123 @@ -236,17 +236,16 @@ $email = $customer->email; > NIE należy definiować ich własnoręcznie. Ponieważ atrybuty Active Record nazywane są zgodnie z nazwami kolumn, możesz natknąć się na kod PHP typu -`$customer->first_name`, gdzie podkreślniki używane są do oddzielenia poszczególnych słów w nazwach atrybutów, jeśli kolumny tabeli nazywane są +`$customer->first_name`, gdzie podkreślniki używane są do oddzielenia poszczególnych słów w nazwach atrybutów, w przypadku, gdy kolumny tabeli nazywane są właśnie w ten sposób. Jeśli masz wątpliwości dotyczące spojności takiego stylu programowania, powinieneś zmienić odpowiednio nazwy kolumn tabeli -(używając np. camelCase). +(używając np. formatowania typu "camelCase"). ### Transformacja danych Często zdarza się, że dane wprowadzane i/lub wyświetlane zapisane są w formacie różniącym się od tego używanego w bazie danych. Dla przykładu, w bazie danych przechowywane są daty urodzin klientów jako uniksowe znaczniki czasu, podczas gdy w większości przypadków pożądana forma zapisu daty -to `'RRRR/MM/DD'`. Aby osiągnąć ten cel, można zdefiniować metody *transformujące dane* w klasie `Customer` -Active Record class like the following: +to `'RRRR/MM/DD'`. Aby osiągnąć ten format, można zdefiniować metody *transformujące dane* w klasie `Customer`: ```php class Customer extends ActiveRecord @@ -269,13 +268,13 @@ Od tego momentu, w kodzie PHP, zamiast odwołać się do `$customer->birthday`, wprowadzenie i wyświetlenie daty urodzin klienta w formacie `'RRRR/MM/DD'`. > Tip: Powyższy przykład pokazuje podstawowy sposób transformacji danych. Podczas zwyczajowej pracy z formularzami danych można skorzystać z -> [DateValidator](tutorial-core-validators.md#date) i [[yii\jui\DatePicker|DatePicker]], co jest prostsze w użyciu i posiadające więcej możliwości. +> [DateValidator](tutorial-core-validators.md#date) i [[yii\jui\DatePicker|DatePicker]], co jest prostsze w użyciu i daje więcej możliwości. ### Pobieranie danych jako tablice Pobieranie danych jako obiekty Active Record jest wygodne i elastyczne, ale nie zawsze pożądane, zwłaszcza kiedy konieczne jest -uzyskanie ogromnej liczby danych, z powodu użycia sporej ilości pamięci. W takim przypadku można pobrać dane jako tablicę PHP wywołując metodę +uzyskanie ogromnej liczby danych, z powodu użycia sporej ilości pamięci. W takim przypadku można pobrać dane jako tablicę PHP, wywołując metodę [[yii\db\ActiveQuery::asArray()|asArray()]] przed wykonaniem kwerendy: ```php @@ -356,8 +355,8 @@ public function save($runValidation = true, $attributeNames = null) } ``` -> Tip: Możesz również wywołać [[yii\db\ActiveRecord::insert()|insert()]] lub [[yii\db\ActiveRecord::update()|update()]] bezpośrednio, aby dodać lub -> uaktualnić wiersz. +> Tip: Możesz również wywołać [[yii\db\ActiveRecord::insert()|insert()]] lub [[yii\db\ActiveRecord::update()|update()]] bezpośrednio, aby, odpowiednio, +> dodać lub uaktualnić wiersz. ### Walidacja danych @@ -366,9 +365,9 @@ Dzięki temu, że [[yii\db\ActiveRecord]] rozszerza klasę [[yii\base\Model]], k Możesz definiować zasady walidacji nadpisując metodę [[yii\db\ActiveRecord::rules()|rules()]] i uruchamiać procedurę walidacji wywołując metodę [[yii\db\ActiveRecord::validate()|validate()]]. -Wywołanie [[yii\db\ActiveRecord::save()|save()]] automatycznie wywołuje również [[yii\db\ActiveRecord::validate()|validate()]]. -Dopiero po pomyślnym przejściu walidacji rozpocznie się proces zapisywania danych; w przeciwnym wypadku zostanie zwrócona flaga false, a komunikaty o -błędach walidacji można odczytać sprawdzając właściwość [[yii\db\ActiveRecord::errors|errors]]. +Wywołanie [[yii\db\ActiveRecord::save()|save()]] automatycznie wywołuje również metodę [[yii\db\ActiveRecord::validate()|validate()]]. +Dopiero po pomyślnym przejściu walidacji rozpocznie się proces zapisywania danych; w przeciwnym wypadku zostanie zwrócona flaga false - komunikaty z +błędami walidacji można odczytać sprawdzając właściwość [[yii\db\ActiveRecord::errors|errors]]. > Tip: Jeśli masz pewność, że dane nie potrzebują przechodzić procesu walidacji (np. pochodzą z zaufanych źródeł), możesz wywołać `save(false)`, > aby go pominąć. @@ -376,8 +375,9 @@ błędach walidacji można odczytać sprawdzając właściwość [[yii\db\Active ### Masowe przypisywanie -Tak jak w zwyczajnych [modelach](structure-models.md), instancje Active Record posiadają również mechanizm [masowego przypisywania](structure-models.md#massive-assignment). -Funkcjonalność ta umożliwia przypisanie wartości wielu atrybutom Active Record za pomocą pojedynczej instrukcji PHP, jak pokazano to poniżej. +Tak jak w zwyczajnych [modelach](structure-models.md), instancje Active Record posiadają również mechanizm +[masowego przypisywania](structure-models.md#massive-assignment). Funkcjonalność ta umożliwia przypisanie wartości wielu atrybutom Active Record za +pomocą pojedynczej instrukcji PHP, jak pokazano to poniżej. Należy jednak pamiętać, że w ten sposób mogą być przypisane tylko [bezpieczne atrybuty](structure-models.md#safe-attributes). ```php @@ -407,7 +407,7 @@ $post->updateCounters(['view_count' => 1]); ``` > Note: Jeśli używasz [[yii\db\ActiveRecord::save()]] do aktualizacji licznika, możesz otrzymać nieprawidłowe rezultaty, ponieważ jest możliwe, że - ten sam licznik zostanie odczytany i zapisany jednocześnie przez wiele zapytań. +> ten sam licznik zostanie odczytany i zapisany jednocześnie przez wiele zapytań. ### Brudne atrybuty @@ -433,7 +433,7 @@ Jeśli chcesz sprawdzić wartość atrybutu sprzed ostatniej zmiany, możesz wyw ### Domyślne wartości atrybutów Niektóre z kolumn tabeli bazy danych mogą mieć przypisane domyślne wartości w bazie danych. W przypadku, gdy chcesz wypełnić takimi wartościami -internetowy formularz dla instancji Active Record, zamiast ponownie ustalać wszystkie domyślne wartości możesz wywołać metodę +formularz dla instancji Active Record, zamiast ponownie ustawiać wszystkie domyślne wartości, możesz wywołać metodę [[yii\db\ActiveRecord::loadDefaultValues()|loadDefaultValues()]], która przypisze wszystkie domyślne wartości odpowiednim atrybutom: ```php @@ -484,7 +484,7 @@ Customer::deleteAll(['status' => Customer::STATUS_INACTIVE]); ## Cykl życia Active Record Istotnym elementem pracy z Yii jest zrozumienie cyklu życia Active Record w zależności od metodyki jego użycia. -Podczas każdego cyklu wykonywane są określone sekwencje metod i aby dopasować go do własnych potrzeb, wytarczy je nadpisać. +Podczas każdego cyklu wykonywane są określone sekwencje metod i aby dopasować go do własnych potrzeb, wystarczy je nadpisać. Można również śledzić i odpowiadać na eventy Active Record uruchamiane podczas cyklu życia, aby wstrzyknąć swój własny kod. Takie eventy są szczególnie użyteczne podczas tworzenia wpływających na cykl życia [behaviorów](concept-behaviors.md) Active Record. @@ -526,7 +526,7 @@ Podczas wywołania [[yii\db\ActiveRecord::save()|save()]], w celu dodania lub ua ### Cykl życia przy usuwaniu danych -Podczas wywołania [[yii\db\ActiveRecord::delete()|delete()]], w celu usunięcia danych instancji Active Record instance, zachodzi następujący cykl: +Podczas wywołania [[yii\db\ActiveRecord::delete()|delete()]], w celu usunięcia danych instancji Active Record, zachodzi następujący cykl: 1. [[yii\db\ActiveRecord::beforeDelete()|beforeDelete()]]: uruchamia event [[yii\db\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]]. Jeśli metoda zwróci false lub właściwość [[yii\base\ModelEvent::isValid]] ma wartość false, kolejne kroki są pomijane. @@ -605,7 +605,7 @@ wszystkich trzech powyższych operacji [[yii\db\ActiveRecord::OP_ALL|OP_ALL]]. Optymistyczne blokowanie jest jednym ze sposobów uniknięcia konfliktów, które mogą wystąpić, kiedy pojedynczy wiersz danych jest aktualizowany przez kilku użytkowników. Dla przykładu, użytkownik A i użytkownik B edytują artykuł wiki w tym samym czasie - po tym jak użytkownik A zapisał już swoje zmiany, użytkownik B klika przycisk "Zapisz", aby również wykonać identyczną operację. Ponieważ użytkownik B pracował w rzeczywistości na "starej" wersji -artykułu, byłoby wskazane powstrzymać go przed nadpisaniem wersji użytkownika A i wyświelić jakiś komunikat z wyjaśnieniem sytuacji. +artykułu, byłoby wskazane powstrzymać go przed nadpisaniem wersji użytkownika A i wyświelić komunikat wyjaśniający sytuację. Optymistyczne blokowanie rozwiązuje ten problem za pomocą dodatkowej kolumny w bazie przechowującej numer wersji każdego wiersza. Kiedy taki wiersz jest zapisywany z wcześniejszym numerem wersji niż aktualna rzucany jest wyjątek [[yii\db\StaleObjectException]], który powstrzymuje @@ -615,10 +615,10 @@ zapis wiersza. Optymistyczne blokowanie może być użyte tylko przy aktualizacj Aby skorzystać z optymistycznej blokady: 1. Stwórz kolumnę w tabeli bazy danych powiązaną z klasą Active Record do przechowywania numeru wersji każdego wiersza. - Kolumna powinna być typu big integer (w MySQL `BIGINT DEFAULT 0`). + Kolumna powinna być typu big integer (przykładowo w MySQL `BIGINT DEFAULT 0`). 2. Nadpisz metodę [[yii\db\ActiveRecord::optimisticLock()]], aby zwrócić nazwę tej kolumny. 3. W formularzu pobierającym dane od użytkownika, dodaj ukryte pole, gdzie przechowasz aktualny numer wersji uaktualnianego wiersza. - Upewnij się, że atrybut wersji ma dodaną zasadę walidacji i przechodzi poprawnie proces walidacji. + Upewnij się, że atrybut wersji ma dodaną zasadę walidacji i przechodzi poprawnie jej proces. 4. W akcji kontrolera uaktualniającej wiersz za pomocą Active Record, użyj bloku try-catch, aby wyłapać wyjątek [[yii\db\StaleObjectException]]. Zaimplemetuj odpowiednią logikę biznesową (np. scalenie zmian, wyświetlenie komunikatu o nieaktualnej wersji, itp.), aby rozwiązać konflikt. @@ -656,19 +656,18 @@ public function actionUpdate($id) ``` -## Working with Relational Data +## Praca z danymi relacji -Besides working with individual database tables, Active Record is also capable of bringing together related data, -making them readily accessible through the primary data. For example, the customer data is related with the order -data because one customer may have placed one or multiple orders. With appropriate declaration of this relation, -you may be able to access a customer's order information using the expression `$customer->orders` which gives -back the customer's order information in terms of an array of `Order` Active Record instances. +Oprócz korzystania z indywidualnych tabel bazy danych, Active Record umożliwia również na uzyskanie danych relacji, +pozwalając na odczytanie ich z poziomu głównego obiektu. Dla przykładu, dane klienta są powiązane relacją z danymi zamówienia, +ponieważ jeden klient może złożyć jedno lub wiele zamówień. Odpowiednio deklarując tę relację, można uzyskać dane zamówienia klienta, +używając wyrażenia `$customer->orders`, które zwróci informacje o zamówieniu klienta jako tablicę instancji `Order` typu Active Record. -### Declaring Relations +### Deklarowanie relacji -To work with relational data using Active Record, you first need to declare relations in Active Record classes. -The task is as simple as declaring a *relation method* for every interested relation, like the following, +Aby móc pracować z relacjami używając Active Record, najpierw musisz je zadeklarować w obrębie klasy. +Deklaracja odbywa się za pomocą utworzenia prostej *metody relacyjnej* dla każdej relacji osobno, jak w przykładach poniżej: ```php class Customer extends ActiveRecord @@ -688,75 +687,70 @@ class Order extends ActiveRecord } ``` -In the above code, we have declared an `orders` relation for the `Customer` class, and a `customer` relation -for the `Order` class. +W powyższym kodzie, zadeklarowano relację `orders` dla klasy `Customer` i relację `customer` dla klasy `Order`. -Each relation method must be named as `getXyz`. We call `xyz` (the first letter is in lower case) the *relation name*. -Note that relation names are *case sensitive*. +Każda metoda relacyjna musi mieć nazwę utworzoną według wzoru `getXyz`. `xyz` (pierwsza litera jest mała) jest *nazwą relacji*. +Zwróć uwagę na to, że nazwy relacji *uwzględniają wielkość liter*. -While declaring a relation, you should specify the following information: +Deklarując relację powinno się zwrócić uwagę na następujące dane: -- the multiplicity of the relation: specified by calling either [[yii\db\ActiveRecord::hasMany()|hasMany()]] - or [[yii\db\ActiveRecord::hasOne()|hasOne()]]. In the above example you may easily read in the relation - declarations that a customer has many orders while an order only has one customer. -- the name of the related Active Record class: specified as the first parameter to - either [[yii\db\ActiveRecord::hasMany()|hasMany()]] or [[yii\db\ActiveRecord::hasOne()|hasOne()]]. - A recommended practice is to call `Xyz::className()` to get the class name string so that you can receive - IDE auto-completion support as well as error detection at compiling stage. -- the link between the two types of data: specifies the column(s) through which the two types of data are related. - The array values are the columns of the primary data (represented by the Active Record class that you are declaring - relations), while the array keys are the columns of the related data. +- mnogość relacji: określona przez wywołanie odpowiednio [[yii\db\ActiveRecord::hasMany()|hasMany()]] + lub [[yii\db\ActiveRecord::hasOne()|hasOne()]]. W powyższym przykładzie można łatwo zobaczyć w definicji relacji, że + klient może mieć wiele zamówień, podczas gdy zamówienie ma tylko jednego klienta. +- nazwę powiązanej klasy Active Record: określoną jako pierwszy argument w [[yii\db\ActiveRecord::hasMany()|hasMany()]] lub + [[yii\db\ActiveRecord::hasOne()|hasOne()]]. + Rekomendowany sposób uzyskania nazwy klasy to wywołanie `Xyz::className()`, dzięki czemu możemy posiłkować się wsparciem autouzupełniania IDE + i wykrywaniem błędów na poziomie kompilacji. +- powiązanie pomiędzy dwoma rodzajami danych: określone jako kolumna(y), poprzez którą dane nawiązują relację. + Wartości tablicy są kolumnami głównych danych (reprezentowanymi przez klasę Active Record, w której deklaruje się relacje), a klucze tablicy są + kolumnami danych relacyjnych. - An easy rule to remember this is, as you see in the example above, you write the column that belongs to the related - Active Record directly next to it. You see there that `customer_id` is a property of `Order` and `id` is a property - of `Customer`. + Aby łatwo opanować technikę deklarowania relacji wystarczy zapamiętać, że kolumnę należącą do relacyjnej klasy Active Record zapisuje się zaraz obok + jej nazwy (jak to widać w przykładzie powyżej - `customer_id` jest właściwością `Order` a `id` jest właściwością `Customer`). -### Accessing Relational Data +### Uzyskiwanie dostępu do danych relacji -After declaring relations, you can access relational data through relation names. This is just like accessing -an object [property](concept-properties.md) defined by the relation method. For this reason, we call it *relation property*. -For example, +Po zadeklarowaniu relacji, możesz uzyskać dostęp do danych poprzez jej nazwę. Odbywa się to w taki sam sposób jak uzyskiwanie dostępu do +[właściwości](concept-properties.md) obiektu zdefiniowanego w metodzie relacyjnej. Właśnie dlatego też nazywamy je *właściwościami relacji*. +Przykład: ```php // SELECT * FROM `customer` WHERE `id` = 123 $customer = Customer::findOne(123); // SELECT * FROM `order` WHERE `customer_id` = 123 -// $orders is an array of Order objects +// $orders jest tablicą obiektów typu Order $orders = $customer->orders; ``` -> Info: When you declare a relation named `xyz` via a getter method `getXyz()`, you will be able to access - `xyz` like an [object property](concept-properties.md). Note that the name is case sensitive. +> Info: Deklarując relację o nazwie `xyz` poprzez metodę-getter `getXyz()`, uzyskasz dostęp do `xyz` jak do [właściwości obiektu](concept-properties.md). +> Zwróć uwagę na to, że nazwa uwzględnia wielkość liter. -If a relation is declared with [[yii\db\ActiveRecord::hasMany()|hasMany()]], accessing this relation property -will return an array of the related Active Record instances; if a relation is declared with -[[yii\db\ActiveRecord::hasOne()|hasOne()]], accessing the relation property will return the related -Active Record instance or null if no related data is found. +Jeśli relacja jest zadeklarowana poprzez [[yii\db\ActiveRecord::hasMany()|hasMany()]], zwraca tablicę powiązanych instancji Active Record; +jeśli deklaracja odbywa się poprzez [[yii\db\ActiveRecord::hasOne()|hasOne()]], zwraca pojedynczą powiązaną instancję Active Record lub wartość null, +w przypadku, gdy nie znaleziono powiązanych danych. -When you access a relation property for the first time, a SQL statement will be executed, like shown in the -above example. If the same property is accessed again, the previous result will be returned without re-executing -the SQL statement. To force re-executing the SQL statement, you should unset the relation property -first: `unset($customer->orders)`. +Podczas pierwszego odwołania się do właściwości relacji wykonywana jest kwerenda SQL, tak jak pokazano to w przykładzie powyżej. +Odwołanie się do tej samej właściwości kolejny raz zwróci poprzedni wynik, bez wykonywanie ponownie kwerendy. Aby wymusić wykonanie kwerendy w takiej +sytuacji, należy najpierw usunąć z pamięci właściwość relacyjną poprzez `unset($customer->orders)`. -> Note: While this concept looks similar to the [object property](concept-properties.md) feature, there is an -> important difference. For normal object properties the property value is of the same type as the defining getter method. -> A relation method however returns an [[yii\db\ActiveQuery]] instance, while accessing a relation property will either -> return a [[yii\db\ActiveRecord]] instance or an array of these. +> Note: Pomimo podobieństwa mechanizmu relacji do [właściwości obiektu](concept-properties.md), jest tutaj znacząca różnica. +> Wartości właściwości zwykłych obiektów są tego samego typu jak definiująca je metoda-getter. +> Metoda relacyjna zwraca jednak instancję [[yii\db\ActiveQuery]], a właściwości relacji są instancjami [[yii\db\ActiveRecord]] lub tablicą takich obiektów. > > ```php -> $customer->orders; // is an array of `Order` objects -> $customer->getOrders(); // returns an ActiveQuery instance +> $customer->orders; // tablica obiektów `Order` +> $customer->getOrders(); // instancja ActiveQuery > ``` > -> This is useful for creating customized queries, which is described in the next section. +> Taka funkcjonalność jest użyteczna przy tworzeniu kwerend dostosowanych do potrzeb programisty, co opisane jest w następnej sekcji. -### Dynamic Relational Query +### Dynamiczne kwerendy relacyjne -Because a relation method returns an instance of [[yii\db\ActiveQuery]], you can further build this query -using query building methods before performing DB query. For example, +Dzięki temu, że metoda relacyjna zwraca instancję [[yii\db\ActiveQuery]], możliwe jest dalsze rozbudowanie takiej kwerendy korzystając z +metod konstruowania kwerend. Dla przykładu: ```php $customer = Customer::findOne(123); @@ -768,11 +762,11 @@ $orders = $customer->getOrders() ->all(); ``` -Unlike accessing a relation property, each time you perform a dynamic relational query via a relation method, -a SQL statement will be executed, even if the same dynamic relational query was performed before. +Inaczej niż w przypadku właściwości relacji, za każdym razem, gdy wywyłujesz dynamiczną kwerendę relacyjną poprzez metodę relacji, wykonywane jest +zapytanie do bazy, nawet jeśli identyczna kwerenda została już wywołana wcześniej. -Sometimes you may even want to parametrize a relation declaration so that you can more easily perform -dynamic relational query. For example, you may declare a `bigOrders` relation as follows, +Możliwe jest także sparametryzowanie deklaracji relacji, dzięki czemu można w łatwiejszy sposób wykonywać relacyjne kwerendy. Dla przykładu, możesz +zadeklarować relację `bigOrders` jak to pokazano poniżej: ```php class Customer extends ActiveRecord @@ -786,7 +780,7 @@ class Customer extends ActiveRecord } ``` -Then you will be able to perform the following relational queries: +Dzięki czemu możesz wykonać następujące relacyjne kwerendy: ```php // SELECT * FROM `order` WHERE `subtotal` > 200 ORDER BY `id` @@ -797,17 +791,16 @@ $orders = $customer->bigOrders; ``` -### Relations via a Junction Table +### Relacje za pomocą tabeli węzła -In database modelling, when the multiplicity between two related tables is many-to-many, -a [junction table](https://en.wikipedia.org/wiki/Junction_table) is usually introduced. For example, the `order` -table and the `item` table may be related via a junction table named `order_item`. One order will then correspond -to multiple order items, while one product item will also correspond to multiple order items. +W projekcie bazy danych, kiedy połączenie pomiędzy dwoma relacyjnymi tabelami jest typu wiele-do-wielu, zwykle stosuje się tzw. +[tabelę węzła](https://en.wikipedia.org/wiki/Junction_table). Dla przykładu, tabela `order` i tabela `item` mogą być powiązane poprzez węzeł nazwany +`order_item`. Jedno zamówienie będzie posiadało wiele produktów zamówienia (pozycji), a każdy indywidualny produkt będzie także powiązany z wieloma +pozycjami zamówienia. -When declaring such relations, you would call either [[yii\db\ActiveQuery::via()|via()]] or [[yii\db\ActiveQuery::viaTable()|viaTable()]] -to specify the junction table. The difference between [[yii\db\ActiveQuery::via()|via()]] and [[yii\db\ActiveQuery::viaTable()|viaTable()]] -is that the former specifies the junction table in terms of an existing relation name while the latter directly -the junction table. For example, +Deklarując takie relacje, możesz wywołać zarówno metodę [[yii\db\ActiveQuery::via()|via()]] jak i [[yii\db\ActiveQuery::viaTable()|viaTable()]], aby +określić tabelę węzła. Różnica pomiędzy [[yii\db\ActiveQuery::via()|via()]] i [[yii\db\ActiveQuery::viaTable()|viaTable()]] jest taka, że pierwsza metoda +definiuje tabelę węzła dla istniejącej nazwy relacji, podczas gdy druga definiuje bezpośrednio węzeł. Przykład: ```php class Order extends ActiveRecord @@ -820,7 +813,7 @@ class Order extends ActiveRecord } ``` -or alternatively, +lub alternatywnie, ```php class Order extends ActiveRecord @@ -838,7 +831,7 @@ class Order extends ActiveRecord } ``` -The usage of relations declared with a junction table is the same as that of normal relations. For example, +Sposób użycia relacji zadeklarowanych z pomocą tabeli węzła jest taki sam jak dla zwykłych relacji. Dla przykładu: ```php // SELECT * FROM `order` WHERE `id` = 100 @@ -846,17 +839,17 @@ $order = Order::findOne(100); // SELECT * FROM `order_item` WHERE `order_id` = 100 // SELECT * FROM `item` WHERE `item_id` IN (...) -// returns an array of Item objects +// zwraca tablicę obiektów Item $items = $order->items; ``` -### Lazy Loading and Eager Loading +### Pobieranie leniwe i gorliwe -In [Accessing Relational Data](#accessing-relational-data), we explained that you can access a relation property -of an Active Record instance like accessing a normal object property. A SQL statement will be executed only when -you access the relation property the first time. We call such relational data accessing method *lazy loading*. -For example, +W sekcji [Uzyskiwanie dostępu do danych relacji](#accessing-relational-data) wyjaśniliśmy, że można uzyskać dostęp do właściwości relacji instancji +Active Record w identyczny sposób jak w przypadku zwykłych właściwości obiektu. Kwerenda SQL zostanie wykonana tylko w momencie pierwszego +odwołania się do właściwości relacji. Taki sposób uzyskiwania relacyjnych danych nazywamy *pobieraniem leniwym*. +Przykład: ```php // SELECT * FROM `customer` WHERE `id` = 123 @@ -865,13 +858,12 @@ $customer = Customer::findOne(123); // SELECT * FROM `order` WHERE `customer_id` = 123 $orders = $customer->orders; -// no SQL executed +// bez wykonywania zapytania SQL $orders2 = $customer->orders; ``` -Lazy loading is very convenient to use. However, it may suffer from a performance issue when you need to access -the same relation property of multiple Active Record instances. Consider the following code example. How many -SQL statements will be executed? +Leniwe pobieranie jest bardzo wygodne w użyciu, może jednak powodować spadek wydajności aplikacji, kiedy konieczne jest uzyskanie dostępu do +tej samej relacyjnej właściwości dla wielu instancji Active Record. Rozważmy poniższy przykład - ile zapytań SQL zostanie wykonanych? ```php // SELECT * FROM `customer` LIMIT 100 @@ -883,11 +875,10 @@ foreach ($customers as $customer) { } ``` -As you can see from the code comment above, there are 101 SQL statements being executed! This is because each -time you access the `orders` relation property of a different `Customer` object in the for-loop, a SQL statement -will be executed. +Jak wynika z opisu powyżej, zostanie wykonanych aż 101 kwerend SQL! Dzieje się tak, ponieważ za każdym razem, gdy uzyskujemy dostęp do właściwości +relacyjnej `orders` dla kolejnego obiektu `Customer` w pętli, wykonywane jest nowe zapytanie SQL. -To solve this performance problem, you can use the so-called *eager loading* approach as shown below, +Aby rozwiązać ten wydajnościowy problem, należy użyć tak zwanego *gorliwego pobierania*, jak w przykładzie poniżej: ```php // SELECT * FROM `customer` LIMIT 100; @@ -898,52 +889,51 @@ $customers = Customer::find() ->all(); foreach ($customers as $customer) { - // no SQL executed + // kwerenda SQL nie jest wykonywana $orders = $customer->orders; } ``` -By calling [[yii\db\ActiveQuery::with()]], you instruct Active Record to bring back the orders for the first 100 -customers in one single SQL statement. As a result, you reduce the number of the executed SQL statements from 101 to 2! +Wywołanie metody [[yii\db\ActiveQuery::with()]] powoduje pobranie zamówień dla pierwszych 100 klientów w pojedynczej kwerendzie SQL, dzięki czemu +redukujemy ilość zapytań ze 101 do 2! -You can eagerly load one or multiple relations. You can even eagerly load *nested relations*. A nested relation is a relation -that is declared within a related Active Record class. For example, `Customer` is related with `Order` through the `orders` -relation, and `Order` is related with `Item` through the `items` relation. When querying for `Customer`, you can eagerly -load `items` using the nested relation notation `orders.items`. +Możliwe jest gorliwe pobranie jednej lub wielu relacji, a nawet gorliwe pobranie *zagnieżdżonych relacji*. Zagnieżdżona relacja to taka, która +została zadeklarowana w relacyjnej klasie Active Record. Dla przykładu, `Customer` jest powiązany z `Order` poprzez relację `orders`, a `Order` +jest powiązany z `Item` poprzez relację `items`. Ładując dane dla `Customer`, możesz gorliwie pobrać `items` używając notacji zagnieżdżonej +relacji `orders.items`. -The following code shows different usage of [[yii\db\ActiveQuery::with()|with()]]. We assume the `Customer` class -has two relations `orders` and `country`, while the `Order` class has one relation `items`. +Poniższy kod pokazuje różne sposoby użycia [[yii\db\ActiveQuery::with()|with()]]. Zakładamy, że klasa `Customer` posiada dwie relacje `orders` i +`country`, a klasa `Order` jedną relację `items`. ```php -// eager loading both "orders" and "country" +// gorliwe pobieranie "orders" i "country" $customers = Customer::find()->with('orders', 'country')->all(); -// equivalent to the array syntax below +// odpowiednik powyższego w zapisie tablicowym $customers = Customer::find()->with(['orders', 'country'])->all(); -// no SQL executed +// kwerenda SQL nie jest wykonywana $orders= $customers[0]->orders; -// no SQL executed +// kwerenda SQL nie jest wykonywana $country = $customers[0]->country; -// eager loading "orders" and the nested relation "orders.items" +// gorliwe pobieranie "orders" i zagnieżdżonej relacji "orders.items" $customers = Customer::find()->with('orders.items')->all(); -// access the items of the first order of the first customer -// no SQL executed +// uzyskanie dostępu do produktów pierwszego zamówienia pierwszego klienta +// kwerenda SQL nie jest wykonywana $items = $customers[0]->orders[0]->items; ``` -You can eagerly load deeply nested relations, such as `a.b.c.d`. All parent relations will be eagerly loaded. -That is, when you call [[yii\db\ActiveQuery::with()|with()]] using `a.b.c.d`, you will eagerly load -`a`, `a.b`, `a.b.c` and `a.b.c.d`. +Możesz pobrać gorliwie także głęboko zagnieżdżone relacje, jak np. `a.b.c.d`. Każda z kolejnych następujących po sobie relacji zostanie pobrana gorliwie - +wywołując [[yii\db\ActiveQuery::with()|with()]] z `a.b.c.d`, pobierzesz `a`, `a.b`, `a.b.c` i `a.b.c.d`. -> Info: In general, when eagerly loading `N` relations among which `M` relations are defined with a - [junction table](#junction-table), a total number of `N+M+1` SQL statements will be executed. - Note that a nested relation `a.b.c.d` counts as 4 relations. +> Info: Podsumowując, podczas gorliwego pobierania `N` relacji, pośród których `M` relacji jest zdefiniowanych za pomocą +> [tabeli węzła](#junction-table), zostanie wykonanych łącznie `N+M+1` kwerend SQL. +> Zwróć uwagę na to, że zagnieżdżona relacja `a.b.c.d` jest liczona jako 4 relacje. -When eagerly loading a relation, you can customize the corresponding relational query using an anonymous function. -For example, +Podczas gorliwego pobierania relacji, możesz dostosować kwerendę do własnych potrzeb korzystając z funkcji anonimowej. +Przykład: ```php -// find customers and bring back together their country and active orders +// znajdź klientów i pobierz ich kraje zamieszkania i aktywne zamówienia // SELECT * FROM `customer` // SELECT * FROM `country` WHERE `id` IN (...) // SELECT * FROM `order` WHERE `customer_id` IN (...) AND `status` = 1 @@ -955,31 +945,27 @@ $customers = Customer::find()->with([ ])->all(); ``` -When customizing the relational query for a relation, you should specify the relation name as an array key -and use an anonymous function as the corresponding array value. The anonymous function will receive a `$query` parameter -which represents the [[yii\db\ActiveQuery]] object used to perform the relational query for the relation. -In the code example above, we are modifying the relational query by appending an additional condition about order status. +Dostosowując relacyjną kwerendę należy podać nazwę relacji jako klucz tablicy i użyć funkcji anonimowej jako odpowiadającej kluczowi wartości. +Funkcja anonimowa otrzymuje parametr `$query`, reprezentujący obiekt [[yii\db\ActiveQuery]], służący do wykonania relacyjnej kwerendy. +W powyższym przykładzie modyfikujemy relacyjną kwerendę dodając warunek ze statusem zamówienia. -> Note: If you call [[yii\db\Query::select()|select()]] while eagerly loading relations, you have to make sure -> the columns referenced in the relation declarations are being selected. Otherwise, the related models may not -> be loaded properly. For example, +> Note: Wywołując [[yii\db\Query::select()|select()]] podczas gorliwego pobierania relacji, należy upewnić się, że kolumny określone w deklaracji +> relacji znajdują się na liście pobieranych. W przeciwnym razie powiązany model może nie zostać poprawnie załadowany. Przykład: > > ```php > $orders = Order::find()->select(['id', 'amount'])->with('customer')->all(); -> // $orders[0]->customer is always null. To fix the problem, you should do the following: +> // $orders[0]->customer ma zawsze wartość null. Aby rozwiązać ten problem, należy użyć: > $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all(); > ``` -### Joining with Relations +### Przyłączanie relacji -> Note: The content described in this subsection is only applicable to relational databases, such as - MySQL, PostgreSQL, etc. +> Note: Zawartość tej sekcji odnosi się tylko do relacyjnych baz danych, takich jak MySQL, PostgreSQL, itp. -The relational queries that we have described so far only reference the primary table columns when -querying for the primary data. In reality we often need to reference columns in the related tables. For example, -we may want to bring back the customers who have at least one active order. To solve this problem, we can -build a join query like the following: +Relacyjne kwerendy opisane do tej pory jedynie nawiązują do głównych kolumn tabeli podczas pobierania danych. W rzeczywistości często musimy +odnieść się do kolumn w powiązanych tabelach. Przykładowo chcemy pobrać klientów, którzy złożyli przynajmniej jedno aktywne zamówienie - możemy tego +dokonać za pomocą następującej przyłączającej kwerendy: ```php // SELECT `customer`.* FROM `customer` @@ -995,10 +981,10 @@ $customers = Customer::find() ->all(); ``` -> Note: It is important to disambiguate column names when building relational queries involving JOIN SQL statements. - A common practice is to prefix column names with their corresponding table names. +> Note: Podczas tworzenia relacyjnych kwerend zawierających instrukcję SQL JOIN koniecznym jest ujednoznacznienie nazw kolumn. +> Standardową praktyką w takim wypadku jest poprzedzenie nazwy kolumny odpowiadającą jej nazwą tabeli. -However, a better approach is to exploit the existing relation declarations by calling [[yii\db\ActiveQuery::joinWith()]]: +Jeszcze lepszym rozwiązaniem jest użycie istniejącej deklaracji relacji wywołując metodę [[yii\db\ActiveQuery::joinWith()]]: ```php $customers = Customer::find() @@ -1007,18 +993,17 @@ $customers = Customer::find() ->all(); ``` -Both approaches execute the same set of SQL statements. The latter approach is much cleaner and drier, though. +Oba rozwiązania wykonują te same zestawy instrukcji SQL, ale ostatnie jest o wiele schludniejsze. -By default, [[yii\db\ActiveQuery::joinWith()|joinWith()]] will use `LEFT JOIN` to join the primary table with the -related table. You can specify a different join type (e.g. `RIGHT JOIN`) via its third parameter `$joinType`. If -the join type you want is `INNER JOIN`, you can simply call [[yii\db\ActiveQuery::innerJoinWith()|innerJoinWith()]], instead. +[[yii\db\ActiveQuery::joinWith()|joinWith()]] domyślnie korzysta z `LEFT JOIN` do przyłączenia głównej tabeli z relacyjną. +Możesz określić inny typ przyłączenia (np. `RIGHT JOIN`) podając trzeci parametr `$joinType`. Jeśli chcesz użyć typu przyłączenia `INNER JOIN`, +możesz bezpośrednio wywołać metodę [[yii\db\ActiveQuery::innerJoinWith()|innerJoinWith()]]. -Calling [[yii\db\ActiveQuery::joinWith()|joinWith()]] will [eagerly load](#lazy-eager-loading) the related data by default. -If you do not want to bring in the related data, you can specify its second parameter `$eagerLoading` as false. +Wywołanie [[yii\db\ActiveQuery::joinWith()|joinWith()]] domyślnie [pobierze gorliwie](#lazy-eager-loading) dane relacyjne. +Jeśli nie chcesz pobierać danych w ten sposób, możesz ustawić drugi parametr `$eagerLoading` na false. -Like [[yii\db\ActiveQuery::with()|with()]], you can join with one or multiple relations; you may customize the relation -queries on-the-fly; you may join with nested relations; and you may mix the use of [[yii\db\ActiveQuery::with()|with()]] -and [[yii\db\ActiveQuery::joinWith()|joinWith()]]. For example, +Tak jak w przypadku [[yii\db\ActiveQuery::with()|with()]], możesz przyłączyć jedną lub wiele relacji na raz, dodać do nich dodatkowe warunki, +przyłączyć zagnieżdżone relacje i korzystać z zarówno [[yii\db\ActiveQuery::with()|with()]] jak i [[yii\db\ActiveQuery::joinWith()|joinWith()]]. Przykładowo: ```php $customers = Customer::find()->joinWith([ @@ -1029,8 +1014,8 @@ $customers = Customer::find()->joinWith([ ->all(); ``` -Sometimes when joining two tables, you may need to specify some extra conditions in the `ON` part of the JOIN query. -This can be done by calling the [[yii\db\ActiveQuery::onCondition()]] method like the following: +Czasem, przyłączając dwie tabele, musisz sprecyzować dodatkowe warunki dla części `ON` kwerendy JOIN. +Można to zrobić wywołując metodę [[yii\db\ActiveQuery::onCondition()]] w poniższy sposób: ```php // SELECT `customer`.* FROM `customer` @@ -1044,18 +1029,18 @@ $customers = Customer::find()->joinWith([ ])->all(); ``` -This above query brings back *all* customers, and for each customer it brings back all active orders. -Note that this differs from our earlier example which only brings back customers who have at least one active order. +Powyższa kwerenda pobiera *wszystkich* klientów i dla każdego z nich pobiera wszystkie aktywne zamówienia. +Zwróć uwagę na to, że ten przykład różni się od poprzedniego, gdzie pobierani byli tylko klienci posiadający przynajmniej jedno aktywne zamówienie. -> Info: When [[yii\db\ActiveQuery]] is specified with a condition via [[yii\db\ActiveQuery::onCondition()|onCondition()]], - the condition will be put in the `ON` part if the query involves a JOIN query. If the query does not involve - JOIN, the on-condition will be automatically appended to the `WHERE` part of the query. +> Info: Jeśli [[yii\db\ActiveQuery]] zawiera warunek podany za pomocą [[yii\db\ActiveQuery::onCondition()|onCondition()]], +> będzie on umieszczony w części instrukcji `ON` tylko jeśli kwerenda zawiera JOIN. W przeciwnym wypadku warunek ten będzie automatycznie +> dodany do części `WHERE`. -### Inverse Relations +### Odwrócone relacje -Relation declarations are often reciprocal between two Active Record classes. For example, `Customer` is related -to `Order` via the `orders` relation, and `Order` is related back to `Customer` via the `customer` relation. +Deklaracje relacji są zazwyczaj obustronne dla dwóch klas Active Record. Przykładowo `Customer` jest powiązany z `Order` poprzez relację `orders`, +a `Order` jest powiązany jednocześnie z `Customer` za pomocą relacji `customer`. ```php class Customer extends ActiveRecord @@ -1075,7 +1060,7 @@ class Order extends ActiveRecord } ``` -Now consider the following piece of code: +Rozważmy teraz poniższy kod: ```php // SELECT * FROM `customer` WHERE `id` = 123 @@ -1087,17 +1072,15 @@ $order = $customer->orders[0]; // SELECT * FROM `customer` WHERE `id` = 123 $customer2 = $order->customer; -// displays "not the same" -echo $customer2 === $customer ? 'same' : 'not the same'; +// zwraca "różne" +echo $customer2 === $customer ? 'takie same' : 'różne'; ``` -We would think `$customer` and `$customer2` are the same, but they are not! Actually they do contain the same -customer data, but they are different objects. When accessing `$order->customer`, an extra SQL statement -is executed to populate a new object `$customer2`. +Wydawałoby się, że `$customer` i `$customer2` powinny być identyczne, ale jednak nie są! W rzeczywistości zawierają takie same dane klienta, ale +są różnymi obiektami. Wywołując `$order->customer` wykonywana jest dodatkowa kwerenda SQL do wypełnienia nowego obiektu `$customer2`. -To avoid the redundant execution of the last SQL statement in the above example, we should tell Yii that -`customer` is an *inverse relation* of `orders` by calling the [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] method -like shown below: +Aby uniknąć nadmiarowego wykonywania ostatniej kwerendy SQL w powyższym przykładzie, powinniśmy wskazać Yii, że `customer` jest *odwróconą relacją* +`orders` wywołując metodę [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] jak pokazano to poniżej: ```php class Customer extends ActiveRecord @@ -1109,7 +1092,7 @@ class Customer extends ActiveRecord } ``` -With this modified relation declaration, we will have: +Z tą dodatkową instrukcją w deklaracji relacji uzyskamy: ```php // SELECT * FROM `customer` WHERE `id` = 123 @@ -1118,23 +1101,22 @@ $customer = Customer::findOne(123); // SELECT * FROM `order` WHERE `customer_id` = 123 $order = $customer->orders[0]; -// No SQL will be executed +// kwerenda SQL nie jest wykonywana $customer2 = $order->customer; -// displays "same" -echo $customer2 === $customer ? 'same' : 'not the same'; +// wyświetla "takie same" +echo $customer2 === $customer ? 'takie same' : 'różne'; ``` -> Note: Inverse relations cannot be defined for relations involving a [junction table](#junction-table). - That is, if a relation is defined with [[yii\db\ActiveQuery::via()|via()]] or [[yii\db\ActiveQuery::viaTable()|viaTable()]], - you should not call [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] further. +> Note: Odwrócone relacje nie mogą być definiowane dla relacji zawierających [tabelę węzła](#junction-table), dlatego też definiując relację z użyciem +> [[yii\db\ActiveQuery::via()|via()]] lub [[yii\db\ActiveQuery::viaTable()|viaTable()]] nie powinno się już wywoływać +> [[yii\db\ActiveQuery::inverseOf()|inverseOf()]]. -## Saving Relations +## Zapisywanie relacji -When working with relational data, you often need to establish relationships between different data or destroy -existing relationships. This requires setting proper values for the columns that define the relations. Using Active Record, -you may end up writing the code like the following: +Podczas pracy z danymi relacyjnymi często konieczne jest ustalenie związku pomiędzy różnymi danymi lub też usunięcie istniejącego połączenia. +Takie akcje wymagają ustalenia właściwych wartości dla kolumn definiujących relacje. Korzystając z Active Record można użyć następujących instrukcji: ```php $customer = Customer::findOne(123); @@ -1142,12 +1124,12 @@ $order = new Order(); $order->subtotal = 100; // ... -// setting the attribute that defines the "customer" relation in Order +// ustawianie wartości dla atrybutu definiującego relację "customer" dla Order $order->customer_id = $customer->id; $order->save(); ``` -Active Record provides the [[yii\db\ActiveRecord::link()|link()]] method that allows you to accomplish this task more nicely: +Active Record zawiera metodę [[yii\db\ActiveRecord::link()|link()]], która pozwala na uzyskanie powyższego w efektywniejszy sposób: ```php $customer = Customer::findOne(123); @@ -1158,53 +1140,47 @@ $order->subtotal = 100; $order->link('customer', $customer); ``` -The [[yii\db\ActiveRecord::link()|link()]] method requires you to specify the relation name and the target Active Record -instance that the relationship should be established with. The method will modify the values of the attributes that -link two Active Record instances and save them to the database. In the above example, it will set the `customer_id` -attribute of the `Order` instance to be the value of the `id` attribute of the `Customer` instance and then save it -to the database. +Metoda [[yii\db\ActiveRecord::link()|link()]] wymaga podania konkretnej nazwy relacji i docelowej instancji Active Record, z którą powinna być nawiązany +związek. Mechanizm ten zmodyfikuje wartości atrybutów łączących obie instancje Active Record i zapisze je w bazie danych. W powyższym przykładzie +atrybut `customer_id` instancji `Order` otrzyma wartość atrybutu `id` instancji `Customer`, a następnie zostanie zapisany w bazie danych. -> Note: You cannot link two newly created Active Record instances. +> Note: Nie możesz łączyć w ten sposób dwóch świeżo utworzonych instancji Active Record. -The benefit of using [[yii\db\ActiveRecord::link()|link()]] is even more obvious when a relation is defined via -a [junction table](#junction-table). For example, you may use the following code to link an `Order` instance -with an `Item` instance: +Zaleta używania [[yii\db\ActiveRecord::link()|link()]] jest jeszcze bardziej widoczna, jeśli relacja jest zdefiniowana poprzez +[tabelę węzła](#junction-table). Przykładowo możesz użyć następującego kodu, aby połączyć instancję `Order` z instancją `Item`: ```php $order->link('items', $item); ``` -The above code will automatically insert a row in the `order_item` junction table to relate the order with the item. +Powyższy przykład automatycznie doda nowy wiersz w tabeli węzła `order_item`, aby połączyć zamówienie z produktem. -> Info: The [[yii\db\ActiveRecord::link()|link()]] method will NOT perform any data validation while - saving the affected Active Record instance. It is your responsibility to validate any input data before - calling this method. +> Info: Metoda [[yii\db\ActiveRecord::link()|link()]] NIE wykona automatycznie żadnego procesu walidacji danych podczas zapisywania instancji Active Record. +> Na Tobie spoczywa obowiązek walidacji wszystkich danych przed wywołaniem tej metody. -The opposite operation to [[yii\db\ActiveRecord::link()|link()]] is [[yii\db\ActiveRecord::unlink()|unlink()]] -which breaks an existing relationship between two Active Record instances. For example, +Odwrotną operacją do [[yii\db\ActiveRecord::link()|link()]] jest [[yii\db\ActiveRecord::unlink()|unlink()]], która usuwa istniejący związek pomiędzy +dwoma instancjami Active Record. Przykładowo: ```php $customer = Customer::find()->with('orders')->all(); $customer->unlink('orders', $customer->orders[0]); ``` -By default, the [[yii\db\ActiveRecord::unlink()|unlink()]] method will set the foreign key value(s) that specify -the existing relationship to be null. You may, however, choose to delete the table row that contains the foreign key value -by passing the `$delete` parameter as true to the method. +Domyślnie metoda [[yii\db\ActiveRecord::unlink()|unlink()]] ustawia wartość klucza(y) obcego, który definiuje istniejącą relację, na null. +Można jednak zamiast tego wybrać opcję usuwania wiersza tabeli, który zawiera klucz obcy, ustawiając w metodzie parametr `$delete` na true. -When a junction table is involved in a relation, calling [[yii\db\ActiveRecord::unlink()|unlink()]] will cause -the foreign keys in the junction table to be cleared, or the deletion of the corresponding row in the junction table -if `$delete` is true. +Jeśli w relacji użyty jest węzeł, wywołanie [[yii\db\ActiveRecord::unlink()|unlink()]] spowoduje wyczyszczenie kluczy obcych w tabeli węzła lub też +usunięcie odpowiadających im wierszy, jeśli `$delete` jest ustawione na true. -## Cross-Database Relations +## Relacje międzybazowe -Active Record allows you to declare relations between Active Record classes that are powered by different databases. -The databases can be of different types (e.g. MySQL and PostgreSQL, or MS SQL and MongoDB), and they can run on -different servers. You can use the same syntax to perform relational queries. For example, +Active Record pozwala na deklarowanie relacji pomiędzy klasami Active Record zasilanymi przez różne bazy danych. +Bazy danych mogę być różnych typów (np. MySQL i PostgreSQL lub MS SQL i MongoDB) i mogą pracować na różnych serwerach. +Do wykonania relacyjnych zapytań używa się takich samych procedur, jak w przypadku relacji w obrębie jednej bazy danych. Przykład: ```php -// Customer is associated with the "customer" table in a relational database (e.g. MySQL) +// Customer jest powiązany z tabelą "customer" w relacyjnej bazie danych (np. MySQL) class Customer extends \yii\db\ActiveRecord { public static function tableName() @@ -1214,12 +1190,12 @@ class Customer extends \yii\db\ActiveRecord public function getComments() { - // a customer has many comments + // klient posiada wiele komentarzy return $this->hasMany(Comment::className(), ['customer_id' => 'id']); } } -// Comment is associated with the "comment" collection in a MongoDB database +// Comment jest powiązany z kolekcją "comment" w bazie danych MongoDB class Comment extends \yii\mongodb\ActiveRecord { public static function collectionName() @@ -1229,7 +1205,7 @@ class Comment extends \yii\mongodb\ActiveRecord public function getCustomer() { - // a comment has one customer + // komentarz jest przypisany do jednego klienta return $this->hasOne(Customer::className(), ['id' => 'customer_id']); } } @@ -1237,17 +1213,16 @@ class Comment extends \yii\mongodb\ActiveRecord $customers = Customer::find()->with('comments')->all(); ``` -You can use most of the relational query features that have been described in this section. +Możesz używać większości funkcjonalności dostępnych dla relacyjnych kwerend opisanych w tym rozdziale. -> Note: Usage of [[yii\db\ActiveQuery::joinWith()|joinWith()]] is limited to databases that allow cross-database JOIN queries. - For this reason, you cannot use this method in the above example because MongoDB does not support JOIN. +> Note: Użycie [[yii\db\ActiveQuery::joinWith()|joinWith()]] jest ograniczone do baz danych pozwalających na międzybazowe kwerendy JOIN, dlatego też +> nie możesz użyć tej metody w powyższym przykładzie, ponieważ MongoDB nie wspiera instrukcji JOIN. -## Customizing Query Classes +## Niestandardowe klasy kwerend -By default, all Active Record queries are supported by [[yii\db\ActiveQuery]]. To use a customized query class -in an Active Record class, you should override the [[yii\db\ActiveRecord::find()]] method and return an instance -of your customized query class. For example, +Domyślnie wszystkie kwerendy Active Record używają klasy [[yii\db\ActiveQuery]]. Aby użyć niestandardowej klasy kwerend razem z klasą Active Record, +należy nadpisać metodę [[yii\db\ActiveRecord::find()]], aby zwracała instancję żądanej klasy kwerend. Przykład: ```php namespace app\models; @@ -1269,14 +1244,13 @@ class CommentQuery extends ActiveQuery } ``` -Now whenever you are performing a query (e.g. `find()`, `findOne()`) or defining a relation (e.g. `hasOne()`) -with `Comment`, you will be working with an instance of `CommentQuery` instead of `ActiveQuery`. +Od tego momentu, za każdym razem, gdy wykonywana będzie kwerenda (np. `find()`, `findOne()`) lub pobierana relacja (np. `hasOne()`) klasy `Comment`, +praca będzie odbywać się na instancji `CommentQuery` zamiast `ActiveQuery`. -> Tip: In big projects, it is recommended that you use customized query classes to hold most query-related code - so that the Active Record classes can be kept clean. +> Tip: Dla dużych projektów rekomendowane jest, aby używać własnych, odpowiednio dopasowanych do potrzeb, klas kwerend, dzięki czemu klasy Active Record +> pozostają przejrzyste. -You can customize a query class in many creative ways to improve your query building experience. For example, -you can define new query building methods in a customized query class: +Możesz dopasować klasę kwerend do własnych potrzeb na wiele kreatywnych sposobów. Przykładowo, możesz zdefiniować nowe metody konstruujące zapytanie: ```php class CommentQuery extends ActiveQuery @@ -1288,18 +1262,18 @@ class CommentQuery extends ActiveQuery } ``` -> Note: Instead of calling [[yii\db\ActiveQuery::where()|where()]], you usually should call - [[yii\db\ActiveQuery::andWhere()|andWhere()]] or [[yii\db\ActiveQuery::orWhere()|orWhere()]] to append additional - conditions when defining new query building methods so that any existing conditions are not overwritten. +> Note: Zwykle, zamiast wywoływać metodę [[yii\db\ActiveQuery::where()|where()]], powinno się używać metody +> [[yii\db\ActiveQuery::andWhere()|andWhere()]] lub [[yii\db\ActiveQuery::orWhere()|orWhere()]], aby dołączać kolejne warunki zapytania w +> konstruktorze kwerend, dzięki czemu istniejące warunki nie zostaną nadpisane. -This allows you to write query building code like the following: +Powyższy przykład pozwala na użycie następującego kodu: ```php $comments = Comment::find()->active()->all(); $inactiveComments = Comment::find()->active(false)->all(); ``` -You can also use the new query building methods when defining relations about `Comment` or performing relational query: +Możesz także użyć nowych metod budowania kwerend przy definiowaniu relacji z `Comment` lub wykonywaniu relacyjnych kwerend: ```php class Customer extends \yii\db\ActiveRecord @@ -1312,7 +1286,7 @@ class Customer extends \yii\db\ActiveRecord $customers = Customer::find()->with('activeComments')->all(); -// or alternatively +// lub alternatywnie $customers = Customer::find()->with([ 'comments' => function($q) { @@ -1321,22 +1295,20 @@ $customers = Customer::find()->with([ ])->all(); ``` -> Info: In Yii 1.1, there is a concept called *scope*. Scope is no longer directly supported in Yii 2.0, - and you should use customized query classes and query methods to achieve the same goal. +> Info: W Yii 1.1 do tego celu służy mechanizm *podzbiorów (scope)*, nie jest on jednak bezpośrednio wspierany w Yii 2.0, a zamiast tego +> powinno się używać dopasowanych do własnych potrzeb klas kwerend. -## Selecting extra fields +## Pobieranie dodatkowych pól -When Active Record instance is populated from query results, its attributes are filled up by corresponding column -values from received data set. +W momencie, gdy instancja Active Record pobiera dane z wyniku kwerendy, wartości kolumn przypisywane są do odpowiadających im atrybutów. -You are able to fetch additional columns or values from query and store it inside the Active Record. -For example, assume we have a table named 'room', which contains information about rooms available in the hotel. -Each room stores information about its geometrical size using fields 'length', 'width', 'height'. -Imagine we need to retrieve list of all available rooms with their volume in descendant order. -So you can not calculate volume using PHP, because we need to sort the records by its value, but you also want 'volume' -to be displayed in the list. -To achieve the goal, you need to declare an extra field in your 'Room' Active Record class, which will store 'volume' value: +Możliwe jest pobranie dodatkowych kolumn lub wartości za pomocą kwerendy i przypisanie ich w Active Record. +Przykładowo załóżmy, że mamy tabelę 'room', która zawiera informacje o pokojach dostępnych w hotelu. Każdy pokój przechowuje informacje na temat swojej +wielkości za pomocą pól 'length', 'width' i 'height'. +Teraz wyobraźmy sobie, że potrzebujemy pobrać listę wszystkich pokojów posortowaną po ich kubaturze w malejącej kolejności. +Nie możemy obliczyć kubatury korzystając z PHP, ponieważ zależy nam na szybkim posortowaniu rekordów i dodatkowo chcemy wyświetlić pole 'volume' na liście. +Aby osiągnąć ten cel, musimy zadeklarować dodatkowe pole w klasie 'Room' rozszerzającej Active Record, które przechowa wartość 'volume': ```php class Room extends \yii\db\ActiveRecord @@ -1347,25 +1319,25 @@ class Room extends \yii\db\ActiveRecord } ``` -Then you need to compose a query, which calculates volume of the room and performs the sort: +Następnie należy skonstruować kwerendę, która obliczy kubaturę i wykona sortowanie: ```php $rooms = Room::find() ->select([ - '{{room}}.*', // select all columns - '([[length]] * [[width]].* [[height]]) AS volume', // calculate a volume + '{{room}}.*', // pobierz wszystkie kolumny + '([[length]] * [[width]].* [[height]]) AS volume', // oblicz kubaturę ]) - ->orderBy('volume DESC') // apply sort + ->orderBy('volume DESC') // posortuj ->all(); foreach ($rooms as $room) { - echo $room->volume; // contains value calculated by SQL + echo $room->volume; // zawiera wartość obliczoną przez SQL } ``` -Ability to select extra fields can be exceptionally useful for aggregation queries. -Assume you need to display a list of customers with the count of orders they have made. -First of all, you need to declare a `Customer` class with 'orders' relation and extra field for count storage: +Możliwość pobrania dodatkowych pól jest szczególnie pomocna przy kwerendach agregujących. +Załóżmy, że potrzebujesz wyświetlić listę klientów wraz z liczbą zamówień, których dokonali. +Najpierw musisz zadeklarować klasę `Customer` wraz z relacją 'orders' i dodatkowym polem przechowującym liczbę zamówień: ```php class Customer extends \yii\db\ActiveRecord @@ -1381,15 +1353,15 @@ class Customer extends \yii\db\ActiveRecord } ``` -Then you can compose a query, which joins the orders and calculates their count: +Teraz już możesz skonstruować kwerendę, która przyłączy zamówienia i policzy ich liczbę: ```php $customers = Customer::find() ->select([ - '{{customer}}.*', // select all customer fields - 'COUNT({{order}}.id) AS ordersCount' // calculate orders count + '{{customer}}.*', // pobierz wszystkie kolumny klienta + 'COUNT({{order}}.id) AS ordersCount' // oblicz ilość zamówień ]) - ->joinWith('orders') // ensure table junction - ->groupBy('{{customer}}.id') // group the result to ensure aggregation function works + ->joinWith('orders') // przyłącz tabelę węzła + ->groupBy('{{customer}}.id') // pogrupuj wyniki dla funkcji agregacyjnej ->all(); ``` From b4d7ebdd9148da2a81a86a66a4b5476df501a5bc Mon Sep 17 00:00:00 2001 From: Daniel Filipek Date: Mon, 5 Oct 2015 12:06:19 +0200 Subject: [PATCH 5/8] input-forms 100%, typo fix in README.md --- docs/guide-pl/README.md | 2 +- docs/guide-pl/input-forms.md | 142 +++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 docs/guide-pl/input-forms.md diff --git a/docs/guide-pl/README.md b/docs/guide-pl/README.md index a7c00abd34..4b82690c84 100644 --- a/docs/guide-pl/README.md +++ b/docs/guide-pl/README.md @@ -92,7 +92,7 @@ Odbieranie danych od użytkowników * [Walidacja danych wejściowych](input-validation.md) * [Wysyłanie plików](input-file-upload.md) * [Odczytywanie tablicowych danych wejściowych](input-tabular-input.md) -* [Odbieranie danych z wielu modeli](input-multiple-models.md) +* [Pobieranie danych dla wielu modeli](input-multiple-models.md) Wyświetlanie danych diff --git a/docs/guide-pl/input-forms.md b/docs/guide-pl/input-forms.md new file mode 100644 index 0000000000..1e84c56230 --- /dev/null +++ b/docs/guide-pl/input-forms.md @@ -0,0 +1,142 @@ +Tworzenie formularzy +============== + +Podstawowym sposobem korzystania z formularzy w Yii jest użycie [[yii\widgets\ActiveForm]]. Ten sposób powinien być używany jeśli formularz jest bazowany na modelu. +Dodatkowo, [[yii\helpers\Html]] zawiera sporo użytecznych metod, które zazwyczaj używane są do dodawania przycisków i tekstów pomocniczych do każdego formularza. + +Formularz, który jest wyświetlany po stronie klienta, w większości przypadków, posiada odpowiedni [model](structure-models.md), który jest używany do walidacji danych wejściowych po stronie serwera. +(Sprawdź sekcję [Walidacja danych wejściowych](input-validation.md) aby uzyskać więcej szczegółów). +Podczas tworzenia formularza na podstawie modelu, pierwszym krokiem jest zdefiniowanie samego modelu. +Model może być bazowany na klasie [Active Record](db-active-record.md), reprezentując dane z bazy danych, lub może być też bazowany na klasie generycznej Model ([[yii\base\Model]]) aby przechwytywać dowolne dane wejściowe, np. formularz logowania. +W poniższym przykładzie pokażemy, jak model generyczny model może być użyty do formularza logowania: + +```php + 'login-form', + 'options' => ['class' => 'form-horizontal'], +]) ?> + field($model, 'username') ?> + field($model, 'password')->passwordInput() ?> + +
+
+ 'btn btn-primary']) ?> +
+
+ +``` + +W powyższym kodzie, [[yii\widgets\ActiveForm::begin()|ActiveForm::begin()]] nie tylko tworzy instancję formularza, ale zaznacza też jego początek. +Cała zawartość położona pomiędzy [[yii\widgets\ActiveForm::begin()|ActiveForm::begin()]] i [[yii\widgets\ActiveForm::end()|ActiveForm::end()]] zostanie otoczona tagiem HTML'owym `
`. +Jak w każdym widżecie, możesz sprecyzować kilka opcji jak widżet powinien być skonfigurowany przez przekazanie tablicy do metody `begin`. +W tym przypadku dodatkowa klasa CSS i identyfikator ID zostały przekazane do otwierającego tagu ``. +Aby zobaczyć wszystkie dostępne opcje, zajrzyj do dokumentacji API [[yii\widgets\ActiveForm]]. + +Do utworzenia formularza, wraz z elementami etykiet oraz wszelkimi walidacjami JavaScript, wywoływana jest metoda [[yii\widgets\ActiveForm::field()|ActiveForm::field()]], która zwraca instancję obiektu [[yii\widgets\ActiveField]]. +Kiedy rezultat tej metody jest bezpośrednio wyświetlany, będzie on regularnym polem tekstowym. +Aby dostosować pola, możesz używać dodatkowych metod łączonych [[yii\widgets\ActiveField|ActiveField]]: + +```php +// pole hasła +field($model, 'password')->passwordInput() ?> +// dodanie podpowiedzi oraz zmiana etykiety +field($model, 'username')->textInput()->hint('Please enter your name')->label('Name') ?> +// utworzenie pola email w formacie HTML5 +field($model, 'email')->input('email') ?> +``` + +Powyższy kod utworzy tagi `