Strona główna bloga

Stwórzmy stronę główną wyświetlającą Twoje najnowsze wpisy.

Zanim zaczniemy, powinieneś 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 (albo kontroler) – warstwa łącząca. Presenter łączy Model i View. Obsługuje żądania, prosi Model o dane, a potem przekazuje je do View.

W przypadku prostej aplikacji jak nasz blog cała warstwa modelu będzie składać się wyłącznie z zapytań do bazy danych, nie potrzebujemy do tego jeszcze żadnego dodatkowego kodu PHP. Na początek utworzymy więc tylko presentery i szablony. W Nette każdy presenter ma własne szablony, więc utworzymy je jednocześnie.

Tworzenie bazy danych w Adminerze

Do przechowywania danych użyjemy bazy MySQL, bo to najczęstszy wybór wśród webdeveloperów. Jeśli jednak nie chcesz jej używać, śmiało wybierz bazę danych według własnego uznania.

Przygotujmy strukturę bazy danych, która będzie przechowywać nasze wpisy blogowe. Możemy zacząć bardzo prosto, tylko od jednej tabeli na wpisy.

Do utworzenia bazy danych możemy pobrać Adminer albo użyć innego narzędzia do zarządzania bazami danych, które wolisz.

Otwórzmy Adminer i utwórzmy nową bazę danych o nazwie quickstart.

Utwórz nową tabelę o nazwie posts i dodaj te kolumny:

  • id int, zaznacz autoincrement (AI)
  • title varchar, długość 255
  • content text
  • created_at timestamp

Powinno to 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;

Bardzo ważne jest użycie silnika składowania InnoDB. Powód zobaczysz później. Na razie po prostu go wybierz i kliknij Zapisz.

Spróbuj dodać ręcznie kilka przykładowych wpisów blogowych, zanim zrealizujemy dodawanie nowych wpisów bezpośrednio z naszej aplikacji.

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

Połączenie z bazą danych

Teraz, gdy baza danych jest utworzona i mamy w niej kilka wpisów, jest właściwy moment, żeby wyświetlić je na naszej zupełnie nowej stronie.

Najpierw musimy powiedzieć naszej aplikacji, której bazy danych ma używać. Konfigurację połączenia z bazą ustawia się w pliku config/common.neon za pomocą DSN i danych uwierzytelniających. Powinno to wyglądać mniej więcej tak:

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

Uważaj przy edycji tego pliku na wcięcia. Format NEON akceptuje zarówno spacje, jak i tabulatory, ale nie oba naraz. Domyślny plik konfiguracyjny w Web Projekcie używa tabulatorów.

Wstrzyknięcie połączenia z bazą danych

Presenter HomePresenter, który będzie wypisywać wpisy, potrzebuje połączenia z bazą danych. Żeby je otrzymać, użyjemy konstruktora w ten sposób:

<?php
namespace App\Presentation\Home;

use Nette;

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

	// ...
}

Wczytanie wpisów z bazy danych

Pobierzmy teraz wpisy z bazy danych i przekażmy je do szablonu, który wyrenderuje potem 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 ma teraz jedną metodę render renderDefault(), która przekazuje dane z bazy do szablonu (View). Szablony znajdują się w app/Presentation/{NazwaPresentera}/{nazwaWidoku}.latte, więc w tym przypadku szablon leży w app/Presentation/Home/default.latte. W szablonie dostępna jest teraz zmienna o nazwie $posts zawierająca wpisy pobrane z bazy danych.

Szablon

Dla całej witryny mamy główny szablon (zwany layoutem, zawierający nagłówek, style, stopkę, …) oraz konkretne szablony dla każdego widoku (np. do wyświetlania wpisów blogowych), które mogą nadpisywać części szablonu głównego.

Domyślnie szablon layoutu leży w app/Presentation/@layout.latte i zawiera:

...
{include content}
...

Tag {include content} wstawia do szablonu głównego blok o nazwie content. Blok ten zdefiniujemy w szablonach poszczególnych widoków. W naszym przypadku zmodyfikujemy plik Home/default.latte następująco:

{block content}
	Hello World
{/block}

Definiuje to blok content, który zostanie wstawiony do głównego layoutu. Jeśli znowu odświeżysz przeglądarkę, zobaczysz stronę z tekstem “Hello World” (w kodzie źródłowym także z nagłówkiem i stopką HTML zdefiniowanymi w @layout.latte).

Wyświetlmy wpisy blogowe, zmodyfikujemy szablon następująco:

{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żysz przeglądarkę, zobaczysz listę swoich wpisów blogowych. Lista nie jest jeszcze zbyt efektowna ani kolorowa, więc śmiało dodaj do pliku www/css/style.css ładne style CSS i podepnij go w layoucie:

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

Tag {foreach} przechodzi wszystkie wpisy przekazane do szablonu w zmiennej $posts i wyświetla dla każdego wpisu kawałek kodu HTML. Zachowuje się dokładnie jak kod PHP.

Zapis |date: nazywa się filtrem. Filtry służą do formatowania wyjścia. Ten konkretny filtr konwertuje datę (np. 2013-04-12) na jej czytelniejszą postać (April 12, 2013). Filtr |truncate przycina ciąg do podanej maksymalnej długości i dodaje na końcu wielokropek (…), jeśli ciąg został przycięty. Ponieważ to podgląd, nie ma sensu wyświetlać pełnej treści wpisu. Pozostałe domyślne filtry znajdziesz w dokumentacji albo możesz w razie potrzeby utworzyć własne.

Jeszcze jedno. Poprzedni kod możemy skrócić i uprościć. Osiągniemy to, zastępując tagi Latte n:atrybutami:

{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 opakowuje blok div pętlą foreach (działa dokładnie tak samo jak poprzedni kod).

Podsumowanie

Mamy teraz bardzo prostą bazę MySQL z kilkoma wpisami. Aplikacja łączy się z tą bazą i wyświetla w szablonie prostą listę tych wpisów.