Статьи

Создание блогового движка с использованием файлов Slim

В этой статье мы узнаем, как создать движок блогов для плоских файлов на PHP с помощью фреймворка Slim . Если вы не понимаете жаргон, не волнуйтесь. Мы просто собираемся создать приложение для спартанского блога, которое использует текстовые файлы вместо базы данных для хранения данных.

Если вы новичок, не волнуйтесь! Начнем с основ использования Slim . Давайте начнем после прыжка!


Slim — это облегченный RESTful PHP-фреймворк для создания простых веб-сайтов. Он вдохновлен Синатрой, фреймворком, написанным на Ruby. Slim поставляется с некоторыми минимальными компонентами, такими как Request, Response и View которые являются единственными необходимыми компонентами в нашем движке блогов.


Для начала давайте настроим приложение hello world. Перед этим вам необходимо загрузить Slim Framework в вашу систему. Мы не будем много говорить о Slim, так как он уже освещался здесь, на Nettuts +. В идеале в вашей папке должны быть следующие файлы:

  • Slim/ — Тонкий каркас
  • Index.php — индексный файл
  • .htaccess — для перезаписи URL

Теперь откройте index.php , где вы увидите кучу HTML внутри следующего раздела. Я удалил все это и заменил на «Hello world». Вы, надеюсь, index.php теперь должны выглядеть так.

1
2
3
4
5
6
require ‘Slim/Slim.php’;
$app = new Slim();
$app->get(‘/’, function () {
    echo «<h1>Hello world</h1>»;
});
$app->run();

Я создал Virtualhost на своем компьютере, URL которого показан на скриншоте. Настройте URL в зависимости от расположения Slim на вашем компьютере.

Перейдите по URL-адресу, и вы увидите домашнюю страницу с текстом «Hello world».

get() — это метод Slim, который будет принимать маршрут в качестве первого аргумента и соответствующую функцию обратного вызова в качестве последнего. Кроме get , у нас также могут быть маршруты для глаголов POST, PUT и DELETE . Поскольку Slim поддерживает PHP 5.3, функция обратного вызова может быть написана и анонимна.


Следующим шагом является рендеринг файла PHP. Прежде чем мы продолжим, создайте каталог с именем templates для хранения всех наших файлов шаблонов. Все созданные нами статические файлы HTML или шаблоны будут помещены в эту папку. Slim позволяет нам указать путь к нашим файлам шаблона в его конфигурации. Мы можем добавить конфигурацию, как показано ниже.

1
2
3
$app->config(array(
   ‘templates.path’ => ‘./templates’
));

Давайте создадим страницу о нашем блоге. Создайте файл PHP с именем about.php и поместите его в папку templates . Добавьте следующий код к нему:

01
02
03
04
05
06
07
08
09
10
11
<html>
<head>
    <title>A Slim Blog engine</title>
</head>
<body>
    <h1>About page</h1>
    <p>
        This page is an example of static route, rendering a php file.
    </p>
</body>
</html>

Чтобы Slim обрабатывал запрос, нам нужно определить соответствующий Route который может быть сопоставлен с этим URL. На нашем первом шаге мы добавили маршрут к index или «/». Теперь давайте добавим еще один маршрут для нашей страницы о.

1
2
3
$app->get(‘/about’, function () use ($app) {
    $app->render(‘about.php’);
});

Если вы загрузите http://slim.local/about в своем браузере, он должен отображать что-то вроде этого:

Мы добавили маршрут к странице about, которая будет отображать about.php расположенный по адресу ./templates (помните переменную ./templates ?). Вы заметили, что мы использовали use ($app) внутри метода get ? Ну, это просто способ использовать переменную внутри анонимной функции, которая находится за пределами ее области видимости.


Теперь мы можем отобразить страницу шаблона для маршрута. Пришло время подумать об отображении некоторых динамических значений в шаблоне, так как мы не можем постоянно рендерить и обслуживать статические файлы. Мы можем назначить значения для отображения в шаблоне из метода render() . Он должен быть передан в качестве второго параметра в виде ассоциативного массива. Давайте изменим приведенный выше код, чтобы он выглядел так:

1
2
3
4
5
6
7
$app->get(‘/about’, function () use ($app) {
    $data = array(
        ‘heading’ => ‘About page’,
        ‘message’ => ‘This page is an example of static route, rendering a php file.’
    );
    $app->render(‘about.php’,$data);
});

И немного измените тело шаблона.

1
2
3
4
5
6
<body>
    <h1><?php echo $heading;
    <p>
        <?php echo $message;
    </p>
</body>

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


До сих пор мы играли с некоторыми статическими маршрутами, ‘/’ и ‘/ about’. Теперь мы собираемся создать динамический маршрут, то есть маршрут, который может отвечать на разные URL-адреса.

1
2
3
$app->get(‘/:param1/:param2’, function ($param1,$param2) use ($app) {
    echo $param1 .’
});

Slim всегда вызывает первый маршрут, который соответствует текущему HTTP-запросу. Это означает, что все статические маршруты должны быть определены до динамических маршрутов.

