# Praca z bazami danych ¶

Ta sekcja opisuje jak utworzyć nową stronę, która będzie wyświetlała dane krajów pobrane z tabeli bazy danych o nazwie `country`. Aby to osiągnąć, musisz skonfigurować swoje połączenie z bazą danych, utworzyć klasę Active Record, zdefiniować akcję oraz utworzyć widok.

W tej sekcji nauczysz się:

- konfigurowania połączenia z bazą danych,
- tworzenia klasy Active Record,
- tworzenia zapytań o dane przy użyciu klasy Active Record,
- wyświetlania danych w widoku wraz ze stronicowaniem.

Zauważ, że w celu przejścia tej sekcji należy mieć już podstawową wiedzę o bazach danych. W szczególności powinieneś wiedzieć, jak utworzyć bazę dancyh oraz jak wywołać komendę SQL używając klienta bazy danych.

## Przygotowanie bazy danych ¶

Aby rozpocząć, musisz utworzyć bazę danych o nazwie `yii2basic`, z której będziesz pobierał dane do swojej aplikacji. Możesz utworzyć bazę SQLite, MySQL, PostgreSQL, MSSQL lub Oracle, ponieważ Yii posiada wbudowane wsparcie dla wielu aplikacji bazodanowych. Dla uproszczenia w naszym przykładzie wykorzystamy MySQL.

