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

Для этого нам нужно будет научить контроллер News определять, что именно запросил пользователь: список новостей или отдельную новость. Это несложно, нужно только проверять, что указано в url после /news/.

Пишем контроллер Brevis\Controllers\News с вот такой инициализацией:
	public function initialize(array $params = array()) {
		if (empty($params)) {
			$this->redirect("/{$this->name}/");
		}
		elseif (!empty($params[0])) {
			$c = $this->core->xpdo->newQuery('Brevis\Model\News');
			if (is_numeric($params[0])) {
				$c->where(array('id' => $params[0]));
			}
			else {
				$c->where(array('alias' => $params[0]));
			}
			if ($news = $this->core->xpdo->getObject('Brevis\Model\News', $c)) {
				$alias = $news->get('alias');
				if (isset($params[1]) || $params[0] != $alias) {
					$this->redirect("/{$this->name}/{$alias}");
				}
				else {
					$this->item = $news;
				}
			}
			else {
				$this->redirect("/{$this->name}/");
			}
		}

		return true;
	}
Давайте разберёмся, что здесь происходит?

Первое правило осталось неизменным — если мы запрашиваем страницу /news без завершающего слеша, то происходит редирект на правильный адрес.

Дальше мы проверяем наличие первого параметра в $params[0]. Он может содержать или число (id новости) или не число (alias новости). Мы ищем нашу новость в зависимости от типа переменной.

Если нашли, но запрошенный параметр не соответствует alias новости — делаем редирект. Также мы делаем редирект на каноничный url, если параметров больше, чем 1. Под этот случай попадает и косая на конце адреса новости.

Если же все проверки пройдены, то мы сохраняем полученную новость в свойство $item контроллера, чтобы работать с ним дальше.

В итоге, верный адрес новости у нас — s1889.h3.modhost.pro/news/the-first и никакой другой. При попытке ввести что-то не то, будет редирект или на правильный адрес, или на раздел /news/.

Теперь в методе run нам нужно просто проверять $this->item на пустоту. Если в него что-то загружено, то выводить страницу отдельной новости. Если нет — то список всех новостей.

Вывод новости

Основной метод контроллера новостей я написал такой:
	/**
	 * @return string
	 */
	public function run() {
		if ($this->item) {
			$data = array(
				'title' => $this->item->get('pagetitle'),
				'pagetitle' => $this->item->get('pagetitle'),
				'longtitle' => $this->item->get('longtitle'),
				'content' => $this->item->get('text'),
			);
		}
		else {
			$data = array(
				'title' => 'Новости',
				'pagetitle' => 'Новости',
				'items' => $this->getItems(),
				'content' => '',
			);
		}

		return $this->template('news', $data, $this);
	}
Проверка наличия загруженной новости, и если её нет — то вывод списка новостей отдельным методом getItems().

В этом методе тоже ничего сложного:
	/**
	 * Выбор последних новостей с обрезкой текста
	 *
	 * @return array
	 */
	public function getItems() {
		$rows = array();
		$c = $this->core->xpdo->newQuery('Brevis\Model\News');
		$c->select($this->core->xpdo->getSelectColumns('Brevis\Model\News', 'News'));
		$c->sortby('id', 'DESC');
		$c->limit($this->limit);
		if ($c->prepare() && $c->stmt->execute()) {
			while ($row = $c->stmt->fetch(\PDO::FETCH_ASSOC)) {
				$cut = strpos($row['text'], "\n");
				if ($cut !== false) {
					$row['text'] = substr($row['text'], 0, $cut);
					$row['cut'] = true;
				}
				else {
					$row['cut'] = false;
				}
				$rows[] = $row;
			}
		}
		else {
			$this->core->log('Не могу выбрать новости:' . print_r($c->stmt->errorInfo(), true));
		}

		return $rows;
	}
Обычный запрос через xPDO, с лимитом, указанным в News::limit. Из интересного только обрезка текста новости до первого встреченного символа переноса строки и добавление записи cut об этом в массив.

Теперь нужно добавить новый шаблон news.tpl и прописать в нём оформление.

Шаблон новостей

Здесь мы работаем с блоком content:
{block 'content'}
	{if $items}
		{foreach $items as $item}
			<div class="news">
				<h3><a href="/news/{$item.alias}">{$item.pagetitle}</a></h3>
				<p>{$item.text}</p>
				{if $item.cut}
					<a href="/news/{$item.alias}" class="btn btn-default">Читать далее →</a>
				{/if}
			</div>
		{/foreach}
	{else}
		<a href="/news/">← Назад</a>
		{parent}
	{/if}
{/block}
Если есть переменная $items, то перебираем и оформляем её в цикле. Если нет — то выводим ссылку на все новости и контент страницы, как это указано в базовом шаблоне.

Таким образом, вот список новостей, вот одна новость, а вот коммит со всеми изменениями.

Подключаем Markdown

Не знаю, как вам, а вот лично мне не нравится, что в наших новостях нет переносов строки и весь текст идёт сплошняком.

Можно, конечно, добавить замену \n на br, но лучше сразу подключить обработчик разметки Markdown — отличный класс Parsedown. Тем более, что это займёт не более 5 минут.

Меняем composer.json:
  "require": {
    "php": ">=5.3.0",
    "fenom/fenom": "2.*",
    "xpdo/xpdo": "3.0.*@dev",
    "erusev/parsedown": "1.5.*"
  },
Добавляем в ядро загрузку парсера:
	public function getParser() {
		if (!$this->parser) {
			$this->parser = new Parsedown();
		}

		return $this->parser;
	}
Добавляем обработку текста парсером при выводе новости:
'content' => $this->core->getParser()->text($this->item->get('text')),
Вот и всё — проще некуда. Метод Core::getParser() возвращает нам объкт Parsedown, в котором мы используем text().

Коммит с изменениями.

Заключение

Мы сделали вывод списка новостей, отдельной новости и добавили в проект работу с разметкой Markdown.
Всё это заняло у нас минут 20 от силы.

На мой взгляд, на этом наш курс обучения можно смело заканчивать, потому что мы:
  1. Хорошо познакомились с ООП в PHP
  2. Написали собственное ядро сайта с простеньким роутером и контроллерами запросов
  3. Отрефакторили наш проект для соответствия PSR-4 и познакомились с Composer
  4. Подключили Fenom для шаблонизации
  5. Подключили xPDO для работы с БД, написали схему и сгенерировали модель
  6. Подключили Parsedown и вывели новости на сайте
Таким образом, мы создали полностью рабочий PHP сайт, который можно бесконечно расширять в любые стороны. Добавлять контроллеры, шаблоны, прописывать вывод информации из БД и всё это с минимальными усилиями.

Если у вас есть вопросы — задавайте. Если нужно рассказать еще что-то — предлагайте, я не против написать еще одно занятие.

Следующая заметка
Добавляем новостям пагинацию
Предыдущая заметка
Подключаем xPDO


Комментарии ()

  1. Семён Лобачевский 14 июня 2015, 19:17 # 0
    Спасибо за уроки!
    Все ясно и понятно, осталось больше попрактиковаться, чтобы уложились новые знания.
    Ещё интересен вопрос по безопасности приложений, на что обращать больше внимания и может есть какие-то приёмы для проверки взломоустойчивости написанного кода.
    1. Василий Наумкин 14 июня 2015, 19:55 # 0
      Основное правило простое — всегда ожидать подвоха от юзера и проверять все входящие от него данные. Большинство возможных проблем по работе с БД мы сразу закрываем, используя xPDO.

      А вообще, тема эта очень обширная, советую начать отсюда.
    Добавление новых комментариев отключено.