PHP и файлы. Новости без MySQL с перелистыванием
Краеугольный камень для всех начинающих программистов PHP — организация сайта без использования базы данных. О целесообразности писать не буду. Есть довольно много работающих систем, некоторые очень даже привлекательны. Считается, что проблема «PHP и файлы» давно решена в пользу PHP+MySQL = стандарт. Но эта проблема застыла в вечности, и все равно к этому вопросу обращаются новые адепты и возвращаются те, у кого есть уже опыт программирования на PHP.
Однако, написать эту заметку меня натолкнула просьба двухлетней давности одного знакомого, которому понадобилось на сайте выводить новости без использования БД. Мало того, человеку не нужна была даже панель администрирования, так как сайт располагался на его домашнем компьютере. Знакомый был убежден в том, что не может быть ничего проще — открыть блокнот, записать туда нужный текст и отправить в нужную папку. Я не смог убедить его в том, что люди не зря придумали панель администрирования.
Я не помню, как мы решили вопрос с сайтом. Но сейчас эта идея всплыла в моей голове. Даже интересно посчитать и сравнить количество операций при добавлении новости на сайт через админку и через блокнот, с учетом того, что сервер на рабочем компьютере.
Шутки ради — честно ищем кратчайший список операций для каждого.
Классический способ
1. Открыть браузер.
2. Набрать адрес или нажать на вкладку.
3. Опционально. набрать пароль или нажать подтвердить.
4. Набрать текст.
6. Нажать отправить.
Получаем 5-6 операций. Затраты времени на открытие браузера.
Способ с файлами
1. Открыть блокнот.
2. Набрать текст
3. Сохранить сразу в папку на сервере.
3 операции, может, 4. Нет затрат времени на открытие браузера, блокнот в Windows открывается мгновенно.
Про Linux/BSD писать не будем, редактор vi не для контент-менеджера, хотя, если сервер не на рабочем компьютере, то все равно может оказаться быстрее — открыть ssh, создать файл с помощью vim или nano. Тем более, что в vim сохранение и закрытие файла прекрасно сводятся к одной команде. =)
Теперь немного практики. Это лишь начало попытки реализовать идею новостей на файлах. Задача написать максимально простой и понятный код.
Что хотелось бы из функционала?
- Выводить новость на главной,
- Перелистывать
- Добавлять через панель администрирования
1. Создадим папку в директории www для хранения файлов, у меня bd/.
2. Создадим в bd файл с именем, допустим, all.dat — в нем будем вести счёт новостям. Наличие этого файла избавляет от использования функций scandir или glob для подсчёта файлов. Запишем в него цифру 0.
Панель администрирования
Создадим папку ad в директории www и поместим туда следующий скрипт. У меня index.php. С учётом того, что данная директория будет под паролем и доступна только одному администратору, то тут можно пренебречь некоторыми принципами хорошего кода.
Все просто. Обрабатываем форму. Я делаю минимум условий. Если текстовое поле не пусто, то получаем запись из файла, в котором ведем счет записям — all.dat. А в нем у нас 0.
Создаем файл с именем 0.txt, проверяем на всякий случай его наличие и записываем в него данные.
Теперь проверим, существование нового файла и увеличим счётчик на 1 в файле all.dat.
Для вывода новостей на главную страницу напишем такой скрипт. Получим число записей из all.dat.
Дальше совсем просто, если переменная для страницы — p — установлена и такой файл есть, получаем содержимое.
Иначе получаем последнюю сделанную запись.
Обратите внимание, что записи перевернуты, то есть файл с большим числом в имени и есть последняя новость на главной, а для перемотки использован декремент.
Вывод: получилось избавиться от массивов, циклов и функций glob, scandir.
К скрипту так же просто можно добавить удаление и редактирование в панели администратора. Останется только организовать защиту и можно потестировать скрипт на бесплатном хостинге.
Файлы можно хранить не в .txt а .html, что позволит прописывать для каждого файла, например, свои мета-теги.
Несложно будет добавить эту опцию в панель администратора.
Post Scriptum:
Целью этой заметки является привлечение внимания к перегруженности профессиональных решений для организаций новостей и поиска вдохновения для написания микродвижков.
Дополнение: на базе этого скрипта можно попробовать реализовать автоматический текстовый слайдер. При этом можно использовать ajax, я использую псевдо-аякс в примере ниже и немного анимации (CSS3).
Рекомендуется открыть в Opera или Chrome: Link
Фикции
В слайдер можно можно добавить опцию для настройки паузы. Если вычислять пазу с помощью скрипта и устанавливать значение времени с учетом количества символов (и учесть вдобавок среднюю скорость чтения взрослого человека ), то вполне может получиться полностью автоматическая система для скорочтения.
Останется добавить только голосовое распознавание текста и от экрана можно отойти.
Выводим новости
На прошлом занятии мы подключили xPDO и попробовали его в работе. Сегодня мы можем добавим в БД пару новостей и вывести их на отдельных страницах.
Для этого нам нужно будет научить контроллер News определять, что именно запросил пользователь: список новостей или отдельную новость. Это несложно, нужно только проверять, что указано в url после /news/.
Пишем контроллер Brevis\Controllers\News с вот такой инициализацией:
public function initialize(array $params = array()) < if (empty($params)) < $this->redirect("/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("/name>/"); > else < $this->item = $news; > > else < $this->redirect("/name>/"); > > return true; >
Давайте разберёмся, что здесь происходит?
Первое правило осталось неизменным — если мы запрашиваем страницу /news без завершающего слеша, то происходит редирект на правильный адрес.
Дальше мы проверяем наличие первого параметра в $params[0]. Он может содержать или число (id новости) или не число (alias новости). Мы ищем нашу новость в зависимости от типа переменной.
Если нашли, но запрошенный параметр не соответствует alias новости — делаем редирект. Также мы делаем редирект на каноничный url, если параметров больше, чем 1. Под этот случай попадает и косая на конце адреса новости.
Если же все проверки пройдены, то мы сохраняем полученную новость в свойство $item контроллера, чтобы работать с ним дальше.
В итоге, верный адрес новости у нас — http://s1889.bez.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:
"> " btn-default">Читать далее →
← Назад
Если есть переменная $items, то перебираем и оформляем её в цикле. Если нет — то выводим ссылку на все новости и контент страницы, как это указано в базовом шаблоне.
Подключаем Markdown
Не знаю, как вам, а вот лично мне не нравится, что в наших новостях нет переносов строки и весь текст идёт сплошняком.
Можно, конечно, добавить замену \n на br, но лучше сразу подключить обработчик разметки Markdown — отличный класс Parsedown. Тем более, что это займёт не более 5 минут.
"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 от силы.
На мой взгляд, на этом наш курс обучения можно смело заканчивать, потому что мы:
- Хорошо познакомились с ООП в PHP
- Написали собственное ядро сайта с простеньким роутером и контроллерами запросов
- Отрефакторили наш проект для соответствия PSR-4 и познакомились с Composer
- Подключили Fenom для шаблонизации
- Подключили xPDO для работы с БД, написали схему и сгенерировали модель
- Подключили Parsedown и вывели новости на сайте
Таким образом, мы создали полностью рабочий PHP сайт, который можно бесконечно расширять в любые стороны. Добавлять контроллеры, шаблоны, прописывать вывод информации из БД и всё это с минимальными усилиями.
Если у вас есть вопросы — задавайте. Если нужно рассказать еще что-то — предлагайте, я не против написать еще одно занятие.