Шаг 6.1 Добавление и вывод статей на сайте

Постепенно мы подбираемся ко все более сложным и интересным моментам нашей работы. На этот раз мы сделаем вывод статей на сайте, используя только собственные разработки и API системы управления MODx. И да, мы не будем использовать Ditto ;).

Итак, рассмотрим наши статьи, как их нарисовал дизайнер, и выделим наиболее существенные части, которые должны быть запрограммированы и выведены на главной странице:

  1. Заголовок статьи
  2. Дата публикации
  3. Категория, к которой принадлежит данный документ
  4. Имя автора статьи
  5. Ссылка на комментарии к данной статье
  6. Вводное содержимое
  7. Ссылка "Читать далее" на полный текст статьи

Эти данные будут присутствовать всегда при появлении новой статьи на сайте. Кроме того, нам необходимо будет предусмотреть ситуацию, когда статей станет много и, следовательно, понадобится автоматически разбивать поток статей на страницы.

Итак, задачи ясны, дерзаем? :)

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

Каждый из этих документов содержит простой текст, что-то наподобие этого:

Теперь осталось дело за малым – сделать вывод этой информации на сайте :).

Логика вывода этой информации будет по большому счету несильно отличаться от вывода ссылок для верхнего меню: с помощью функций API мы получим список дочерних документов и затем выделим из полученного массива необходимые данные. Хотя есть и некоторые особенности:

  • публикацию страниц блога с кратким описанием мы запланировали только на главной странице, и, значит, новый созданный сниппет будет вызываться только на главной странице, а не во всем шаблоне, как это было с верхним меню;
  • будут задействованы некоторые другие поля БД, как например, “introtext”, “content” и другие;
  • к каждой статье блога добавим комментарии;
  • ну и разные другие мелочи, о которых подробнее поговорим ниже.

Рассмотрим HTML код одного блока статьи блога на главной странице:

  1.  
  2. <!-- Article -->
  3. <div class="article">
  4. <h2><span><a href="#">Заголовок статьи</a></span></h2>
  5. <p class="info noprint">
  6. <span class="date">2007-01-01 в 00:01</span><span class="noscreen">,</span>
  7. <span class="cat"><a href="#">Категория</a></span><span class="noscreen">,</span>
  8. <span class="user"><a href="#">Имя автора</a></span><span class="noscreen">,</span>
  9. <span class="comments"><a href="#">Комментарии</a></span>
  10. </p>
  11. <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam pellentesque enim blandit enim bibendum blandit.
  12. Integer eu leo ac est aliquet imperdiet. Quisque nec justo id augue posuere malesuada. Nullam ac metus. Cras non leo
  13. ut est placerat condimentum. Aliquam ut enim. Quisque non sapien in enim eleifend faucibus. Pellentesque sodales. Mauris
  14. auctor arcu sit amet felis. Donec eget enim ut lacus pharetra condimentum. Nulla in felis vel tortor imperdiet consectetuer.
  15. Sed id ante.</p>
  16. <p class="btn-more box noprint"><strong><a href="#">Читать далее</a></strong></p>
  17. </div> <!-- /article -->
  18. <hr class="noscreen" />
  19.  

После тега <hr class="noscreen" /> блоки статей повторяются, поэтому дальше будем работать с приведенным выше кодом.

