Создать блок Query Loop

Вопрос или проблема

Я создаю блок Query Loop, который извлекает тип записи, похожий на блок Гутенберга, но по различным причинам мне нужно создать свой.

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

В настоящее время они используют то, что указано в этом файле.

edit.js

/**
 * Зависимости WordPress
 */
import { __ } from "@wordpress/i18n";
import {
    useBlockProps,
    useInnerBlocksProps,
    store as blockEditorStore,
} from "@wordpress/block-editor";
import { useSelect } from "@wordpress/data";
import { store as coreStore } from "@wordpress/core-data";
import QueryInspectorControls from "./inspector-controls";
import React from "react";

/**
 * Компонент редактирования
 *
 * @param {Object} props
 * @param {Object} props.attributes
 * @param {Object} props.setAttributes
 * @param {string} props.clientId
 */
export default function Edit({ attributes, setAttributes, clientId }) {
    const { query } = attributes;
    const blockProps = useBlockProps();

    // Получение записей
    const { posts, isResolving, templateBlock } = useSelect(
        (select) => {
            const { getEntityRecords, isResolving } = select(coreStore);
            const { getBlocks } = select(blockEditorStore);

            // Поиск блока
            const innerBlocks = getBlocks(clientId);
            const template = innerBlocks.find(
                (block) => block.name === "prismatic/post-type-template",
            );

            return {
                posts: getEntityRecords("postType", query.postType, {
                    per_page: query.perPage,
                    offset: query.offset,
                    orderby: query.orderBy,
                    order: query.order,
                    _embed: true,
                    ...(query.authors?.length && {
                        author: query.authors.join(","),
                    }),
                    ...(query.search?.length && {
                        search: query.search.join(" "),
                    }),
                    ...(query.postType === "page" &&
                        query.parents?.length && {
                            parent: query.parents[0],
                        }),
                    ...(query.taxQuery && {
                        tax_query: Object.entries(query.taxQuery)
                            .filter(([key]) => !key.includes("_relation"))
                            .map(([taxonomy, terms]) => ({
                                taxonomy,
                                terms,
                                operator:
                                    query.taxQuery[`${taxonomy}_relation`] === "AND"
                                        ? "AND"
                                        : "IN",
                            })),
                    }),
                }),
                isResolving: isResolving("getEntityRecords", [
                    "postType",
                    query.postType,
                ]),
                templateBlock: template,
            };
        },
        [query, clientId],
    );

    // Шаблон
    const innerBlocksProps = useInnerBlocksProps(
        {
            style: { display: "none" },
        },
        {
            template: [["prismatic/post-type-template"]],
            allowedBlocks: ["prismatic/post-type-template"],
        },
    );

    // Отрисовка элемента
    function QueryPostTypeItem({ post, templateBlock }) {
        if (!templateBlock || !post) return null;

        return (
            <div
                className={templateBlock.attributes.className || ""}
                data-id={post.id}
            >
                {templateBlock.innerBlocks.map((block) => {
                    if (block.name === "prismatic/post-type-featured-image") {
                        const imageUrl =
                            post._embedded?.["wp:featuredmedia"]?.[0]?.source_url;
                        const imageAlt =
                            post._embedded?.["wp:featuredmedia"]?.[0]?.alt_text || "";

                        if (!imageUrl) return null;

                        return (
                            <img
                                key={block.clientId}
                                src={imageUrl}
                                alt={imageAlt}
                                className={block.attributes.className || ""}
                            />
                        );
                    }

                    if (block.name === "prismatic/post-type-title") {
                        const TagName = block.attributes.tagName || "p";
                        return (
                            <TagName
                                key={block.clientId}
                                className={block.attributes.className || ""}
                            >
                                {post.title.rendered}
                            </TagName>
                        );
                    }

                    if (block.name === "prismatic/post-type-excerpt") {
                        const excerpt = post.excerpt?.rendered || "";
                        const words = block.attributes.words || 25;
                        const strippedExcerpt = excerpt.replace(/<\/?[^>]+(>|$)/g, "");
                        const trimmedExcerpt =
                            strippedExcerpt.split(" ").slice(0, words).join(" ") + "...";

                        return (
                            <p
                                key={block.clientId}
                                className={block.attributes.className || ""}
                                dangerouslySetInnerHTML={{ __html: trimmedExcerpt }}
                            />
                        );
                    }

                    if (block.name === "prismatic/post-type-button") {
                        const text =
                            block.attributes.text || __("Leggi tutto", "prismatic");
                        return (
                            <a
                                key={block.clientId}
                                className={block.attributes.className || ""}
                                href={post.link}
                            >
                                {text}
                            </a>
                        );
                    }

                    return null;
                })}
            </div>
        );
    }

    // Цикл
    function QueryPostTypeLoop({ posts, templateBlock }) {
        if (!posts) return null;

        return (
            <>
                {posts.map((post) => (
                    <QueryPostTypeItem
                        key={post.id}
                        post={post}
                        templateBlock={templateBlock}
                    />
                ))}
            </>
        );
    }

    return (
        <>
            <QueryInspectorControls
                attributes={attributes}
                setAttributes={setAttributes}
            />

            <div {...blockProps}>
                {isResolving ? (
                    <p>{__("Загрузка...", "prismatic")}</p>
                ) : !posts?.length ? (
                    <p>{__("Нет контента.", "prismatic")}</p>
                ) : (
                    <>
                        <div {...innerBlocksProps} />

                        <QueryPostTypeLoop posts={posts} templateBlock={templateBlock} />
                    </>
                )}
            </div>
        </>
    );
}

