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

На предыдущей работе, я создавал небольшие приложения для нескольких брендинговых агентств. В основном это были сайты разной сложности, но с полным отсутствием «легаси». В то время, я не знал такого слова.

Что же мне предстояло переносить — архитектурное чудо, написанное аутсорсерами на CakePHP в 2012 году. Интерфейс был построен на Twitter Bootstrap и jQuery. Из‑за усложнения бизнес логики, в проект добавили AngularJS и десяток библиотек для фингерпринтинга.

Я кивнул и взялся за рефакторинг. Сначала создал приложение на Angular. Затем поэтапно стал переписывать внешние вендоры с javascript на typescript. Были трудности, но они легко решались. Вопросы вызвал блог компании, который представлял собой набор мало связанных статей, ориентированный на поисковые системы.

Сначала я думал, что команда бэкенда предоставит API и я просто набросаю пару шаблонов и выведу содержимое. Однако, API делать никто не хотел, и посовещавшись, мы решили перенести статьи в wordpress.

Конечно, wordpress плохой выбор. Я был против, но не смог убедить коллег использовать что‑то другое, более подходящее.

Из CakePHP в Wordpress

Классика разработки
Классика разработки

Причина по которой отказались от нативного API — это административная панель для контента. У нас нет своих копирайтеров и процесс написания статей отдан на аутсорс. Предоставлять доступ к общей системе неизвестным людям мы не хотели, так как боялись за сохранность персональных данных.

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

Из миллиона бесплатных систем для контента, мы выбрали знакомую многим копирайтерам CMS — Wordpress.

Основные преимущества Wordpress:

  • Одна из старейших CMS, которая популярна до сих пор. Большинство контент менеджеров ее знают и умеют с ней работать.

  • SEO из коробки. Wordpress ориентирован на продвижение в поисковых системах и соответственно предоставляет все необходимое для SEO.

  • Большое количество плагинов. Чтобы расширить функционал достаточно найти нужное решение и нажать кнопку установить.

  • Wordpress имеет API для созданного контента, что позволяет интегрировать с различными системами.

Субъективно, к явным плюсам я бы отнес — скорость разработки. На создание темы и миграцию статей я потратил около двух дней.

Недостатки использования Wordpress:

  • Развертывание приложения. CMS плохо совместима с системами контроля версий. Wordpress обновляется самостоятельно, меняя файловую структуру как ему вздумается. Если говорить про docker, то он не упрощает задачу. При инициализации образа, будет происходить начальный запуск скрипта. По крайней мере, так было в конце 2019 года.

  • Проблема работы приложения при изменении базового url. Например, wordpress расположить в /blog, то тогда навигация в админ панеле может быть сломана. Настройка префикса является не тривиальной задачей, при наличии балансировщика и кластера на kubernetes.

  • Стабильность работы API. Метод по запросу списка записей периодически падал с 500-й. Причину мы не нашли, но грешили на сетевые проблемы, связанные с кластером.

Еще один не очевидный минус — это человеческий фактор. Случалось пару раз, когда пользователи административной панели выключали необходимые плагины, и сайт ломался.

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

После релиза, какое‑то время была стабильность. Но потом, wordpress начал сыпаться. То плагины выключат, то балансировщик ломал префиксы в приложении, то API сыплет 500-тыми.

В итоге, когда речь заходила про wordpress, то разработчики начинали рыдать.

Блог я встраивал в текущий проект на Angular. Я сверстал похожую тему и при переходе на страницу с новостями, пользователь ничего не замечал. Единственной проблемой была авторизация. Когда клиент входит в учетную запись на нашем сайте, мы в шапке указываем имя и отчество. А так как блог никак не связан с основным ресурсом, то и пользователь был не авторизован.

Бизнес часто просил поправить синхронизировать сессии, но я все время откладывал эту задачу.

Что было дальше?

Фотогрфия Bogdan Farca
Фотогрфия Bogdan Farca

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

Меня немного расстраивал тот факт, что последняя новость была опубликована год назад. Поэтому я удалил ссылки на статьи из футера. А несколько месяцев спустя, наш девопс отключил сервис, который отвечал за Wordpress, чтобы в дальнейшем его удалить.

Но не прошло и месяца, как отдел маркетинга и рекламы нашел компанию, которая будет помогать нам с SEO. И первое, что они потребовали сделать — это блог.

Снова встал вопрос относительно публикации статей. Возвращаться к wordpress — выстрел себе в ногу. Я решил посмотреть в сторону статических генераторов сайтов (Static Site Generation, SSG), например, hugo или 11ty.

Hugo мне приглянулся. Я быстро собрал pet проект, подключил тему, вывел список статей. Hugo работал идеально, но был один нюанс:

Quod licet Iovi, non licet bovi.

Hugo берет файлы с расширением.md и генерирует HTML. И казалось, что сложного, создать документ и сделать разметку в markdown, а после закоммитить. Как показывает практика: Трудно научить копирайтера пользоваться git в windows.

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

На одном из форумов, я увидел комментарий, связанный с SSG. Парень писал про связку 11ty и Contentful. 11ty альтернатива hugo, но contentful показался мне занятным и я решил попробовать его в своем проекте.

Вынос контента в Contentful

Один из вариантов использования Contentful в связке с Gatsby и Netlify
Один из вариантов использования Contentful в связке с Gatsby и Netlify

Contentful — это платформа для написания контента и доставки его на разные устройства.

Принцип работы с сервисом следующий:

  1. Регистрируется новый workspace.

  2. Создаются content‑type, которые будут использоваться в блоге.

  3. Каждой сущности добавляется набор полей.

  4. Как только готова структура, можно публиковать записи.

Весь контент доступен с помощью API. На выбор RESTи GraphQL. Для того чтобы использовать API, необходимо создать ключ и отправлять его с запросом.

Отмечу, что в бесплатной версии есть некоторые ограничения:

  • размер хранилища ~ 0.7 TB. Файл не должен превышать 50 Mb;

  • 25 content type;

  • 10 000 записей.

Лимитов более чем достаточно для публикации новостей. Я быстро накидал структуру:

  • Post;

  • Author;

  • Category;

  • Image (Asset).

Contentful сопровождается хорошей документацией и я за пару протестировал API. Меня интересовали три запроса:

  • выборка всех сущностей;

  • получение фильтрованного списка постов по выбранной категории;

  • загрузка публикации по ID.

Все отлично отдается. Осталось только подружить Contentful c Angular.

Интеграция Contentful и Angular

Contentful для Angular представляет собой обычное API, которое ничем не отличается от другого API. Задача Angular в этом случае, получить данные от API и отобразить их.

Типичный подход в фронтенде
Типичный подход в фронтенде

Типичный блог состоит из трех частей:

  1. Лента на главной странице.

  2. Статьи в конкретной категории.

  3. Публикация целиком.

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

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

В такой реализации теряется суть SSG. Один из плюсов SSG — это скорость. Отображение готового HTML всегда будет быстрее и эффективнее, чем запрос данных с сервера.

В Angular для Server Sider Rendering используют Universal. Пререндер из universal позволяет по карте маршрутов сгенерировать статичные страницы. Это необходимо для SEO, так как по умолчанию Angular отдает пустой html.

Если пререндеру передать /post-1, /post-2, то он создаст следующие файлы:

  • dist/app/browser/post-1/index.html

  • dist/app/browser/post-2/index.html

Если пользователь перейдет адресам /post-1 или /post-2, то сервер в качестве ответа, отдаст ранее созданные страницы. Однако, после загрузки Angular начнет восстанавливать состояние, а также выполнять API запросы.

Переходя по внутренним роутам, пользователь не увидит других страниц prerender‑а. Angular будет создавать их на лету. Клиент получит отрисованную версию если принудительно сделает рефреш.