Разделим этот код на части и обсудим детально каждый момент:

  1. Заголовок статьи, который также будет ссылкой на полную статью.
    1.  
    2. <h2><span><a href="#">Заголовок статьи</a></span></h2>
    3.  
    Ну это мы легко получим, как делали уже с верхним меню.
  2. Дата создания статьи.
    1.  
    2. <span class="date">2007-01-01 в 00:01</span>
    3. <span class="noscreen">,</span>
    4.  
    Тоже самое, дата создания любого документа хранится в БД MODx в таблице {PREFIX}site_content в поле "createdon", а, значит, легко нам доступна.
  3. Категория, к которой автор отнесет статью (кстати, мы сделаем такие возможности, чтобы автор мог выбирать несколько категорий для одной статьи)
    1.  
    2. <span class="cat"><a href="#">Категория</a></span>
    3. <span class="noscreen">,</span>
    4.  
    Да, здесь будет чуть сложнее – мы используем TV для вывода категорий. Все категории мы будем хранить как документы в папке "Категории":
  4. Имя автора, который написал данную статью. Мы же помним о том, что теоретически у нас может быть несколько авторов?
    1.  
    2. <span class="user"><a href="#">Имя автора</a></span>
    3. <span class="noscreen">,</span>
    4.  
    В таблице {PREFIX}site_content есть указание на автора в поле ”createdby” для каждого документа в системе, но если посмотреть значения этого поля, то мы увидим только цифры (1, 2, 3 и т.д.). Эти цифры обозначают порядковые номера всех пользователей внутри системы управления. Ниже мы рассмотрим этот вопрос подробнее.
  5. Ссылка на комментарии к данной статье.
    1.  
    2. <span class="comments"><a href="#">Комментарии</a></span>
    3.  
    Нет ничего проще – это будет ссылка на полную статью плюс для удобства добавим якорь именно к комментариям на основе известного сниппета Jot. А, кстати, давайте еще добавим к этому тексту количество комментариев? :) Это будет несложно сделать, а наши авторы и посетители блога будут довольны такой возможностью. Таким образом, у нас должно получиться как-то так:

  6. Краткий вводный текст статьи.
    1.  
    2. <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
    3. ...
    4. </p>
    5.  
    Этот текст также нам будет легко доступен из поля "introtext" в БД (или по-русски в системе управления MODx он назван как "Аннотация"). Кстати, зададимся вопросом: а как быть, если автор при создании статьи не позаботился о заполнении этого поля? Откуда нам взять текст для аннотации в этом случае? По моему опыту это довольно часто случается. Ну что-ж, мы решим и эту проблему.
  7. Ссылка "Читать далее".
    1.  
    2. <p class="btn-more box noprint">
    3. <strong><a href="#">Читать далее</a></strong>
    4. </p>
    5.  
    Элементарно, к тому же, при программировании пунктов выше мы уже решим этот вопрос.

Ну вот как-бы и все, что касается вывода статей на главной странице. Хотя… нет, еще один вопрос остался нерешенным:

  1. Постраничное разбиение статей на главной странице.
    Этого нет ни в базе данных, ни даже в выбранном дизайне (почему-то наш дизайнер поленился нарисовать эту навигацию). Честно говоря, на этом пункте мне хотелось остановиться поподробнее, размять мозги и поработать над хорошей "постраничной навигацией". Но, к сожалению, это займет наверняка не меньше времени (а скорее, даже больше), чем написание подобной статьи. Поэтому упростим навигацию до минимума ссылок "< Назад" и "Вперед >":

    В main.css добавьте следующую строку
    1.  
    2. #pagination a{ margin:0 0 0 20px; }
    3.  
    А также в документе “Блог” добавьте следующий HTML код сразу после всего остального кода
    1.  
    2. <div id="pagination">
    3. <a href="#">< Назад</a>
    4. <a href="#">Вперед ></a>
    5. </div>
    6.  
    Вполне возможно, что в будущем я все-таки вернусь к этому пункту и мы реализуем хорошую во всех смыслах навигацию. А на данном этапе пока пройдем далее.

Итак, создадим новый сниппет с названием "Articles". Для начала пусть он выводит тот же самый HTML код одного блока и навигационный блок:

  1.  
  2. <?php
  3. $output = "
  4. <!-- Article -->
  5. <div class=\"article\">
  6. <h2><span><a href=\"#\">Заголовок статьи</a></span></h2>
  7. <p class=\"info noprint\">
  8. <span class=\"date\">2007-01-01 в 00:01</span><span class=\"noscreen\">,</span>
  9. <span class=\"cat\"><a href=\"#\">Категория</a></span><span class=\"noscreen\">,</span>
  10. <span class=\"user\"><a href=\"#\">Имя автора</a></span><span class=\"noscreen\">,</span>
  11. <span class=\"comments\"><a href=\"#\">Комментарии</a></span>
  12. </p>
  13. <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam pellentesque enim blandit enim bibendum blandit.
  14. Integer eu leo ac est aliquet imperdiet. Quisque nec justo id augue posuere malesuada. Nullam ac metus. Cras non leo
  15. ut est placerat condimentum. Aliquam ut enim. Quisque non sapien in enim eleifend faucibus. Pellentesque sodales. Mauris
  16. auctor arcu sit amet felis. Donec eget enim ut lacus pharetra condimentum. Nulla in felis vel tortor imperdiet consectetuer.
  17. Sed id ante.</p>
  18. <p class=\"btn-more box noprint\"><strong><a href=\"#\">Читать далее</a></strong></p>
  19. </div> <!-- /article -->
  20. <hr class=\"noscreen\" />
  21. <div id=\"pagination\">
  22. <a href=\"#\">< Назад</a>
  23. <a href=\"#\">Вперед ></a>
  24. </div>
  25. ";
  26. return $output;
  27. ?>
  28.  

