Strona główna bloga

Teraz stwórzmy stronę główną pokazującą najnowsze posty.

Zanim zaczniemy, musimy znać przynajmniej podstawy wzorca projektowego Model-Widok-Prezenter (podobnego do MVC:

  • Model – warstwa, która pracuje z danymi. Jest on całkowicie oddzielony od reszty aplikacji. Komunikuje się tylko z prezenterem.
  • Widok – warstwa front-end. Renderuje wymagane dane za pomocą szablonów i wyświetla je użytkownikowi.
  • Prezenter (lub Kontroler) – warstwa łącząca. Prezenter łączy Model i Widok. Przetwarza on żądania, zapytuje Model o dane i zwraca je z powrotem do Widoku.

W przypadku prostych aplikacji, jakimi będzie nasz blog, cała warstwa modelu będzie składać się z samych zapytań do bazy danych – nie potrzebujemy do tego jeszcze żadnego dodatkowego kodu. Na początek więc stworzymy tylko prezentery i szablony. W Nette każdy prezenter ma swoje szablony, więc będziemy je tworzyć jednocześnie.

Tworzenie bazy danych za pomocą Adminera

Do przechowywania danych wykorzystamy bazę MySQL, ponieważ jest ona najczęściej spotykana wśród programistów aplikacji internetowych. Jeśli jednak nie chcesz z niego korzystać, nie krępuj się i wybierz dowolną bazę danych.

Teraz przygotujemy strukturę bazy danych, w której będą przechowywane nasze artykuły na blogu. Zaczniemy bardzo prosto – stworzymy tylko jedną tabelę dla postów.

Aby stworzyć bazę danych, możemy pobrać Adminera, lub inne ulubione narzędzie do zarządzania bazą danych.

Otwórz Adminera i utwórz nową bazę danych o nazwie quickstart.

Utwórz nową tabelę o nazwie posts z następującymi kolumnami:

  • id int, sprawdzenie autoinkrementacji (AI)
  • title varchar, długość 255
  • content tekst
  • created_at znacznik czasu

Powstała struktura powinna wyglądać tak:

CREATE TABLE `posts` (
	`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`title` varchar(255) NOT NULL,
	`content` text NOT NULL,
	`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB CHARSET=utf8;

Naprawdę ważne jest, aby używać InnoDB storage. Za chwilę pokażemy dlaczego. Na razie wystarczy ją zaznaczyć i kliknąć Zapisz.

Zanim stworzymy możliwość dodawania artykułów do bazy danych za pomocą aplikacji, dodajmy ręcznie kilka przykładowych artykułów z bloga.

INSERT INTO `posts` (`id`, `title`, `content`, `created_at`) VALUES
(1,	'Article One',	'Lorem ipusm dolor one',	CURRENT_TIMESTAMP),
(2,	'Article Two',	'Lorem ipsum dolor two',	CURRENT_TIMESTAMP),
(3,	'Article Three',	'Lorem ipsum dolor three',	CURRENT_TIMESTAMP);

Podłączenie do bazy danych

Teraz, gdy baza danych jest utworzona i mamy kilka artykułów w niej zapisanych, to dobry moment, aby wyświetlić je na naszej pięknej, nowej stronie.

Najpierw musimy powiedzieć aplikacji, z jakiej bazy danych ma korzystać. Konfigurujemy połączenie z bazą danych w pliku config/common.neon używając DSN i poświadczeń logowania. Powinno to wyglądać coś takiego:

database:
	dsn: 'mysql:host=127.0.0.1;dbname=quickstart'
	user: *zde vložte jméno uživatele*
	password: *zde vložte heslo k databázi*

Podczas edycji tego pliku zwróć uwagę na wcięcie linii. Format NEON akceptuje zarówno wcięcie spacji, jak i wcięcie tabulatora, ale nie oba jednocześnie. Domyślny plik konfiguracyjny w Web Project wykorzystuje zakładki.

Przekazanie połączenia z bazą danych

Prezenter HomePresenter, który będzie obsługiwał zestawienie artykułów, potrzebuje połączenia z bazą danych. Aby go uzyskać, użyjemy konstruktora, który wygląda tak:

<?php
namespace App\UI\Home;

use Nette;

final class HomePresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	// ...
}

Ładowanie postów z bazy danych

Teraz ładujemy posty z bazy danych i wysyłamy je do szablonu, który następnie renderuje je jako kod HTML. Do tego właśnie służy tzw. metoda render:

public function renderDefault(): void
{
	$this->template->posts = $this->database
		->table('posts')
		->order('created_at DESC')
		->limit(5);
}

Presenter zawiera teraz jedną metodę render renderDefault(), która przekazuje dane z bazy danych do szablonu (View). Szablony znajdują się w app/UI/{PresenterName}/{viewName}.latte, więc w tym przypadku szablon znajduje się w app/UI/Home/default.latte. Szablon będzie miał teraz zmienną $posts, która zawiera posty pobrane z bazy danych.

Szablon

Dla całej strony mamy szablon główny (który nazywa się layout, zawiera nagłówek, style, stopkę,…) oraz specyficzne szablony dla każdego widoku (View) (np. do wyświetlania postów na blogu), które mogą nadpisywać niektóre części szablonu głównego.

Domyślnie szablon układu znajduje się w app/UI/@layout.latte i zawiera:

...
{include content}
...

Wpis {include content} wstawia do głównego szablonu blok o nazwie content. zdefiniujemy to w poszczególnych szablonach widoku (View). W naszym przypadku zmodyfikujemy plik Home/default.latte w następujący sposób:

{block content}
	Hello World
{/block}

W ten sposób zdefiniowaliśmy blok content, który zostanie wstawiony do głównego układu. Jeśli ponownie odświeżymy przeglądarkę, zobaczymy stronę z tekstem “Hello World” (w kodzie źródłowym oraz z nagłówkiem i stopką HTML zdefiniowanymi w @layout.latte).

Wyświetlmy wpisy na blogu – zmodyfikujmy szablon w następujący sposób:

{block content}
	<h1>Můj blog</h1>

	{foreach $posts as $post}
	<div class="post">
		<div class="date">{$post->created_at|date:'F j, Y'}</div>

		<h2>{$post->title}</h2>

		<div>{$post->content|truncate:256}</div>
	</div>
	{/foreach}
{/block}

Jeśli odświeżymy przeglądarkę, zobaczymy zestawienie wszystkich postów. Listing nie jest jeszcze zbyt ładny, nawet nie jest kolorowy, więc możemy dodać kilka stylów CSS do pliku www/css/style.css i połączyć go w układzie:

	...
	<link rel="stylesheet" href="{$basePath}/css/style.css">
</head>
...

Znacznik {foreach} iteruje po wszystkich postach, które przekazaliśmy do szablonu w zmiennej $posts, i dla każdego z nich renderuje dany fragment HTML. Zachowuje się dokładnie jak kod PHP.

Wpis |date: nazywamy filtrem. Filtry mają za zadanie sformatować dane wyjściowe. Ten konkretny filtr konwertuje datę (np. 2013-04-12) na jej bardziej czytelną postać (April 12, 2013). Filtr |truncate obcina łańcuch do maksymalnej podanej długości i dodaje na końcu trójkę, jeśli łańcuch jest obcięty. Ponieważ jest to podgląd, nie ma sensu pokazywać całej treści artykułu. Inne domyślne filtry można znaleźć w dokumentacji lub w razie potrzeby można stworzyć własne.

Jeszcze jedna rzecz. Możemy skrócić i uprościć poprzedni kod. Możemy to zrobić zastępując znaczniki Latte przez n:attributes:

{block content}
	<h1>Můj blog</h1>

	<div n:foreach="$posts as $post" class="post">
		<div class="date">{$post->created_at|date:'F j, Y'}</div>

		<h2>{$post->title}</h2>

		<div>{$post->content|truncate:256}</div>
	</div>
{/block}

Atrybut n:foreach owija div blokiem foreach (działa dokładnie tak samo jak poprzedni kod).

Streszczenie.

Mamy teraz bardzo prostą bazę danych MySQL z kilkoma postami. Aplikacja łączy się z tą bazą danych i wyprowadza prostą listę tych postów do szablonu.