Następnie, utwórz tabelę o nazwie `country` i wstaw przykładowe dane. Możesz użyc poniższej komendy:

    CREATE TABLE `country` (
      `code` CHAR(2) NOT NULL PRIMARY KEY,
      `name` CHAR(52) NOT NULL,
      `population` INT(11) NOT NULL DEFAULT '0'
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    INSERT INTO `country` VALUES ('AU','Australia',24016400);
    INSERT INTO `country` VALUES ('BR','Brazil',205722000);
    INSERT INTO `country` VALUES ('CA','Canada',35985751);
    INSERT INTO `country` VALUES ('CN','China',1375210000);
    INSERT INTO `country` VALUES ('DE','Germany',81459000);
    INSERT INTO `country` VALUES ('FR','France',64513242);
    INSERT INTO `country` VALUES ('GB','United Kingdom',65097000);
    INSERT INTO `country` VALUES ('IN','India',1285400000);
    INSERT INTO `country` VALUES ('RU','Russia',146519759);
    INSERT INTO `country` VALUES ('US','United States',322976000);

W tym miejscu masz już utworzoną bazę danych o nazwie `yii2basic`, posiadającą tabelę `country` z trzema kolumnami. Tabela zawiera 10 wierszy danych.

## Konfiguracja połączenia z bazą danych ¶

Przed przystąpieniem do tej części, upewnij się, że masz zainstalowane rozszerzenie PDO oraz sterownik PDO dla bazy danych której używasz (np. `pdo_mysql` dla MySQL). Jest to podstawowe wymaganie, jeśli Twoja aplikacja używa relacyjnej bazy danych.

Jeśli posiadasz zainstalowane powyższe rozszerzenia, otwórz plik `config/db.php` i zmień parametry na odpowiednie do Twojej bazy danych. Domyślnie plik zawiera poniższy fragment:

    <?php

    return [
        'class' => 'yii\db\Connection',
        'dsn' => 'mysql:host=localhost;dbname=yii2basic',
        'username' => 'root',
        'password' => '',
        'charset' => 'utf8',
    ];

Plik `config/db.php` jest typowym narzędziem konfiguracyjnym opartym na plikach. Ten szczególny plik konfiguracyjny określa parametry potrzebne do utworzenia oraz zainicjalizowania instancji Connection, dzięki czemu będziesz mógł wywoływać komendy SQL do swojej bazy przez aplikację.

Powyższa konfiguracja może być dostępna z poziomu kodu aplikacji używając wyrażenia `Yii::$app->db`.

> Informacja: Plik `config/db.php` będzie załączony do głównej konfiguracji aplikacji `config/web.php`, która określa jak instancja aplikacji powinna zostać zainicjalizowana. Po więcej informacji zajrzyj do sekcji Konfiguracje.

## Tworzenie klasy Active Record ¶

Do pobrania i reprezentowania danych z tabeli `country` utwórz pochodną klasę Active Record o nazwie `Country`, kolejnie zapisz ją w pliku `models/Country.php`.

    <?php

    namespace app\models;

    use yii\db\ActiveRecord;

    class Country extends ActiveRecord
    {
    }

Klasa `Country` rozszerza klasę ActiveRecord. Nie musisz pisać w niej żadnego kodu! Posiadając tylko powyżej podany kod, Yii odgadnie nazwę powiązanej tabeli z nazwy klasy.

> Informacja: Jeśli nie można dopasować tabeli do nazwy klasy, możesz nadpisać metodę tableName(), aby wskazywała na konkretną powiązaną tabelę.

Używając klasy `Country` możesz w łatwy sposób manipulować danymi z tabeli `country`, tak jak pokazano w poniższych przykładach:

    use app\models\Country;

    // pobiera wszystkie wiersze tabeli `country` i porządkuje je według kolumny "name"
    $countries = Country::find()->orderBy('name')->all();

    // pobiera wiersz, którego kluczem głównym jest "US"
    $country = Country::findOne('US');

    // wyświetla "United States"
    echo $country->name;

    // modyfikuje nazwę kraju na "U.S.A." i zapisuje go do bazy danych
    $country->name = 'U.S.A.';
    $country->save();

> Informacja: Active Record jest potężnym narzędziem do dostępu i manipulacji danymi w bazie danych w sposób zorientowany obiektowo. Więcej szczegółowych informacji znajdziesz w sekcji Active Record. Alternatywnie, do łączenia się z bazą danych możesz użyć niskopoziomowej metody dostępu do danych nazwanej Data Access Objects.

## Tworzenie akcji ¶

Aby przedstawić kraje użytkownikowi musisz utworzyć nową akcję. Zamiast umieszczać nową akcję w kontrolerze `site`, tak jak w poprzednich sekcjach, bardziej sensownym rozwiązaniem jest utworzenie nowego kontrolera odpowiedzialnego za wszystkie akcje dotyczące danych z tabeli `country`. Nazwij nowy kontroler `CountryController`, a następnie utwórz w nim akcję `index`, tak jak na poniższym przykładzie:

    <?php

    namespace app\controllers;

    use yii\web\Controller;
    use yii\data\Pagination;
    use app\models\Country;

    class CountryController extends Controller
    {
        public function actionIndex()
        {
            $query = Country::find();

            $pagination = new Pagination([
                'defaultPageSize' => 5,
                'totalCount' => $query->count(),
            ]);

            $countries = $query->orderBy('name')
                ->offset($pagination->offset)
                ->limit($pagination->limit)
                ->all();

            return $this->render('index', [
                'countries' => $countries,
                'pagination' => $pagination,
            ]);
        }
    }

Zapisz powyższy kod w pliku `controllers/CountryController.php`.

Akcja `index` wywołuje metodę `Country::find()`, pochodzącą z klasy Active Record, która buduje zapytanie bazodanowe i wyszukuje wszystkich danych z tabeli `country`. Aby ograniczyć liczbę zwracanych krajów w każdym żądaniu, zapytanie jest stronicowane przy pomocy obiektu Pagination. Obiekt `Pagination` służy dwóm celom:

- Ustawia klauzule `offset` i `limit` do komend SQL reprezentowanych przez zapytanie tak, aby zwracały tylko jedną stronę na raz (maksymalnie 5 wierszy na stronę),
- Jest używany w widoku do wyświetlania stronicowania jako listy przycisków z numerami stron, co będzie wyjaśnione w kolejnej sekcji.

Na końcu akcja `index` renderuje widok o nazwie `index`, do którego przekazuje dane krajów oraz informacje o ich stronicowaniu.

## Tworzenie widoku ¶

W katalogu `views` utwórz nowy katalog o nazwie `country`. Będzie on używany do przechowywania wszystkich plików widoków renderowanych przez kontroler `country`. W katalogu `views/country` utwórz plik o nazwie `index.php` zawierający poniższy kod:

    <?php
    use yii\helpers\Html;
    use yii\widgets\LinkPager;
    ?>
    <h1>Kraje</h1>
    <ul>
    <?php foreach ($countries as $country): ?>
        <li>
            <?= Html::encode("{$country->name} ({$country->code})") ?>:
            <?= $country->population ?>
        </li>
    <?php endforeach; ?>
    </ul>

    <?= LinkPager::widget(['pagination' => $pagination]) ?>

Widok posiada dwie części. Pierwsza część odpowiada za wyświetlenie danych krajów jako nieuporządkowana lista HTML, natomiast druga część renderuje widżet LinkPager na podstawie dostarczonych mu informacji z akcji. Widżet `LinkPager` wyświetla listę przycisków z numerami stron. Kliknięcie w którykolwiek z nich zmienia dane z listy na dane odpowiadające wybranej stronie.

## Sprawdź jak to działa ¶

Aby zobaczyć jak działa powyższy kod, użyj przeglądarki i przejdź pod podany adres URL:

    https://hostname/index.php?r=country%2Findex

Na początku zobaczysz stronę pokazującą pięć krajów. Poniżej listy znajduje się paginacja z czterema przyciskami. Jeśli klikniesz przycisk "2", zobaczysz stronę wyświetlającą pięć innych krajów z bazy danych: druga strona wierszy. Zauważ, że adres URL w przeglądarce również się zmienił na

    https://hostname/index.php?r=country%2Findex&page=2

Za kulisami, Pagination dostarcza wszystkich niezbędnych funkcjonalności do stronicowania zbioru danych:

- Początkowo Pagination prezentuje pierwszą stronę, która odzwierciedla zapytanie "SELECT" tabeli `country` z klauzulą `LIMIT 5 OFFSET 0`. W rezultacie pobieranych i wyświetlanych jest pięć pierwszych krajów.
- Widżet LinkPager renderuje przyciski stron używając adresów URL tworzonych przez metodę createUrl(). Adresy zawierają parametr zapytania `page`, który reprezentuje różne numery stron.
- Jeśli klikniesz przycisk "2", zostanie uruchomione i przetworzone nowe żądanie dla route'a `country/index`. Pagination odczytuje parametr `query` z adresu URL, a następnie ustawia aktualny numer strony na 2. Nowe zapytanie o kraje będzie zawierało klauzulę `LIMIT 5 OFFSET 5` i zwróci pięć kolejnych krajów do wyświetlenia.

## Podsumowanie ¶

W tej sekcji nauczyłeś się jak pracować z bazą danych. Nauczyłeś się również jak pobierać i wyświetlać dane ze stronicowaniem przy pomocy Pagination oraz LinkPager.

W następnej sekcji nauczysz się jak używać potężnego narzędzie do generowania kodu nazwanego Gii, aby pomóc Ci w szybki sposób implementować niektóre powszechnie wymagane funkcjonalności, takie jak operacje CRUD dla zadań z danymi w bazie danych. Kod, który właśnie napisaliśmy, może być w całości automatycznie wygenerowany w Yii przy użyciu narzędzia Gii.