Вызывать этот сниппет будем на главной странице, а это значит, что в содержимом документа "Блог" нужно добавить следующую конструкцию [!Articles!]. Как видно из данной конструкции, сниппет будет некэшируемый. Задание на дом – почему? :) Весь остальной код из документа "Блог" можно смело удалить.

Теперь будем в нашем сниппете последовательно по списку заменять статичный код на динамический. Во многом нам будет полезна уже известная API функция getDocumentChildren():

  1.  
  2. <?php
  3. $results = $modx->getDocumentChildren(
  4. $id = 1, // ID родительского документа, а именно документа "Блог"
  5. $active = 1, // Выбираем только опубликованные документы
  6. $deleted = 0, // Выбираем только неудаленные документы
  7. 'id, pagetitle, published, introtext, content, menuindex, createdby, createdon, deleted, menutitle', // Выбираем поля из БД
  8. $where = '', // Дополнительные условия не требуются
  9. $sort='createdon', // Сортируем документы по полю createdon
  10. $dir='DESC', // Сортируем документы по убыванию
  11. $limit = '' // Ограничения не устанавливаем (параметр LIMIT в SQL запросе)
  12. );
  13.  
  14. foreach($results as $key => $value) {
  15. if ($value["menutitle"] != "") {
  16. $title = $value["menutitle"];
  17. }
  18. else {
  19. $title = $value["pagetitle"];
  20. }
  21. $items .= "
  22. <!-- Article -->
  23. <div class=\"article\">
  24. <h2><span><a href=\"#\">Заголовок статьи</a></span></h2>
  25. <p class=\"info noprint\">
  26. <span class=\"date\">2007-01-01 в 00:01</span><span class=\"noscreen\">,</span>
  27. <span class=\"cat\"><a href=\"#\">Категория</a></span><span class=\"noscreen\">,</span>
  28. <span class=\"user\"><a href=\"#\">Имя автора</a></span><span class=\"noscreen\">,</span>
  29. <span class=\"comments\"><a href=\"#\">Комментарии</a></span>
  30. </p>
  31. <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam pellentesque enim blandit enim bibendum blandit.
  32. Integer eu leo ac est aliquet imperdiet. Quisque nec justo id augue posuere malesuada. Nullam ac metus. Cras non leo
  33. ut est placerat condimentum. Aliquam ut enim. Quisque non sapien in enim eleifend faucibus. Pellentesque sodales. Mauris
  34. auctor arcu sit amet felis. Donec eget enim ut lacus pharetra condimentum. Nulla in felis vel tortor imperdiet consectetuer.
  35. Sed id ante.</p>
  36. <p class=\"btn-more box noprint\"><strong><a href=\"#\">Читать далее</a></strong></p>
  37. </div> <!-- /article -->
  38. <hr class=\"noscreen\" />
  39. "; // Собираем блоки статей
  40. }
  41.  
  42. $output = "
  43. <div id=\"pagination\">
  44. <a href=\"#\">< Назад</a>
  45. <a href=\"#\">Вперед ></a>
  46. </div>
  47. ";
  48.  
  49. return $items.$output;
  50. ?>
  51.  

После выполнения этого кода мы получим несколько раз повторяющийся HTML код одного и того же блока, при этом количество повторений будет совпадать с количеством дочерних документов у документа с ID = 1. В моем случае код повторился 7 раз.

Теперь изменим код, используя некоторые результаты запроса функции getDocumentChildren:

  1.  
  2. <?php
  3. $results = $modx->getDocumentChildren(
  4. $id = 1, // ID родительского документа, а именно документа "Блог"
  5. $active = 1, // Выбираем только опубликованные документы
  6. $deleted = 0, // Выбираем только неудаленные документы
  7. 'id, pagetitle, published, introtext, content, menuindex, createdby, createdon, deleted, menutitle', // Выбираем поля из БД
  8. $where = '', // Дополнительные условия не требуются
  9. $sort='createdon', // Сортируем документы по полю createdon
  10. $dir='DESC', // Сортируем документы по убыванию
  11. $limit = '' // Ограничения не устанавливаем (параметр LIMIT в SQL запросе)
  12. );
  13.  
  14. foreach($results as $key => $value) {
  15. if ($value["menutitle"] != "") {
  16. $title = $value["menutitle"];
  17. }
  18. else {
  19. $title = $value["pagetitle"];
  20. }
  21. $items .= "
  22. <!-- Article -->
  23. <div class=\"article\">
  24. <h2><span><a href=\"[~".$value["id"]."~]\">".$title."</a></span></h2>
  25. <p class=\"info noprint\">
  26. <span class=\"date\">".$value["createdon"]."</span><span class=\"noscreen\">,</span>
  27. <span class=\"cat\"><a href=\"#\">Категория</a></span><span class=\"noscreen\">,</span>
  28. <span class=\"user\"><a href=\"#\">".$value["createdby"]."</a></span><span class=\"noscreen\">,</span>
  29. <span class=\"comments\"><a href=\"[~".$value["id"]."~]#comments\">Комментарии</a></span>
  30. </p>
  31. ".$value["introtext"]."
  32. <p class=\"btn-more box noprint\"><strong><a href=\"[~".$value["id"]."~]\">Читать далее</a></strong></p>
  33. </div> <!-- /article -->
  34. <hr class=\"noscreen\" />
  35. "; // Собираем блоки статей
  36. }
  37.  
  38. $output = "
  39. <div id=\"pagination\">
  40. <a href=\"#\">< Назад</a>
  41. <a href=\"#\">Вперед ></a>
  42. </div>
  43. ";
  44.  
  45. return $items.$output;
  46. ?>
  47.  

