Вопрос или проблема
Предыстория
Мне нужно получить термины для сообщения на произвольном сайте в сети мультисайтов. Это не обязательно сайт, на котором в данный момент работает экземпляр WP. wp_get_object_terms()
требует таксономии, для которой нужно получить термины. Поэтому, чтобы получить все термины для всех таксономий, нужно сначала получить таксономии. Это кажется невозможным, так как таксономии, которые регистрируются плагинами, могут не регистрироваться на текущем (оригинальном) сайте, на котором запускается экземпляр, поскольку сами плагины не будут активны на этом сайте.
Вопрос
Как я могу получить все таксономии произвольного сайта программным способом? Другими словами, имея сайт $siteId
, как я могу получить все таксономии на этом сайте?
Или же, как я могу получить все термины таксономий для сообщения на произвольном сайте программным способом? Другими словами, имея сообщение $post
и сайт $siteId
, как я могу получить все термины для всех таксономий этого сообщения на этом сайте?
как я могу получить все термины таксономий для сообщения на произвольном сайте программным способом? Другими словами, имея сообщение $post и сайт $siteId, как я могу получить все термины для всех таксономий этого сообщения на этом сайте?
Вы можете сделать это, исключив таксономию из WP_Term_Query
:
$query = new WP_Term_Query( [
'object_ids' => [ $post_id ]
] );
$terms = $query->get_terms();
Обратите внимание, что это не дает вам информации о том, какие таксономии были зарегистрированы с этими терминами, поэтому постоянные ссылки и метки могут быть недоступны
Как я могу получить все таксономии произвольного сайта? Другими словами, имея сайт $siteId, как я могу получить все таксономии на этом сайте?
К сожалению, нет, в PHP нет API или вызова функции, которые предоставят вам эту информацию.
Как правило, необходимость в этой информации является признаком того, что что-то пошло не так на уровне архитектуры высокого уровня, и существуют более простые и легкие решения. Каждое решение имеет компромиссы и встроенные ограничения, и это одна из тех ситуаций, когда нарушение ограничения требует компромисса или изменения архитектуры.
Корень проблемы в том, что switch_to_blog
изменяет таблицы и глобальные переменные, но не загружает код с другого блога, к которому переключаются, поэтому нам нужно восстановить, какие зарегистрированные типы сообщений и таксономий имеются на этом сайте.
Варианты обхода проблемы
Тем не менее, существуют обходные пути, какой из них лучше для вас, будет зависеть от нескольких факторов, и каждый имеет свои преимущества и недостатки.
- Получение всех терминов и изучение их поля таксономии
- WP CLI
- REST API
- Специальные REST API эндпоинты
- Перемещение данных
- Единые таксономии с различной видимостью
- Подготовка данных заранее
Все это обходные пути, которые пытаются учесть особый случай, о котором вы спрашиваете.
Из всех этих способов только WP CLI надежно предоставляет всю необходимую информацию без проблем с производительностью и масштабируемостью. Независимо от используемого метода, кэшируйте результат.
Получение всех терминов
Мы можем получить все термины на сайте, а затем пройтись по ним, чтобы определить поля taxonomy
:
$query = new WP_Term_Query([]);
$terms = $query->get_terms();
$taxonomies = wp_list_pluck($terms, 'taxonomy');
$taxonomies = array_unique($taxonomies);
Однако, существуют значительные проблемы с этим:
- это не масштабируется, извлечение всех терминов в память требует времени и ресурсов, в итоге сайт будет иметь больше данных, чем можно удержать, что приведет к превышению лимита времени выполнения или исчерпанию памяти
- показывает только используемые таксономии, если нет категорий, то таксономия категории не будет отображаться
- это не дает вам информации о том, какие таксономии были зарегистрированы с этими терминами, поэтому постоянные ссылки и метки могут быть недоступны
WP CLI
WP CLI может предоставить вам именно то, что нужно для обоих ваших вопросов, если он доступен. Собирая команду и вызывая её из PHP, вы можете программно получить желаемые данные, избегая HTTP-запроса.
- мы вызываем WP CLI через
exec
илиproc_open
- мы указываем, какой сайт нам нужен через параметр
--url
, URL можно получить через стандартный API вызов, такой какget_site_url($site_id)
- мы добавляем параметр
--format
для того, чтобы WP CLI предоставил нам машину читаемые результаты, либо--format="csv"
для строки CSV, которую могут обработать функции такие какfgetcsv
и др., или--format="json"
, что может быть обработаноjson_decode
. JSON будет проще - мы обрабатываем результат в PHP
Например, вот мой локальный экземпляр сайта:
vagrant@vvv:/srv/www/tomjn/public_html$ wp taxonomy list
+----------------+------------------+-------------+---------------+---------------+--------------+--------+
| name | label | description | object_type | show_tagcloud | hierarchical | public |
+----------------+------------------+-------------+---------------+---------------+--------------+--------+
| category | Categories | | post | 1 | 1 | 1 |
| post_tag | Tags | | post | 1 | | 1 |
| nav_menu | Navigation Menus | | nav_menu_item | | | |
| link_category | Link Categories | | link | 1 | | |
| post_format | Formats | | post | | | 1 |
| technology | Technologies | | project | 1 | | 1 |
| tomjn_talk_tag | Talk Tags | | tomjn_talks | 1 | | 1 |
| series | Series | | post | | | 1 |
+----------------+------------------+-------------+---------------+---------------+--------------+--------+
Я также могу передать --format=json
или --format=csv
для получения машиночитаемого результата.
Я могу нацелиться на отдельные сайты в установке мультисайта, передав параметр --url
например:
wp taxonomy list --url="https://example.com/" --format="json"
Я могу затем вызвать это из PHP и разобрать результат, чтобы получить список таксономий.
То же самое касается и терминов, например, вот пример вывода:
❯ wp term list category
+---------+------------------+-----------------+-----------------------+-------------+--------+-------+
| term_id | term_taxonomy_id | name | slug | description | parent | count |
+---------+------------------+-----------------+-----------------------+-------------+--------+-------+
| 129 | 136 | Auto-Aggregated | auto-aggregated | | 0 | 1 |
| 245 | 276 | Big WP | big-wp | | 4 | 0 |
| 95 | 100 | CSS | css | | 7 | 1 |
| 7 | 7 | Design | design | | 0 | 3 |
| 30 | 32 | Development | development | | 17 | 31 |
...
Или категории для сообщений:
❯ wp post term list 15 category --format=csv
term_id,name,slug,taxonomy
5,WordCamp,wordcamp,category
Убедитесь, что задан правильный рабочий путь, и что wp
установлено, доступно и исполняется.
Будьте осторожны, если вы запросите у WP CLI каждый пост в базе данных, он предоставит их все, даже если это займет 1 час. Ваш запрос истечет по времени задолго до этого, что приведет к ошибке. Поэтому всегда указывайте верхние пределы на то, сколько постов вы хотите, даже если не ожидаете их достичь. Кроме того, возможно запросить больше данных, чем сервер может уместить в памяти, WP CLI в этих случаях завершится с ошибкой из-за недостатка памяти. Например, импортирование 5 ГБ wxr импортного файла или запрос 10k постов.
Однако, если WP CLI недоступен…
REST API
Вы можете запросить таксономии через REST API. Все сайты его поддерживают, но имеется 2 условия:
- HTTP-запросы дорогие, и вы не можете обойти эту стоимость функцией PHP, так как нужно загрузить WP с нуля, чтобы получить зарегистрированные таксономии и типы сообщения, которые вам нужны
- Частные и скрытые таксономии не будут показаны, некоторые таксономии будут показаны только с аутентификацией, поэтому потребуется добавить плагин аутентификации
Но если вас это устраивает, вы можете использовать встроенное обнаружение для поиска типов сообщений и таксономий.
Перейдите на example.com/wp-json/wp/v2/taxonomies
и получите JSON-список публичных таксономий, каждый объект в списке имеет подполе types
, которое перечисляет типы сообщений, к которым он относится.
Вы также можете получить сообщение таким образом через конечную точку /wp/v2/posts
, но вы получите обратно IDs терминов, а не их имена, что, вероятно, потребует дополнительных запросов.
Специальный эндпоинт
Вы можете попытаться обойти ограничения выше, создав специальный эндпоинт. Это позволит вам вернуть все, что нужно, с помощью одного запроса.
Для этого вызовите register_rest_route
для регистрации эндпоинта и верните массив с ключами и значениями из функции обратного вызова. API закодирует их как JSON
Перемещение данных
Вместо получения этой информации с других сайтов в установке мультисайта, гораздо более эффективно иметь эти сайты, передающие информацию вам, и кешировать результат. Это самый производительный метод.
Единые таксономии с различной видимостью
Некоторые сайты могут воспользоваться этой возможностью. Если на всех сайтах зарегистрированы одинаковые таксономии, вы можете запросить все термины, переключаясь между блогами, независимо от сайтов. Но никто не сказал, что таксономии должны иметь одинаковые опции
Например, сайт A имеет таксономию категории, и сайт B имеет таксономию тегов. A не использует теги, и B не использует категории. Поэтому мы регистрируем обе таксономии на обоих сайтах, но на A делаем теги скрытой частной таксономией, а на B делаем категории скрытой частной таксономией.
Эта альтернатива не подходит для всех ситуаций и не может учитывать таксономии, зарегистрированные сторонними плагинами
Подготовка данных заранее
Мы не знаем, какие таксономии и термины для каждого сообщения зарегистрированы на других сайтах, потому что не загрузили код этого сайта. Однако этот сайт знает. Так почему бы не заставить сайт сохранять эту информацию где-то, откуда её можно будет получить?
Например, сайт может хранить в опциях зарегистрированные таксономии и их атрибуты.
То же можно сделать с терминами сообщений, поле мета-сообщения можно обновить на хуке сохранения, чтобы содержать список терминов с их именами, URL и таксономией, к которой они относятся, чтобы список терминов мог быть создан в другом месте без необходимости знания зарегистрированных таксономий.
Используйте WP_Term_Query
без параметра taxonomy
. Несмотря на то, что wp_get_object_terms()
странно не позволяет опустить параметр taxonomy
, WP_Term_Query
, используемый под капотом, это делает.
function getPostTerms(int $postId, int $siteId): array
{
switch_to_blog($siteId);
$query = new WP_Term_Query(['object_ids' => [$postId]]);
$terms = $query->get_terms();
restore_current_blog();
/* @var $terms WP_Term[] */
return $terms;
}
Это возвращает все термины для $postId
. Дисклеймер: возвращаются только термины, ассоциированные хотя бы с одним сообщением.
Отсюда можно пройтись по терминам и получить их таксономию, если цель состоит в том, чтобы перечислить все таксономии:
function getAllTaxonomies(int $siteId): array
{
switch_to_blog($siteId);
$query = new WP_Term_Query([]);
$terms = $query->get_terms();
$taxonomies = [];
foreach ($terms as $term) {
assert($term instanceof WP_Term);
$taxonomy = $term->taxonomy;
$taxonomies[$taxonomy] = true;
}
restore_current_blog();
$taxonomies = array_keys($taxonomies);
return $taxonomies;
}
Это позволяет получить все слаги таксономий для конкретного сайта. Дисклеймер: кажется, это возвращает только те таксономии, которые имеют хотя бы 1 сообщение, ассоциированное хотя бы с 1 термином. Это не самый производительный способ, так как придется проходить по всем терминам, но в большинстве случаев этого достаточно, так как обычно не больше дюжины терминов для таксономии.
Ограничения
- Невозможно получить термины, не ассоциированные с сообщениями. Следовательно, невозможно получить слаги таксономий, для которых нет терминов, ассоциированных хотя бы с одним сообщением.
- Невозможно получить объекты
WP_Taxonomy
для произвольного сайта, так как таксономии из плагинов, которые не работают на текущем сайте, не гарантировано будут зарегистрированы вообще.
.
Ответ или решение
Теория
В многосайтовой сети WordPress часто возникает необходимость управлять данными для различных сайтов, которые не являются текущими, в пределах которых работает экземпляр WordPress. Одной из таких задач является извлечение таксономий и терминов таксономий для произвольного сайта. Это может быть нужно для различных целей: от сбора аналитики до более сложных интеграций и автоматизаций контента.
Однако, в WordPress, таксономии могут быть зарегистрированы через плагины, которые могут быть отключены на текущем сайте, и поэтому данные таксономии могут быть недоступны для стандартных вызовов функции, таких как wp_get_object_terms()
.
Пример
Предположим, что у нас есть идентификатор сайта $siteId
и поста $postId
в многосайтовой установке WordPress. Задача состоит в том, чтобы программно извлекать все терминов для всех таксономий указанного поста на этом сайте. Одним из подходов к решению этой задачи является использование WP_Term_Query
, что позволяет извлекать термины без необходимости задания конкретной таксономии.
Также можно использовать командную строку WP CLI или встроенный REST API, чтобы извлечь таксономии и связанные термины. Это может быть более производительным и унифицированным способом управления данными в контексте многосайтовой сети, особенно если CLI или API уже настроены.
Применение
Для извлечения всех терминов поста в указанном сайте, можно использовать следующую функцию:
function getPostTerms(int $postId, int $siteId): array
{
switch_to_blog($siteId); // Переключаемся на указанный сайт
$query = new WP_Term_Query(['object_ids' => [$postId]]);
$terms = $query->get_terms(); // Извлечение всех терминов
restore_current_blog(); // Возвращаемся к исходному сайту
return $terms;
}
Однако, следует учесть, что данное решение не позволяет извлекать термины, не привязанные ни к одному посту.
Для извлечения всех таксономий сайта, можно использовать следующий подход:
function getAllTaxonomies(int $siteId): array
{
switch_to_blog($siteId); // Переключаемся на указанный сайт
$query = new WP_Term_Query([]);
$terms = $query->get_terms(); // Извлечение всех терминов
$taxonomies = [];
foreach ($terms as $term) {
$taxonomy = $term->taxonomy;
$taxonomies[$taxonomy] = true;
}
restore_current_blog(); // Возвращаемся к исходному сайту
return array_keys($taxonomies);
}
Несмотря на то, что данные функции имеют ограниченную производительность при большом объеме данных, они предоставляют простой метод извлечения базовой информации о терминах и таксономиях.
Преимущества использования WP CLI:
- Надежность: WP CLI предоставляет функции для прямого извлечения и управления таксономиями.
- Кросс-платформенность: этот подход будет работать в любой среде, где настроен WP CLI.
- Компактность: отлично подходит для автоматизации задач, где возможна интеграция командной строки.
Несмотря на свои преимущества, WP CLI может быть недоступен на некоторых серверах, поэтому стоит рассмотреть REST API как альтернативу. REST API позволяет извлекать данные через HTTP-запросы, что делает этот метод более универсальным, но может требовать аутентификации.
Ограничения и рекомендации
- Исполняемость: Ограничения памяти и времени выполнения могут стать проблемой при обработке большого объема данных.
- Полнота данных: Нельзя извлечь термины без постов, и таксономии, которые не активны на всех сайтах.
- Кеширование: Рекомендуется кешировать результаты для повышения производительности и уменьшения нагрузки на сервер.
- Распределенный управление данными: Рассмотрите вариант предварительной обработки данных и хранение результатов в централизованном месте для лучшего доступа.
Резюмируя, извлечение таксономий и терминов в многосайтовой архитектуре WordPress — задача нетривиальная, но решаемая с помощью грамотного выбора инструментов и подходов.