Начална страница на блога

Сега ще създадем начална страница, показваща последните публикации.

Преди да започнем, е необходимо да познавате поне основите на дизайнерския модел Model-View-Presenter (подобен на MVC):

  • Модел – слой, работещ с данни. Той е напълно отделен от останалата част на приложението. Комуникира само с презентера.
  • Изглед – фронт-енд слой. Рендира изискваните данни с помощта на шаблони и ги показва на потребителя.
  • Presenter (или Контролер) – свързващ слой. Presenter свързва Модела и Изгледа. Обработва заявките, изисква данни от Модела и ги връща обратно на Изгледа.

В случай на прости приложения, като нашия блог, целият моделен слой ще се състои само от заявки към базата данни – засега не се нуждаем от допълнителен код за това. За начало ще създадем само презентери и шаблони. В Nette всеки презентер има свои собствени шаблони, така че ще ги създаваме едновременно.

Създаване на база данни с помощта на Adminer

За съхраняване на данни ще използваме MySQL база данни, тъй като тя е най-разпространената сред програмистите на уеб приложения. Ако обаче не искате да я използвате, спокойно изберете база данни по ваш избор.

Сега ще подготвим структурата на базата данни, където ще се съхраняват статиите на нашия блог. Ще започнем много просто – ще създадем само една таблица за публикациите.

За създаване на базата данни можем да изтеглим Adminer или друг ваш любим инструмент за управление на бази данни.

Отваряме Adminer и създаваме нова база данни с име quickstart.

Създаваме нова таблица с име posts и със следните колони:

  • id int, отметнете autoincrement (AI)
  • title varchar, length 255
  • content text
  • created_at timestamp

Получената структура трябва да изглежда така:

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;

Наистина е важно да използвате хранилище InnoDB. След малко ще покажем защо. Засега просто го изберете и кликнете върху запазване.

Преди да създадем възможност за добавяне на статии в базата данни с помощта на приложението, добавете няколко примерни статии в блога ръчно.

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);

Свързване с базата данни

Сега, когато базата данни вече е създадена и имаме запазени няколко статии в нея, е време да ги покажем на нашата красива нова страница.

Първо трябва да кажем на приложението коя база данни да използва. Връзката с базата данни се настройва във файла config/common.neon с помощта на DSN и данните за вход. Трябва да изглежда по следния начин:

database:
	dsn: 'mysql:host=127.0.0.1;dbname=quickstart'
	user: *тук въведете потребителско име*
	password: *тук въведете парола за базата данни*

При редактиране на този файл внимавайте с индентацията на редовете. Форматът NEON приема както индентация с интервали, така и индентация с табулации, но не и двете едновременно. Конфигурационният файл по подразбиране в Web Project използва табулации.

Предаване на връзката с базата данни

Presenter HomePresenter, който ще се грижи за извеждането на статиите, се нуждае от връзка с базата данни. За да я получим, ще използваме конструктор, който ще изглежда така:

<?php
namespace App\Presentation\Home;

use Nette;

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

	// ...
}

Зареждане на публикации от базата данни

Сега ще заредим публикациите от базата данни и ще ги изпратим към шаблона, който след това ще ги рендира като HTML код. За това е предназначен така нареченият render метод:

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

Presenter сега съдържа един рендиращ метод renderDefault(), който предава данни от базата данни към шаблона (Изглед). Шаблоните са разположени в app/Presentation/{PresenterName}/{viewName}.latte, така че в този случай шаблонът е разположен в app/Presentation/Home/default.latte. В шаблона сега ще бъде налична променлива $posts, в която са публикациите, получени от базата данни.

Шаблон

За целия уеб сайт имаме на разположение главен шаблон (който се нарича лейаут, съдържа хедър, стилове, футър,…) и след това конкретни шаблони за всеки изглед (View) (например за показване на публикации в блога), които могат да презапишат някои части от главния шаблон.

По подразбиране лейаут шаблонът е разположен в app/Presentation/@layout.latte и съдържа:

...
{include content}
...

Записът {include content} вмъква в главния шаблон блок с име content. Него ще дефинираме в шаблоните на отделните изгледи (View). В нашия случай файлът Home/default.latte ще променим по следния начин:

{block content}
	Hello World
{/block}

С това дефинирахме блок content, който ще бъде вмъкнат в главния лейаут. Ако отново обновим браузъра, ще видим страница с текст “Hello World” (в изходния код също с HTML хедър и футър, дефинирани в @layout.latte).

Нека покажем публикациите от блога – ще променим шаблона по следния начин:

{block content}
	<h1>Моят блог</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}

Ако обновим браузъра, ще видим списък с всички публикации. Списъкът засега не е много хубав, нито цветен, затова можем да добавим към файла www/css/style.css няколко CSS стила и да го свържем в лейаута:

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

Тагът {foreach} итерира през всички публикации, които сме предали на шаблона в променливата $posts, и за всяка рендира дадения HTML код. Държи се точно като PHP код.

На записа |date: казваме филтър. Филтрите са предназначени за форматиране на изхода. Този конкретен филтър преобразува дата (напр. 2013-04-12) в нейната по-четима форма (April 12, 2013). Филтърът |truncate отрязва низ до посочената максимална дължина и в случай, че низът бъде скъсен, добавя накрая три точки. Тъй като това е преглед, няма смисъл да се показва цялото съдържание на статията. Други филтри по подразбиране можем да намерим в документацията или можем да създадем собствени, когато е необходимо.

Още нещо. Предишният код можем да съкратим и опростим. Това ще постигнем чрез замяна на Latte тагове с n:атрибути:

{block content}
	<h1>Моят блог</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}

Атрибутът n:foreach обвива div блока с foreach (работи абсолютно същото като предишния код).

Резюме

Сега имаме много проста MySQL база данни с няколко публикации. Приложението се свързва с тази база данни и извежда прост списък с тези публикации в шаблона.