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

Создадим главную страницу, показывающую ваши последние записи.

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

  • Model – слой, работающий с данными. Он полностью отделён от остального приложения. Общается он только с презентером.
  • View – слой отображения. Он отрисовывает запрошенные данные с помощью шаблонов и показывает их пользователю.
  • Presenter (или контроллер) – соединяющий слой. Презентер соединяет Model и View. Он обрабатывает запросы, просит у Model данные и затем передаёт их во View.

В случае простого приложения вроде нашего блога весь слой модели будет состоять только из запросов к базе данных – никакого дополнительного PHP-кода для этого нам пока не нужно. Так что для начала мы создадим только презентеры и шаблоны. В Nette у каждого презентера свои шаблоны, так что создавать их будем одновременно.

Создание базы данных с помощью Adminer

Для хранения данных мы воспользуемся базой данных MySQL, потому что это самый частый выбор среди веб-разработчиков. Однако если вы не хотите её использовать, спокойно выберите базу данных по своему вкусу.

Подготовим структуру базы данных, в которой будут храниться записи нашего блога. Начать можно совсем просто, всего с одной таблицы для записей.

Чтобы создать базу данных, мы можем скачать Adminer либо использовать другой инструмент управления базами данных, который вам больше нравится.

Откроем Adminer и создадим новую базу данных под названием quickstart.

Создайте новую таблицу с именем posts и добавьте эти столбцы:

  • id int, отметьте autoincrement (AI)
  • title varchar, длина 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 ipsum 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(), который передаёт данные из базы данных в шаблон (View). Шаблоны находятся в app/Presentation/{ИмяПрезентера}/{имяПредставления}.latte, так что в данном случае шаблон находится в app/Presentation/Home/default.latte. В шаблоне теперь доступна переменная с именем $posts, содержащая записи, полученные из базы данных.

Шаблон

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

По умолчанию шаблон макета находится в app/Presentation/@layout.latte и содержит:

...
{include content}
...

Тег {include content} вставляет в главный шаблон блок с именем content. Этот блок мы определим в шаблонах отдельных представлений. В нашем случае мы изменим файл 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}

Если вы обновите браузер, вы увидите список записей своего блога. Список пока не слишком нарядный и цветной, так что смело добавьте красивые стили CSS в файл www/css/style.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 с несколькими записями. Приложение подключается к этой базе данных и выводит в шаблоне простой список этих записей.