Если вы загрузите http://slim.local/first-param/second-param в своем браузере, он отобразит first-param - second-param.

Маршрут переменной должен начинаться с ‘ : ‘. Slim передаст значение этой переменной в качестве аргумента нашей функции обратного вызова, чтобы мы могли проанализировать ее и выполнить соответствующую операцию. Slim ожидает ровно два параметра для вышеуказанного маршрута, поскольку две переменные маршрута являются обязательными. Если его нет, Slim отобразит ошибку 404. Мы можем сделать параметр URL необязательным, как показано на следующем шаге.


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

1
2
3
4
5
6
$app->get(‘/:param1(/:param2(/:param3))’, function () use ($app) {
    $args = func_get_args();
    foreach($args as $arg){
        echo $arg .
    }
});

При этом второй и третий параметры являются необязательными. Мы можем использовать метод func_get_args() для получения всех аргументов, которые передаются в функцию обратного вызова.

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


Нам нужно создать файловую систему, прежде чем мы объединим все эти разные компоненты. Вот простая файловая система для нашего приложения.

Это минималистичная файловая система с только необходимыми файлами / папками. Все статьи будут храниться в папке articles . В папке assets будут храниться наши файлы CSS и JavaScript, а также изображения. Slim будет содержать рамки и файлы шаблонов.


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

  • Статья будет написана в текстовом файле с URL-адресом в качестве имени файла.
  • Наше приложение будет сопоставлять URL-адреса с соответствующими статьями.
  • Для нашего удобства мы будем хранить метаинформацию (например, имя автора, дату и т. Д.) Статьи в текстовом файле в формате JSON. Это поможет нам получить их, используя встроенную в json_decode() функцию json_decode() . Содержание и метаданные будут разделены пустой строкой.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
// add article location in configuration
$app->config(array(
   ‘templates.path’ => ‘./templates’,
   ‘article.path’ => ‘./articles’ // location of articles
));
// ‘/post-url’ will load post-url.txt file.
$app->get(‘/:article’,function($article) use($app){
    $path = $app->config(‘article.path’);
    //open text file and read it
    $handle = fopen($path . ‘/’ . $article . ‘.txt’, ‘r’);
    $content = stream_get_contents($handle);
    // split the content to get metadata
    $content = explode(«\n\n», $content);
    $rawMeta = array_shift($content);
    // metadata is json encoded.
    $meta = json_decode($rawMeta,true);
    $content = implode(«\n\n», $content);
    $article = array(‘meta’ => $meta , ‘content’ => $content);
    $app->render(‘article.php’, $article);
});

Мы добавили динамический маршрут с одним параметром маршрута. Функция обратного вызова получит значение для этого параметра, которое должно быть именем файла без его расширения. Затем мы извлекаем значение переменной конфигурации article.path , где мы храним наши статьи.

В следующих двух строках мы читаем этот файл и сохраняем его содержимое в переменной $content . Как я упоминал в предыдущем абзаце, статья будет содержать метаданные и фактический контент, которые будут разделены одной строкой («\ n \ n»). Часто в содержимом статьи может быть много других пустых строк, что, вероятно, нарушит текущий метод. Чтобы избежать этого, мы будем использовать первый элемент для получения метаданных и присоединимся к остальной части массива, используя ту же пустую строку. Поскольку метаданные представлены в формате JSON, мы должны декодировать их отсюда и сохранить их в массиве $meta .

Создайте файл шаблона для визуализации статьи и поместите его в папку template .

1
2
3
//article.php
echo ‘<h2>’ .
echo $content;

Давайте создадим наш первый пост в блоге сейчас. Создайте новый файл с именем first-article.txt , поместите его в папку articles и добавьте содержимое, как показано ниже. Убедитесь, что у вас есть метаданные и контент, разделенные пустой строкой.

1
2
3
4
5
6
7
8
9
//first-article.txt
{
    «title» : «This is my first article»,
    «date» : «02/15/2012»,
    «slug» : «first-article»,
    «author»: «Author name»
}
 
Fruitcake jelly-o halvah marshmallow bonbon.

Отлично! Вы можете начать публикацию статей сейчас. Но ждать! У нас еще нет страницы со списком. Нам нужно перечислить все статьи, доступные в нашей системе, с ее названием и небольшим описанием. Чтобы сделать это, нам нужно будет проанализировать папку статей, чтобы найти все статьи и добавить их в массив, читая их одну за другой.


01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
$app->get(‘/’, function() use ($app){
    $path = $app->config(‘article.path’);
    $dir = new DirectoryIterator($path);
    $articles = array();
    foreach($dir as $file){
        if($file->isFile()){
            $handle = fopen($path . ‘/’ . $file->getFilename(), ‘r’);
            $content = stream_get_contents($handle);
            $content = explode(«\n\n», $content);
            $rawMeta = array_shift($content);
            $meta = json_decode($rawMeta,true);
            $content = implode(«\n\n», $content);
            $articles[$file->getFilename()] = array(‘meta’ => $meta, ‘content’ => $content);
        }
    }
    $app->render(‘index.php’,array(‘articles’ => $articles));
});