В сниппете используются следующие переменные:

  1. $value["pagetitle"] – заголовок документа
  2. $value["menutitle"] – пункт меню, если он заполнен, то берем название документа из него
  3. $value["id"] – ID документа, при помощи конструкции MODx [~ID~] превращаем его в ссылку на данный документ (где ID – номер документа)
  4. $value["createdon"] – дата создания документа, хранится в базе данных в виде количества секунд, прошедших с начала Эпохи Unix
  5. $value["createdby"] – ID автора документа, в дальнейшем мы его превратим в нормальный текст
  6. $value["introtext"] – вводный текст статьи, аннотация

Сохраним сниппет и посмотрим результат его работы на главной странице сайта:

Как видно из рисунка, все записи расположились по убыванию в порядке добавления их в систему. Также мы видим, что все записи получили следующие данные:

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

Для форматирования даты используем стандартную функцию PHP date():

  1.  
  2. date("d/m/Y в H:i", $value["createdon"])
  3.  

При этом дата будет выводиться в виде "19/01/2009 в 22:15", где, соответственно, 19 – день, 01 – номер месяца, 2009 – год, 22 – час и 15 – минута создания документа.

Замените в сниппете $value["createdon"] на date("d/m/Y в H:i", $value["createdon"]). Сохраните сниппет и обновите страницу. Этого будет достаточно, чтобы наша дата превратилась в нормальный вид.

Чтобы выводить нормальное имя автора, а не его ID, воспользуемся еще одной функцией MODx API getUserInfo(). Функция getUserInfo() получает только один параметр – ID пользователя, а возвращает результат в виде массива данных. В возвращаемом массиве есть ключ "fullname". Как и следует из названия, здесь хранится полное имя автора.

Откроем код сниппета, найдем в коде сниппета $items и выше перед переменной $items добавим следующую строку:

  1.  
  2. $author = $modx->getUserInfo($value["createdby"]);
  3.  

Далее найдем $value["createdby"], заменим эту переменную на $author["fullname"], сохраним сниппет и обновим страницу. В итоге сниппет выведет что-то вроде этого:

"Default admin account" как-то не звучит, правда? :) Наверняка, у большинства будет тоже самое. А теперь зайдем в MODx, закладка "Пользователи" -> "Управление менеджерами", найдем своего пользователя и отредактируем имя. У меня получилось так:

Заключение

За сим пока остановимся, иначе эта статья расползется на километр :). Подведем некоторые итоги:

  • сделали вывод статей блога на главной странице с помощью собственного сниппета (и, как ни странно, Ditto здесь совершенно ни при чем!);
  • все необходимые первичные данные получили с помощью функции MODx API - getDocumentChildren() и обработали их так, как нам понадобилось в нашем дизайне;
  • узнали об еще одной функции API – getUserInfo() и использовали ее по назначению в своем коде;
  • если у вас что-то не получилось, скопируйте полный листинг нашего сниппета и замените у себя; я надеюсь, теперь все заработает :).

А в следующей части этой статьи мы научимся:

  1. подключать комментарии к статьям и изменять их внешний вид;
  2. подключать категории;
  3. разбивать блоки статей постранично;
  4. если хватит свободного времени и объема статьи, то в следующей же статье научимся сортировать все записи по автору публикации и категориям; если же нет, то это мы это обсудим сразу в новой статье;
  5. ну и скорее всего по ходу написания статьи будут появляться разные другие мелкие вкусности :).

С приветом и ожиданием активного обсуждения,
Игорь a.k.a Fuzzy.

Предыдущие статьи из цикла:

Первые | Назад | с 11 по 20 из 49 | Далее | Последние
new comment 02.02.2009, 09:26:05