Ответ или решение

Создание блока Query Loop в среде WordPress – задача, требующая внимания к деталям и хорошего понимания архитектуры блоков и API WordPress. Давайте рассмотрим принципы, используемые в вашем текущем решении, и посмотрим, как его можно улучшить, чтобы использовать отдельные файлы edit.js для внутренних блоков.

Теория

Блок Query Loop предназначен для динамического извлечения записей на основе заданных параметров, таких как тип поста, количество, порядок и фильтры по таксономиям. В вашем коде используется useSelect для извлечения данных из WordPress, что позволяет делать это на уровне клиента в React-компонентах. Основные зависимости включают в себя @wordpress/block-editor для работы с блоками и @wordpress/core-data для доступа к данным.

Текущая реализация интегрирует в себе и извлечение данных, и рендеринг внутреннего содержимого, используя блоки только из одного файла edit.js. Разделение этого на отдельные файлы кодов поможет достичь более модульного и простого в сопровождении кода.

Пример

Ваш текущий код использует useSelect для получения записей и шаблонов блоков, после чего проходит через каждый пост, рендеря его, используя внутренние блоки, такие как "prismatic/post-type-featured-image", "prismatic/post-type-title" и другие. Эти внутренние блоки извлекают и отображают конкретные свойства постов, в том числе изображения, заголовки и выдержки.

Сейчас все эти логики находятся в одном файле edit.js. Для улучшения, мы можем реализовать следующие модули:

  1. Получение данных: Остается в основном файле edit.js, отвечая за запросы API и управление состоянием на уровне компонента.

  2. Отображение данных: Разделите отображение отдельных блоков (например, заголовок, изображение) в их собственные компоненты, каждый с собственным файлом edit.js.

Пример нового компонента для блока отображения заголовка поста:

// file: post-type-title/edit.js
import { __ } from "@wordpress/i18n";
import { useBlockProps } from "@wordpress/block-editor";

export default function Edit({ attributes }) {
  const blockProps = useBlockProps();
  const TagName = attributes.tagName || "p";

  return (
    <TagName {...blockProps}>
      {attributes.title || __("Заголовок поста", "prismatic")}
    </TagName>
  );
}

Применение

При применении нового подхода, вам нужно:

  1. Создать отдельные компоненты для каждого элемента контента. Каждый блок, такой как изображение, заголовок и выдержка, должен получить свой собственный компонент редактирования, который только отвечает за корректное отображение информации.

  2. Архитектурное улучшение проекта. Организовать структуру проекта так, чтобы каждый блок находился в своей папке вместе с файлом edit.js, что упростит его модификацию и поддержку.

  3. Улучшение производительности. Разделение кода позволяет разбивать логические компоненты и, как следствие, повышает эффективность их загрузки в среде WordPress.

  4. Снижение зависимости компонентов. Изоляция кода конкретного блока предотвращает нежелательные взаимодействия с другими компонентами и делает повторное использование более удобным.

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

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

Капча загружается...