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

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

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

  • Модель — уровень манипулирования данными. Он полностью отделен от остальной части приложения и общается только с презентерами.
  • Вид (или _Представление_) — внешний уровень определения. Он отображает запрашиваемые данные пользователю с помощью шаблонов.
  • Презентер (или _Контроллер_) — уровень соединения. Презентер соединяет модель и вид. Обрабатывает запросы, запрашивает данные у модели и затем передает их текущему представлению.

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

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

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

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

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

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

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

  • id int, нажмите на автоинкремент (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. Причину вы увидите позже. Пока что просто создайте всё по инструкции и нажмите кнопку Сохранить. Либо используйте полный код создания таблицы и кнопку SQL-запрос в Adminer.

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

INSERT INTO `posts` (`id`, `title`, `content`, `created_at`) VALUES
(1,	'Статья первая',	'Lorem ipusm dolor one',	CURRENT_TIMESTAMP),
(2,	'Статья вторая',	'Lorem ipsum dolor two',	CURRENT_TIMESTAMP),
(3,	'Статья третья',	'Lorem ipsum dolor three',	CURRENT_TIMESTAMP);

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

Теперь, когда база данных создана и в ней есть несколько постов, самое время отобразить их на нашей новой блестящей странице.

Во-первых, нам нужно сообщить нашему приложению, какую базу данных использовать. Конфигурация подключения к базе данных хранится в файле config/local.neon. Установите соединение DSN и свои учётные данные. Это должно выглядеть следующим образом:

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

Помните об отступах при редактировании этого файла. Формат NEON принимает и пробелы, и табуляцию, но не то и другое вместе! В файле конфигурации в веб-проекте по умолчанию используется табуляция.

Вся конфигурация хранится в config/ в файлах common.neon и local.neon. Файл common.neon содержит глобальную конфигурацию приложения, а local.neon содержит только параметры, специфичные для среды (например, разница между сервером разработки и рабочим сервером).

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

Презентер (расположенный в app/Presenters/HomepagePresenter.php), который будет перечислять статьи, нуждается в подключении к базе данных. Для этого измените конструктор следующим образом:

<?php
namespace App\Presenters;

use Nette;
use Nette\Application\UI\Form;

final class HomepagePresenter 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(), который передает данные в представление под названием default. Шаблоны презентера можно найти в app/Presenters/templates/{PresenterName}/{viewName}.latte, поэтому в данном случае шаблон будет расположен в app/Presenters/templates/Homepage/default.latte. В шаблоне теперь доступна переменная $posts, которая содержит посты из базы данных.

Шаблон

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

По умолчанию шаблон макета располагается в файле app/Presenters/templates/@layout.latte, который содержит:

...
{include content}
...

{include content} вставляет блок с именем content в основной шаблон. Вы можете определить его в шаблонах каждого представления. В нашем случае мы отредактируем файл app/Presenters/templates/Homepage/default.latte следующим образом:

{block content}
	Привет, мир!
{/block}

Он определяет блок контента, который будет вставлен в макет. Если вы обновите браузер, то увидите страницу с текстом «Привет, мир!» (в исходном коде также с HTML заголовком и колонтитулом, определенными в @layout.latte).

Давайте отобразим записи блога — для этого отредактируем шаблон следующим образом:

{block content}
	<h1 n:block="title">Мой блог</h1>

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

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

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

Если вы обновите браузер, вы увидите список записей вашего блога. Список не очень причудлив или красочен, поэтому не стесняйтесь добавить немного блестящего CSS в www/css/style.css, а затем вставьте ссылку на этот файл в макет (файл @layout.latte):

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

Тег {foreach} перебирает все посты, переданные шаблону в переменной $posts, и выводит фрагмент HTML-кода для каждого поста. Точно так же, как это делается в PHP-коде.

Функция |date называется фильтром. Фильтры используются для форматирования вывода. Этот конкретный фильтр преобразует дату (например, 2013-04-12) в более читаемую форму (12.04.2013). Фильтр |truncate усекает строку до указанной максимальной длины и добавляет многоточие в конец, если строка усечена. Поскольку это предварительный просмотр, нет смысла отображать полное содержание статьи. Другие фильтры по умолчанию можно найти в документации или вы можете создать свои собственные, если это необходимо.

И ещё одно. Мы можем сделать код немного короче и, следовательно, проще. Мы можем заменить теги Latte на n:attributes следующим образом:

{block content}
	<h1>My 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}</div>
	</div>
{/block}

n:foreach просто обертывает div блоком foreach (он делает точно то же самое, что и предыдущий блок кода).

Подведём итог

У нас есть очень простая база данных MySQL с некоторыми записями в блоге. Приложение подключается к базе данных и отображает простой список постов.