Главная страница блога

Теперь мы создадим главную страницу, отображающую последние посты.

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

  • Модель (Model) – слой, работающий с данными. Полностью отделен от остальной части приложения. Общается только с презентером.
  • Представление (View) – фронтенд-слой. Отображает запрашиваемые данные с помощью шаблонов и показывает их пользователю.
  • Презентер (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 использует табы.

Передача подключения к базе данных

Презентер 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);
}

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

Шаблон

Для всего веб-сайта у нас есть главный шаблон (который называется layout, содержит шапку, стили, подвал,…) и далее конкретные шаблоны для каждого представления (View) (например, для отображения постов в блоге), которые могут переопределить некоторые части главного шаблона.

По умолчанию шаблон layout находится в 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 с несколькими постами. Приложение подключается к этой базе данных и выводит простой список этих постов в шаблон.