Здесь мы добавили маршрут к домашней странице. Мы используем встроенный в PHP класс Directoryiterator для обхода каждого файла в каталоге. Каждая статья добавляется в массив $articles Articles. Из файла шаблона ( index.php ) мы можем просмотреть эти статьи, как показано ниже.

1
2
3
4
5
6
foreach($articles as $article){
    echo «<h1> «.
    echo substr(strip_tags($article[‘content’]), 0,200)
            .
            .
}

Наша домашняя страница готова сейчас. Он перечислит все статьи в нашем блоге с соответствующим заголовком и частью контента.


Далее мы создадим страницу «архив». Поскольку мы ожидаем, что страница архивов будет иметь фильтры на основе года, месяца и даты, мы добавим маршрут с дополнительными параметрами. Страница архивов будет поддерживать следующие URL.

  • / архивы
  • / Архив / гггг
  • / Архивы / гггг / мм
  • / Дд архивы / гггг / мм /

Для этого мы загрузим все статьи и отфильтруем их по аргументам, переданным в функцию обратного вызова. Я переместил все в класс, чтобы мы могли снова использовать наш код. Маршрут, который будет поддерживать вышеуказанные URL, будет выглядеть примерно так:

1
2
3
// assign $this to another variable as it is not supported inside closure
$blog = new Blog();
$slim->get(‘/archives(/:yyyy(/:mm(/:dd)))’, function() use ($blog,$slim) {});

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$args = func_get_args();
//load all articles
$articles = $blog->loadArticles();
$archives = array();
// check count($args) for optional route params
if(count($args)>0) {
    switch(count($args)){
        case 1 : //only year is present
            $format = ‘Y’;
            $date = $dateFormat($args,$format);
            break;
        case 2 : //year and month are present
            $format = ‘Y-m’;
            $date = $dateFormat($args,$format);
            break;
        case 3 : //year, month and date are present
            $format = ‘Ym-d’;
            $date = $dateFormat($args,$format);
            break;
    }
    // filter articles
    foreach($articles as $article){
        if($dateFormat($article[‘meta’][‘date’], $format) == $date){
            $archives[] = $article;
        }
    }
}
else{
    $archives = $articles;
}
// render archives
$slim->render(‘archives.php’,array(‘archives’ => $archives));

Внутри оператора switch мы создаем дату для фильтрации, используя переданные аргументы. Эта дата сравнивается с датой каждой статьи, и если они совпадают, она добавляется в массив $archives . $dateFormat() — анонимная функция внутри маршрута для форматирования дат.

1
2
3
4
5
$dateFormat = function($args,$format){
    $temp_date = is_array($args) ?
    $date = new DateTime($temp_date);
    return $date->format($format);
};

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

1
2
3
4
5
6
7
$slim->get(‘/archives(/:yyyy(/:mm(/:dd)))’, function() use $blog {
})->conditions(
        array(
             ‘yyyy’ => ‘(19|20)\d\d’
            ,’mm’=>'(0[1-9]|1[0-2])’
            ,’dd’=>'(0[1-9]|[1-2][0-9]|3[0-1])’
        ));

Здесь год должен начинаться с 19 или 20, за которыми следуют любые две цифры. Месяц должен быть между 1 и 12, а дата должна быть между 01 и 31.

Вот страница архивов, которую я создал с помощью приведенного выше кода. Если вы заметили, что я использовал Twitter Bootstrap для применения некоторых основных стилей, вы получите дополнительный cookie!


Итак, теперь у нас есть рабочая модель блогового движка с плоскими файлами. Следующее, что нам нужно сделать, это организовать приложение, чтобы избежать дублирования кода и добавить такие функции, как комментирование и так далее. Давайте переместим этот код из index.php в отдельный класс для лучшей организации.

До сих пор мы использовали класс View платформы Slim. Мы можем создать собственный класс представления, который расширит Slim_View для добавления некоторых дополнительных функций, таких как настройка базового макета, глобальные настройки и т. Д. Если вы предпочитаете писать статьи в Markdown, вы также можете включить анализатор Markdown.

Мы также должны обратить внимание на улучшение эстетики приложения. Я предпочитаю использовать загрузчик Twitter, поскольку его очень легко использовать и настраивать. Я не думаю, что это хорошая идея, чтобы копаться в этих деталях здесь. Я собрал их в простое приложение под названием TextPress, которое можно скачать здесь .


Почти все блоговые движки предпочитают жить в облаке. Далее Git, скорее всего, используется для публикации статей. С нашим движком вы можете создавать сообщения с помощью простого текстового файла и публиковать его с помощью командной строки. Поскольку нет никакой админ-панели для взлома, она намного более безопасна, чем почти любая другая система управления контентом. Прежде всего, его легко размещать, поскольку сервисы платформы, такие как PHP Fog, позволяют нам свободно размещать приложения в облаке.

Так вот и все. Дайте мне знать, если у вас есть какие-либо вопросы в комментариях ниже, и большое спасибо за чтение!