Strona główna bloga

Teraz stworzymy stronę główną wyświetlającą najnowsze posty.

Zanim zaczniemy, trzeba znać przynajmniej podstawy wzorca projektowego Model-View-Presenter (podobnego do MVC):

  • Model – warstwa pracująca z danymi. Jest całkowicie oddzielona od reszty aplikacji. Komunikuje się tylko z presenterem.
  • View – warstwa front-endowa. Renderuje żądane dane za pomocą szablonów i wyświetla je użytkownikowi.
  • Presenter (lub Controller) – warstwa łącząca. Presenter łączy Model i View. Przetwarza żądania, pyta Model o dane i zwraca je z powrotem do View.

W przypadku prostych aplikacji, takich jak nasz blog, całą warstwę modelową będą stanowić tylko zapytania do bazy danych – na to na razie nie potrzebujemy żadnego dodatkowego kodu. Na początek stworzymy więc tylko presentery i szablony. W Nette każdy presenter ma swoje własne szablony, więc będziemy je tworzyć jednocześnie.

Tworzenie bazy danych za pomocą Adminera

Do przechowywania danych użyjemy bazy danych MySQL, ponieważ jest najbardziej rozpowszechniona wśród programistów aplikacji internetowych. Jeśli jednak nie chcesz jej używać, śmiało wybierz bazę danych według własnego uznania.

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

Do stworzenia bazy danych możemy pobrać Adminer lub inne ulubione narzędzie do zarządzania bazami danych.

Otworzymy Adminera i stworzymy nową bazę danych o nazwie quickstart.

Stworzymy nową tabelę o nazwie posts z następującymi kolumnami:

  • id int, zaznaczymy autoincrement (AI)
  • title varchar, length 255
  • content text
  • created_at timestamp

Wynikowa 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 użycie silnika InnoDB. Za chwilę pokażemy dlaczego. Na razie po prostu go wybierz i kliknij zapisz.

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

INSERT INTO `posts` (`id`, `title`, `content`, `created_at`) VALUES
(1,	'Artykuł Pierwszy',	'Lorem ipusm dolor jeden',	CURRENT_TIMESTAMP),
(2,	'Artykuł Drugi',	'Lorem ipsum dolor dwa',	CURRENT_TIMESTAMP),
(3,	'Artykuł Trzeci',	'Lorem ipsum dolor trzy',	CURRENT_TIMESTAMP);

Połączenie z bazą danych

Teraz, gdy baza danych jest już stworzona i mamy w niej zapisanych kilka artykułów, nadszedł właściwy czas, aby wyświetlić je na naszej pięknej nowej stronie.

Najpierw musimy powiedzieć aplikacji, jakiej bazy danych ma używać. Połączenie z bazą danych ustawimy w pliku config/common.neon za pomocą DSN i danych logowania. Powinno to wyglądać mniej więcej tak:

database:
	dsn: 'mysql:host=127.0.0.1;dbname=quickstart'
	user: *tutaj wstaw nazwę użytkownika*
	password: *tutaj wstaw hasło do bazy danych*

Podczas edycji tego pliku zwracaj uwagę na wcięcia linii. Format NEON akceptuje zarówno wcięcia za pomocą spacji, jak i wcięcia za pomocą tabulatorów, ale nie oba jednocześnie. Domyślny plik konfiguracyjny w Web Project używa tabulatorów.

Przekazanie połączenia z bazą danych

Presenter HomePresenter, który będzie zajmował się wyświetlaniem artykułów, potrzebuje połączenia z bazą danych. Aby je uzyskać, wykorzystamy konstruktor, który będzie wyglądał tak:

<?php
namespace App\Presentation\Home;

use Nette;

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

	// ...
}

Wczytywanie postów z bazy danych

Teraz wczytamy posty z bazy danych i przekażemy je do szablonu, który następnie wyrenderuje je jako kod HTML. Do tego służy tak zwana metoda render:

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

Presenter teraz zawiera jedną metodę renderującą renderDefault(), która przekazuje dane z bazy danych do szablonu (View). Szablony są umieszczone w app/Presentation/{PresenterName}/{viewName}.latte, więc w tym przypadku szablon znajduje się w app/Presentation/Home/default.latte. W szablonie teraz będzie dostępna zmienna $posts, w której znajdują się posty pobrane z bazy danych.

Szablon

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

Domyślnie szablon layoutu znajduje się w app/Presentation/@layout.latte i zawiera:

...
{include content}
...

Zapis {include content} wstawia do głównego szablonu blok o nazwie content. Będziemy go definiować w szablonach poszczególnych widoków (View). W naszym przypadku plik Home/default.latte zmodyfikujemy następująco:

{block content}
	Witaj Świecie
{/block}

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

Wyświetlmy posty z bloga – szablon zmodyfikujemy następująco:

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

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

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

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

Jeśli odświeżymy przeglądarkę, zobaczymy listę wszystkich postów. Lista na razie nie jest zbyt ładna ani kolorowa, dlatego możemy do pliku www/css/style.css dodać kilka stylów CSS i podlinkować go w layoucie:

	...
	<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 renderuje dany fragment HTML. Zachowuje się dokładnie jak kod PHP.

Zapisowi |date: mówimy filtr. Filtry służą do formatowania wyjścia. Ten konkretny filtr konwertuje datę (np. 2013-04-12) na jej bardziej czytelną postać (12 kwietnia 2013). Filtr |truncate przycina ciąg znaków do podanej maksymalnej długości, a w przypadku skrócenia ciągu dodaje na końcu wielokropek. Ponieważ jest to podgląd, nie ma sensu wyświetlać całej treści artykułu. Inne domyślne filtry znajdziemy w dokumentacji, a także możemy tworzyć własne, gdy jest to potrzebne.

Jeszcze jedna rzecz. Poprzedni kod możemy skrócić i uprościć. Osiągniemy to, zamieniając tagi Latte na n:atrybuty:

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

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

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

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

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

Podsumowanie

Teraz mamy bardzo prostą bazę danych MySQL z kilkoma postami. Aplikacja łączy się z tą bazą danych i wyświetla prostą listę tych postów w szablonie.