Благодарю за ответ. Буду ждать, когда опишите решение о подключении категорий :)

Ещё вопросик: в предложенном решении автором статьи числится не автор, а лицо, опубликовавшее статью. А это разные вещи. И не раскрыта тема совместного авторства.

new comment 02.02.2009, 10:41:00

2Игорь:

>>Буду ждать, когда опишите решение о подключении категорий

Скоро все будет :)

>>в предложенном решении автором статьи числится не автор, а лицо, опубликовавшее статью

Хм.. на самом деле подразумевалось, что тот, кто публикует статью, сам же автором и является. Это же блог, все-таки, а не корпоративный веб-сайт.

Если обязательно требуется указывать другое лицо как автора статьи, то, возможно, для этого потребуется вводить специальные TV с именем автора. Например, логика могла быть в этом случае такой: если не заполнено специальное поле TV "автор", то выводится имя автора как было описано выше; если же поле TV "автор" заполнено, то берем имя автора отсюда. Это легко организовать, но работа с TV у нас будет дальше и не вписывается в рамки этой статьи.

new comment 02.02.2009, 11:09:14

Мария, Fuzzy - решил вмешаться в ваши обсуждения, надеюсь не помешаю :)

Конечно можно использовать SQL-запросы напрямую, но мне кажется что все таки оптимальным решением является использование API-функций движка. Если вы считаете, что API-функции избыточны или неоптимизированы - на это стоит обратить внимание разработчиков.

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

new comment 02.02.2009, 16:59:32

>>работа с TV у нас будет дальше Вот как :) Интересно. Всё больше убеждаюсь, что я ещё не осилил концепцию ModX, а то я уже думал, что придётся перекраивать таблички для хранения данных...

new comment 10.02.2009, 18:11:09

Отличные статьи, большое спасибо автору. Небольшое пожелание: как-то некрасиво выглядит php вперемешку с html. А вдруг нагрянет неожиданная смена дизайна? Потом копаться в php коде ни один дизайнер не захочет, да и программисту работа не из приятных.

PS: А вообще, все супер. С нетерпением жду следующей статьи.

new comment 11.02.2009, 16:44:26

Толково пишите, продолжайте в том же дуже, буду ждать. После Друпала мне МОД-х как-то понятнее. Не нравится что шаблоны в БД, но можно ведь инклудить их, хоть и не слишком удобно. Странный какой-то выбор. Или от этого защита выше ? :)

new comment 12.02.2009, 11:31:58

2Yanzay: Верно, в принципе, стоит отделять html от php.. Но честно говоря, лень :) Если заниматься еще и этой оптимизацией, то статьи будут выходить еще реже... Готов выложить подправленную версию кода, если Вы поучаствуете и поможете мне в этом ;)

И вообще, мы всегда открыты для предложений по улучшению и внедрению новых возможностей как собственно статей и кода, так и вообще самого сайта modx.ru.

new comment 12.02.2009, 11:39:16

2Дизайнер: Не думаю, что это как-то влияет на защиту. Так уж повелось еще со времен Etomite. Чем был обусловлен такой выбор - трудно сказать. Хотя для меня обычно этот способ хранения шаблонов не создает никаких проблем. И даже в чем-то удобен, например тем, что можно быстро сделать исправления в коде шаблона, не залезая на сервер по FTP.

new comment 12.02.2009, 17:51:45

Мне хранение шаблона показалось неудобным, т.к. в случае ошибок в рнр коде выдается на сайте ошибка mysql. Отследить труднее. Еще вопросик возник по сортировке новостей. $results = $modx->getDocumentChildren( . . . $dir='DESC', // Сортируем документы по убыванию )

Не сортирует, пробовал ASC - тоже самое (хотя если работает то не знаю как проверить, выдает то по возрастанию), наверное не отрабатывает а почему?

New
new comment 26.02.2009, 18:27:12

Здравствуйте. В первую очередь хочу поблагодарить автора за отличные статьи. Очень легко и понятно разбираться в modx с вашей помощью. Я новичок в модикс, поэтому нюбский вопрос: " в“Блог” добавьте следующий HTML код " - мне не шибко понятно где в документе "блог" можно редактировать его html? Мы ведь создали чанки, там лежит шаблон.

Первые | Назад | с 11 по 20 из 49 | Далее | Последние

Добавить комментарий

Для форматирования используйте синтаксис Markdown. Если вы не можете разобрать символы в рисунке, нажмите на него для автоматического обновления
Если вы не можете разобрать символы в рисунке, нажмите на него для автоматического обновления
Код безопасности: