В этом руководстве вы узнаете, как создать плагин в Strapi, создав плагин аналитики.

Автор: Гириш Венкатачалам

В сегодняшней модели веб-разработки Jamstack безголовая система управления контентом (CMS) может стать отличным выбором для получения преимущества над конкурентами.

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

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

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

Эта статья покажет вам, как разработать плагин Strapi для личного использования или для распространения и интеграции с торговой площадкой Strapi.

Процесс может показаться немного сложным из-за огромного количества доступной информации, отсутствия документации по процессу и неразработанного кода в репозиториях GitHub. Однако, если вы будете следовать этим инструкциям, вы сможете создать подключаемый модуль аналитики, демонстрирующий, как Strapi можно использовать для построения графиков и визуализации контента CMS.

Чтобы создать этот плагин, вам понадобятся хорошие навыки работы с Node.js и знание маршрутизации Express, Hapi или Koa, моделей, контроллеров и промежуточного программного обеспечения.

Что такое Страпи?

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

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

Вы можете использовать веб-интерфейс Strapi для добавления, удаления или изменения контента, который может быть предоставлен с помощью REST API или запроса GraphQL. После извлечения контента вы можете отображать его с помощью Angular, Vue, React или любой технологии или темы внешнего интерфейса для рендеринга контента в Strapi CMS. Думайте о Strapi как о логике, построенной на данных и контенте. Strapi без проблем работает с методологией создания веб-сайтов Jamstack и избавляет от хлопот, связанных с работой с данными.

Strapi также может быть полезен, если вы используете генератор статических сайтов с открытым исходным кодом, такой как Hugo, или вы можете использовать его для обработки абстракции контента, пока вы фокусируетесь на логике JavaScript своего веб-сайта. Это может быть особенно удобно в мире, где почти семьдесят процентов веб-сайтов работают на монолитном решении для управления контентом, таком как Wordpress.

Что такое плагины Strapi?

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

Архитектуру плагинов в Strapi легко объяснить, но гораздо сложнее кодировать. Безголовая CMS означает, что у вас есть репозиторий и логика, построенная вокруг контента, но вы можете свободно выбирать, как вы его используете, поскольку конечные точки API доступны для интеграции с вашим уникальным вариантом использования.

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

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

Создание плагина Strapi Analytics

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

Плагины находятся внутри каталога проекта sec/plugins, поэтому очень важно знать имена файлов, имена каталогов и другие соглашения.

Вот пример файловой структуры плагина с именем analytics:

cd src/plugins
tree
└── analytics
     ├── admin
     │   └── src
     │       ├── components
     │       │   ├── Initializer
     │       │   │   └── index.js
     │       │   └── PluginIcon
     │       │       └── index.js
     │       ├── index.js
     │       ├── pages
     │       │   ├── App
     │       │   │   └── index.js
     │       │   └── HomePage
     │       │       └── index.js
     │       ├── pluginId.js
     │       ├── translations
     │       │   ├── en.json
     │       │   └── fr.json
     │       └── utils
     │           ├── axiosInstance.js
     │           └── getTrad.js
     ├── package.json
     ├── README.md
     ├── server
     │   ├── bootstrap.js
     │   ├── config
     │   │   └── index.js
     │   ├── content-types
     │   │   └── index.js
     │   ├── controllers
     │   │   ├── index.js
     │   │   └── my-controller.js
     │   ├── destroy.js
     │   ├── index.js
     │   ├── middlewares
     │   │   └── index.js
     │   ├── policies
     │   │   └── index.js
     │   ├── register.js
     │   ├── routes
     │   │   └── index.js
     │   └── services
     │       ├── index.js
     │       └── my-service.js
     ├── strapi-admin.js
     └── strapi-server.js
 
 19 directories, 27 files

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

Каталоги server и admin предназначены для хранения бэкенда и внешнего интерфейса плагина соответственно. Поскольку Strapi безголовый, то есть нет внешнего интерфейса, мы говорим об административном интерфейсе Strapi. Интерфейс вашего веб-сайта совершенно другой, и Strapi не заботится об этом.

Обратите внимание, что strapi-admin.js и strapi-server.js не нужно трогать или изменять. Все, что нам нужно сделать, это изменить файл admin/pages/HomePage/index.js для круговой диаграммы и гистограммы.

Мы также должны изменить значок плагина, чтобы он соответствовал системе дизайна Strapi; это улучшает внешний вид нашего плагина, придавая ему уникальный внешний вид. Он также служит мнемоникой для пользователей, чтобы понять, о чем ваш плагин.

Пошаговые инструкции по созданию плагина аналитики в Strapi

Вы начинаете с настройки проекта Strapi с помощью команды:

yarn create strapi-app strapi-graphing --quickstart

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

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

Вам нужно создать два типа коллекций: Budget и Population. Перейдите в Плагины › Конструктор типов контента и нажмите Создать новый тип коллекции. Введите Budget в качестве отображаемого имени и добавьте два поля:

  1. Country: текстовое поле
  2. Spending: поле с плавающей запятой

Сохраните эту коллекцию и добавьте вторую коллекцию под названием Population с текстовым полем State и полем с плавающей запятой Percentage.

Вы можете проверить конечную точку API:

curl https://localhost:1337/api/budgets

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

Чтобы исправить это, перейдите в «Настройки» > «Плагин пользователя и разрешений» > «Роли» и отредактируйте роль Public. Здесь установите флажки рядом с действиями find и findOne для действий Budget и Population.

Затем вы должны создать плагин. Один для этого урока называется analytics.

Выполните команду yarn strapi generate в каталоге strapi-graphing и выберите плагин, как показано на скриншоте ниже.

Цель подключаемого модуля аналитики, который мы разрабатываем, — показать, как можно использовать Strapi CMS для аналитики контента в CMS. Как правило, контент для веб-сайтов используется для отображения текста и мультимедиа, но в нашем случае мы хотим показать некоторые графики и диаграммы для объяснения как часть веб-страницы.

Вы должны включить плагин, прежде чем его можно будет использовать. Создайте configs/plugins.js:

module.exports = {
  'analytics': {
    enabled: true,
    resolve: './src/plugins/analytics'
  },
}

Изменение значка плагина

Файл src/plugins/analytics/admin/src/components/PluginIcon/index.js можно использовать для изменения значка плагина. Но сначала вам нужно установить модуль @strapi/icons npm. Перейдите в домашний каталог плагина:

cd src/plugins/analytics

и добавьте модуль:

yarn add @strapi/icons

Есть большой выбор иконок. В данном случае значок эквалайзера оказался подходящим для подключаемого модуля аналитики. Поэтому вы должны изменить admin/src/components/PluginIcon/index.js на следующее:

/**
     *
     * PluginIcon
     *
     */
    
    import React from 'react';
    import Equalizer from '@strapi/icons/Equalizer';
    
    const PluginIcon = () => <Equalizer />;
    
    export default PluginIcon;

Теперь вы должны увидеть плагин на панели инструментов.

Исправьте домашнюю страницу плагина для отображения графиков и аналитики

Теперь пришло время написать ядро ​​плагина. Плагин будет создавать визуализации на основе данных в CMS. Во-первых, вам нужно установить библиотеки d3 и react-faux-dom. Убедитесь, что вы находитесь в домашнем каталоге плагина, и запустите:

yarn add d3 d3-react react-faux-dom

Вам нужно будет внести некоторые изменения в файл admin/src/pages/HomePage/index.js. Первый шаг — импортировать модули и создать компонент:

import PropTypes from 'prop-types';
import pluginId from '../../pluginId';
import * as d3 from "d3";
import React, {
  Component
} from 'react';
import axios from 'axios'
import {
  Element
} from 'react-faux-dom';
class App extends Component {
}
export default App

Внутри компонента у вас будут состояния для хранения данных для диаграмм:

…
class App extends Component {
  state = {
    pieChartData: [],
    barChartData: [],
  }
}

Метод plotBarChart будет рисовать гистограмму на основе типа Budget:

plotBarChart(chart, width, height) {
      // create scales!
      const xScale = d3.scaleBand()
          .domain(this.state.barChartData.map(d => d.attributes.Country))
          .range([0, width]);
      const yScale = d3.scaleLinear()
          .domain([0, d3.max(this.state.barChartData, d => d.attributes.Spending)])
          .range([height, 0]);
      const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
      chart.selectAll('.bar')
          .data(this.state.barChartData)
          .enter()
          .append('rect')
          .classed('bar', true)
          .attr('x', d => xScale(d.attributes.Country))
          .attr('y', d => yScale(d.attributes.Spending))
          .attr('height', d => (height - yScale(d.attributes.Spending)))
          .attr('width', d => xScale.bandwidth())
          .style('fill', (d, i) => colorScale(i));
      chart.selectAll('.bar-label')
          .data(this.state.barChartData)
          .enter()
          .append('text')
          .classed('bar-label', true)
          .attr('x', d => xScale(d.attributes.Country) + xScale.bandwidth() / 2)
          .attr('dx', -6)
          .attr('y', d => yScale(d.attributes.Spending))
          .attr('dy', -6)
          .attr("fill", "#fff")
          .text(d => d.attributes.Spending);
      const xAxis = d3.axisBottom()
          .scale(xScale);
      chart.append('g')
          .classed('x axis', true)
          .attr('transform', `translate(0,${height})`)
          .attr("color", "#fff")
          .call(xAxis);
      const yAxis = d3.axisLeft()
          .ticks(5)
          .scale(yScale);
      chart.append('g')
          .classed('y axis', true)
          .attr('transform', 'translate(0,0)')
          .attr("color", "#fff")
          .call(yAxis);
      chart.select('.x.axis')
          .append('text')
          .attr('x', width / 2)
          .attr('y', 60)
          .attr('fill', '#fff')
          .style('font-size', '20px')
          .style('text-anchor', 'middle')
          .text('Country');
      chart.select('.y.axis')
          .append('text')
          .attr('x', 0)
          .attr('y', 0)
          .attr('transform', `translate(-50, ${height/2}) rotate(-90)`)
          .attr('fill', '#fff')
          .style('font-size', '20px')
          .style('text-anchor', 'middle')
          .text('Spending in Billion Dollars');
      const yGridlines = d3.axisLeft()
          .scale(yScale)
          .ticks(5)
          .tickSize(-width, 0, 0)
          .tickFormat('')
      chart.append('g')
          .call(yGridlines)
          .classed('gridline', true);
  }

Метод plotPieChart будет рисовать круговую диаграмму на основе типа Population:

plotPieChart(chart, width, height) {
    const radius = Math.min(width, height) / 2;

    const g = chart
        .append("g")
        .attr("transform", `translate(${width / 2}, ${height / 2})`);
    const color = d3.scaleOrdinal(["gray", "green", "brown"]);
    const pie = d3.pie().value(function(d) {
        return d.attributes.Percentage;
    });
    const path = d3
        .arc()
        .outerRadius(radius - 10)
        .innerRadius(0);
    const label = d3
        .arc()
        .outerRadius(radius)
        .innerRadius(radius - 80);
    const arc = g
        .selectAll(".arc")
        .data(pie(this.state.pieChartData))
        .enter()
        .append("g")
        .attr("class", "arc");
    arc
        .append("path")
        .attr("d", path)
        .attr("fill", function(d) {
            return color(d.data.attributes.State);
        });
    arc
        .append("text")
        .attr("fill", "#fff")
        .attr("font-size", "20px")
        .attr("transform", function(d) {
            return `translate(${label.centroid(d)})`;
        })
        .text(function(d) {
            return d.data.attributes.State;
        });
    chart
        .append("g")
        .attr("transform", `translate(${width / 2 - 120},10)`)
        .append("text")
        .text("Top populated states in the US")
        .attr("fill", "#fff")
        .attr("class", "title");
  }

Метод drawChart будет удобной оболочкой двух предыдущих методов:

drawChart() {
    const width = 400;
    const height = 450;
    const el = new Element('div');
    const margin = {
        top: 60,
        bottom: 100,
        left: 80,
        right: 40
    };
    const svg = d3.select(el)
        .append('svg')
        .attr('id', 'barchart')
        .attr('width', width)
        .attr('height', height);

    const barchart = svg.append('g')
        .classed('display', true)
        .attr('transform', `translate(${margin.left},${margin.top})`);
    const margin2 = {
        top: 60,
        bottom: 100,
        left: 80,
        right: 40
    };
    const svg2 = d3.select(el)
        .append('svg')
        .attr('id', 'piechart')
        .attr('width', width)
        .attr('height', height);

    const piechart = svg2.append('g')
        .classed('display', true)
        .attr('transform', `translate(${margin2.left},${margin2.top})`);

    const chartWidth = width - margin.left - margin.right;
    const chartHeight = height - margin.top - margin.bottom
    const chartWidth2 = width - margin2.left - margin2.right;
    const chartHeight2 = height - margin2.top - margin2.bottom
    this.plotBarChart(barchart, chartWidth, chartHeight);
    this.plotPieChart(piechart, chartWidth2, chartHeight2);
    return el.toReact();
  }

Наконец, когда компонент смонтирован, вы отобразите диаграммы:

componentDidMount() {
      axios.get('https://localhost:1337/api/budgets').then((response) => {
          console.log(response.data.data);
          const barChartData = response.data.data;
          this.setState({
              barChartData
          });
      }, (error) => {
          console.log("No data seen at endpoint");
          console.log(error);
      });
      axios.get('https://localhost:1337/api/populations').then((response) => {
          console.log(response.data.data);
          const pieChartData = response.data.data;
          this.setState({
              pieChartData
          });
      }, (error) => {
          console.log("No data seen at endpoint");
          console.log(error);
      });
  }

  render() {
      return this.drawChart();
  }

Рекомендуется потратить некоторое время на настройку стиля и внешнего вида диаграмм, например, изменить цвет текста, если вы используете светлую тему.

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

Соберите плагин и запустите сервер:

yarn build && yarn develop

В диспетчере контента добавьте несколько образцов в Budget и Population. Затем посетите страницу analytics, чтобы увидеть диаграммы.

Разработка подключаемого модуля Strapi требует обширных базовых знаний о том, как работает REST API, что такое Axios, синтаксиса React и, в данном случае, также немного графического представления d3.

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

Заключение

Strapi — очень полезная библиотека, которую можно добавить на свой веб-сайт или веб-приложение, поскольку вы можете использовать ее богатое управление содержимым, хранение и доступ, не беспокоясь о конечных точках базы данных или API. Тот факт, что Strapi поддерживает конечные точки GraphQL и REST для доступа к контенту, позволяет беспрепятственно интегрировать его с остальной частью вашей бизнес-логики. Экосистема подключаемых модулей добавляет разнообразия.

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

Наконец, вы можете найти код, который мы использовали в этой статье о построении графиков Strapi, в этом репозитории GitHub.