Angular можно добавить преимущества SSG с помощью лайфхака. Суть метода заключается в том, чтобы вместо API использовать мета данные из роута.

При создании навигации в Angular есть возможность передавать любые значения в компонент. Это делается с помощью свойства data.

Пример:

import { Route } from '@angular/router';

export const blogRoutes: Route[] = [
  {
    path: '',
    loadComponent: () => import('@angular-blog/posts/view/page').then((modules) => modules.PostViewPageComponent),
    data: {
      post: {
        tags: [],
        published: '2023-08-05T00:00+07:00',
        title: 'Ядерное оружие, строительство метро и почитание миллионов: к годовщине гибели последнего русского императора и его семьи',
        description: 'Николай II был не только талантливым государственным деятелем и военачальником, но и выдающимся творческим человеком',
        category: { slug: 'stati', name: 'Статьи' },
        image: '//images.ctfassets.net/5lmjt5hjp7mm/1eoB41kQiSaQu9Uh4FlU3E/b38718f091199923f82e4acf2b7965ab/p18.webp',
        imageOriginal: {
          fields: {
            title:
              'Ядерное оружие, строительство метро и почитание миллионов: к годовщине гибели последнего русского императора и его семьи',
            description: '',
            file: {
              url: '//images.ctfassets.net/5lmjt5hjp7mm/1eoB41kQiSaQu9Uh4FlU3E/b38718f091199923f82e4acf2b7965ab/p18.webp',
              details: { size: 46762, image: { width: 800, height: 450 } },
              fileName: 'p18.webp',
              contentType: 'image/webp',
            },
          },
        },
        author: {
          fullName: 'Стасик Птичкин',
          email: 'stas@fafn.ru',
          avatar: '//images.ctfassets.net/5lmjt5hjp7mm/2JQGPr04yvSVmpuH6JIZYA/656c3939dcbd0296dc9c32b99e86cdb4/s3.jpg',
        },
        headline:
          'Ядерное оружие, строительство метро и почитание миллионов: к годовщине гибели последнего русского императора и его семьи',
        intro:
          'Сегодня весь мир скорбит о Николае II, которого вместе с семьёй 105 лет назад расстреляли иуды-большевики. Вот уже больше века русофобы и коммунисты пытаются оклеветать последнего императора, но «Панорама» даже в самые тёмные годы сталинской диктатуры продолжала писать правду о Николае Александровиче. ',
        slug: 'yadernoe-oruzhie-stroitelstvo-metro-i-pochitanie-millionov-k-godovshine',
        body: "<p>Сегодня весь мир скорбит о Николае II, которого вместе с семьёй 105 лет назад расстреляли иуды-большевики. Вот уже больше века русофобы и коммунисты пытаются оклеветать последнего императора, но «Панорама» даже в самые тёмные годы сталинской диктатуры продолжала писать правду о Николае Александровиче.</p><p>Оно и понятно – если бы не незаконный большевистский переворот, то ядерное оружие появилось бы у Российской империи ещё в годы Первой мировой войны.</p><p>Николай II был не только талантливым государственным деятелем и военачальником, но и выдающимся творческим человеком. Именно он создал проект высоток, которые впоследствии у него украл бессовестный Сталин. Большевики пытались скрыть, что именно при императоре в Петербурге появилось секретное метро, также незаслуженно забыт царский космолёт. Заслуги последнего царя в освоении межзвёздного пространства признали на самом высоком уровне – в честь Николая II назвали российскую космическую станцию.</p><p>К сожалению, часть имущества нашего святого императора осталось за пределами России. Достаточно вспомнить о царском мобильном телефоне, который был похищен агентами британской разведки в 1917 году и тайно вывезен в Лондон.</p><p>Но память народа жива, люди чтят своего царя. Достаточно вспомнить, с каким восторгом во всех странах СНГ встречали мощи Николая II. В своей бессильной злобе наследники большевиков-христопродавцев пытаются всячески поглумиться над памятью императора. Миллионы граждан были возмущены, когда банда коммунистов украла у Натальи Поклонской коллекцию икон святого мученика. А инициатива назначить Николая II пожизненным сенатором вызвала небывалое единение в нашем обществе.</p><p>Завершить мы бы хотели призывом установить памятник нашему любимому императору в каждом райцентре. Для экономии можно переделать монументы Ленину – опыт модернизации скульптур у отечественных специалистов есть. <a routerLink='/'>Домой</a></p>",
      },
      sitemap: {
        loc: '/yadernoe-oruzhie-stroitelstvo-metro-i-pochitanie-millionov-k-godovshine',
      },
      meta: {
        title: 'Ядерное оружие, строительство метро и почитание миллионов: к годовщине гибели последнего русского императора и его семьи',
        description: 'Николай II был не только талантливым государственным деятелем и военачальником, но и выдающимся творческим человеком',
        image: '//images.ctfassets.net/5lmjt5hjp7mm/1eoB41kQiSaQu9Uh4FlU3E/b38718f091199923f82e4acf2b7965ab/p18.webp',
        imageType: 'image/webp',
        imageWidth: '800',
        imageHeight: '450',
      },
      breadcrumbs: [
        {
          label: 'Блог',
          route: '/',
        },
        {
          label: 'Статьи',
          route: '/category/stati',
        },
      ],
    },
  },
];

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

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

Для блога с двумя категориями по двадцать материалов в каждой, получается 46 роутов.

  • Главная и лента новостей, 2 шт:

    • /

    • /feed/2

  • Список статей в конкретном разделе, 4 шт:

    • /category/first

    • /category/first/2

    • /category/second

    • /category/second/2

  • Страница публикации — 40 шт.

Демонстрация

Если проделать вышеописанные манипуляции, то получится что‑то наподобие этого:

Блог в действии
Блог в действии

Демо можно глянуть тут — angular‑blog.fafn.ru.

Исходный код на github — https://github.com/Fafnur/angular‑blog.

В следующей статье — Создание статичного блога на Angular и Contentful я расскажу подробно про реализацию блога на Angular и Contentful.

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


  1. 3ton
    16.08.2023 12:01
    -2

    Указывая недостатки Wordpress, Вы забыли указать самый главный, который может каждый узнать используя запрос "самая взламываемая CMS" в любой поисковой системе. А это на мой взгляд не маловажный минус. И я не к тому что Вы сделали плохой выбор, а к тому что есть полно молодых "специалистов", которые вот так почитают статьи и думают что все легко и просто. А потом ноют что их взломали, хостинг блокирнули, а клиент оставляет плохие отзывы. Хотя по мнению "специалиста" он все делал правильно.


    1. init0
      16.08.2023 12:01
      +2

      Ну считать это недостатком может только действительно не специалист т.к. все взломы происходят через плагины, к которым разработчики вордпресс не имеют никакого отношения. Даже если опустить этот нюанс, то поисковый запрос "самая взламываемая CMS" тоже не выглядит стоп фактором для специалиста т.к. последний должен знать, что на WP работает 40% сайтов в интернете, а ближайший конкурент не переступил даже пятипроцентный рубеж, это почти тоже самое, как утверждать, что легковые автомобили чаще попадают в ДТП чем другие транспортные средства.


      1. 3ton
        16.08.2023 12:01

        Если мы вернемся к Вашему примеру с легковым и другими транспортными средствами, то верным будет сделать аналитический срез по причинам ДТП. И у легкового транспорта их будет гораздо больше ввиду более резвого стиля езды, ввиду более доступной категории прав и всего остального. Поэтому я согласен с Вами о том что в преимущественном большинстве вина на "специалисте", но то что инструмент позволяет на 5 скорости врубить заднюю - не вина водителя так делающего, а проектировщика данного продукта.


        1. init0
          16.08.2023 12:01

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

          Не совсем верно, если в вашем предложении заменить "легкового транспорта" на "мотоциклов" - вот тогда фраза становится достоверной.

          то что инструмент позволяет на 5 скорости врубить заднюю - не вина водителя так делающего, а проектировщика данного продукта.

          Тут тоже плохо с аналогией. Кому в здравом уме прийдет в голову такая идея? Если вы делаете такое то вы или в усмерть пьяны, либо ваши права куплены (это еще одна отсылка к "специалистам" btw).


    1. fafnur Автор
      16.08.2023 12:01
      +1

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


      1. 3ton
        16.08.2023 12:01

        Мы просто по разному смотрим на ситуацию, но сама ситуация то от этого не становится разной...

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

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


  1. init0
    16.08.2023 12:01
    +1

    CMS плохо совместима с системами контроля версий.

    Можно раскрыть мысль, что конкретно несовместимо?

    Wordpress обновляется самостоятельно, меняя файловую структуру как ему вздумается.

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

    Проблема работы приложения при изменении базового url. Например, wordpress расположить в /blog, то тогда навигация в админ панеле может быть сломана.

    Тоже что и выше – изменить константу в конфигурационном файле.

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

    Логи не пробовали смотреть? Ну и как "проблема" на сетевом уровне может аффектить приложение на php не очень понимаю – 500-ка это же просто fatal ошибка (смотреть логи).


    1. fafnur Автор
      16.08.2023 12:01

      Можно раскрыть мысль, что конкретно несовместимо?

      Я имел ввиду то, что нельзя просто делать `git pull` и считать, что все обновилось. Я не знаю, есть ли консольная команда, которая может обновить проект Wordpress-а.

      Про файловую структуру тоже совершенно непонятно, она не меняется уже много лет.

      Я видимо попал в тот момент, когда сверстал тему на основе базовой темы. И при одном обновлении эта тема изменилась. Возможно сейчас это не так.

      Тоже что и выше – изменить константу в конфигурационном файле.

      Локально все хорошо. Проблемы возникали в продакшене. Возможно у нас какие-то проблемы были с кластером и это косяки со стороны девопсов, но в любом случае это вызывало дискомфорт.

      Логи не пробовали смотреть?

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


      1. init0
        16.08.2023 12:01
        +1

        Я имел ввиду то, что нельзя просто делать `git pull` и считать, что все обновилось.

        Именно так и обновляется WP как и плагины/темы. Другое дело, что после обновления вордпреса/плагинов может понадобтся обновить данные в БД.

        Я не знаю, есть ли консольная команда, которая может обновить проект Wordpress-а.

        WP CLI

        Я видимо попал в тот момент, когда сверстал тему на основе базовой темы. И при одном обновлении эта тема изменилась. Возможно сейчас это не так.

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

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

        Вы же должны понимать, что это нонсенс, php упал с fatal ошибкой и в логи ничего не записал?


        1. fafnur Автор
          16.08.2023 12:01

          Про логи согласен.

          WP CLI слышу в первый раз. Но я не сильно погружался в экосистему WP.

          Wordpress имеет место на существование и я никого не отговариваю от его использования. Если разобраться в тонкостях, достаточно сносное решение.


      1. powernic
        16.08.2023 12:01

        Я имел ввиду то, что нельзя просто делать `git pull` и считать, что все обновилось. Я не знаю, есть ли консольная команда, которая может обновить проект Wordpress‑а.

        Можно использовать composer и устанавливать wordpress вот так: composer require johnpbloch/wordpress если нужно изменить версию wp то пишем composer require johnpbloch/wordpress:6.0.1, с плагинами аналогично composer require wpackagist-plugin/akismet.


        1. fafnur Автор
          16.08.2023 12:01

          Composer это хорошо. Можно все автоматизировать и упростить.

          Я больше говорил про сам процесс. Нужно что-то настраивать. Если в WP поддерживал миграции, то было бы проще.

          Возможно миграции уже есть в ядре. Я работал с WP очень давно. Все развивается, даже Wordpress.

          Например:

          git pull
          composer update
          composer migrate

          Затем все собрать в образ docker build . и выложить в прод.