Вопрос или проблема
Я работаю над порталом для внутренних операций клиента, и сталкиваюсь с проблемой фильтрации контента для конкретных пользователей. Вот детали:
Я создал пользовательский тип записи (CPT) для магазинов с использованием Advanced Custom Fields (ACF). В базе данных более 3000 магазинов.
Каждому магазину назначен конкретный торговый представитель (пользователь с ролью “sales-rep”). Я достиг этого, создав пользовательский плагин, который позволяет назначать магазины пользователям и ограничивать доступ на основе этих назначений.
Код плагина:
if (!defined('ABSPATH')) {
exit;
}
// Регистрация метабокса для назначения пользователей-торговых представителей
add_action('add_meta_boxes', 'mkusers_add_metabox');
function mkusers_add_metabox() {
add_meta_box(
'mkusers_access_control',
'Назначить торговых представителей',
'mkusers_render_metabox',
'customer',
'side',
'default'
);
}
function mkusers_render_metabox($post) {
$assigned_users = get_post_meta($post->ID, '_mkusers_assigned_users', true) ?: [];
$sales_reps = get_users(['role' => 'sales-rep']);
wp_nonce_field('mkusers_save_metabox', 'mkusers_nonce');
echo '';
foreach ($sales_reps as $user) {
$checked = in_array($user->ID, $assigned_users) ? 'checked' : '';
echo '
';
}
echo '';
}
// Сохранение данных метабокса
add_action('save_post', 'mkusers_save_metabox');
function mkusers_save_metabox($post_id) {
if (!isset($_POST['mkusers_nonce']) || !wp_verify_nonce($_POST['mkusers_nonce'], 'mkusers_save_metabox')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (isset($_POST['mkusers_assigned_users'])) {
$assigned_users = array_map('intval', $_POST['mkusers_assigned_users']);
update_post_meta($post_id, '_mkusers_assigned_users', $assigned_users);
} else {
delete_post_meta($post_id, '_mkusers_assigned_users');
}
}
// Добавление полей быстрой и массовой правки
add_action('quick_edit_custom_box', 'mkusers_quick_edit_box', 10, 2);
add_action('bulk_edit_custom_box', 'mkusers_bulk_edit_box', 10, 2);
function mkusers_quick_edit_box($column_name, $post_type) {
if ($post_type !== 'customer' || $column_name !== 'assigned_users') {
return;
}
$sales_reps = get_users(['role' => 'sales-rep']);
echo '';
}
function mkusers_bulk_edit_box($column_name, $post_type) {
if ($post_type !== 'customer') {
return;
}
mkusers_quick_edit_box($column_name, $post_type);
}
// Сохранение данных быстрой и массовой правки
add_action('save_post_customer', 'mkusers_save_quick_edit');
function mkusers_save_quick_edit($post_id) {
if (isset($_POST['mkusers_quick_edit_assigned_users'])) {
$assigned_users = array_map('intval', $_POST['mkusers_quick_edit_assigned_users']);
update_post_meta($post_id, '_mkusers_assigned_users', $assigned_users);
}
}
// Обработка данных массовой правки
add_action('wp_ajax_mkusers_bulk_edit', 'mkusers_handle_bulk_edit');
function mkusers_handle_bulk_edit() {
if (!isset($_POST['post_ids'], $_POST['assigned_users'])) {
wp_send_json_error('Недопустимый ввод');
}
$post_ids = array_map('intval', $_POST['post_ids']);
$assigned_users = array_map('intval', $_POST['assigned_users']);
foreach ($post_ids as $post_id) {
if (!empty($assigned_users)) {
update_post_meta($post_id, '_mkusers_assigned_users', $assigned_users);
} else {
delete_post_meta($post_id, '_mkusers_assigned_users');
}
}
wp_send_json_success();
}
// Добавление колонки назначенных пользователей в список CPT покупателей
add_filter('manage_customer_posts_columns', 'mkusers_add_assigned_users_column');
function mkusers_add_assigned_users_column($columns) {
$columns['assigned_users'] = 'Назначенные торговые представители';
return $columns;
}
// Заполнение колонки назначенных пользователей
add_action('manage_customer_posts_custom_column', 'mkusers_display_assigned_users_column', 10, 2);
function mkusers_display_assigned_users_column($column_name, $post_id) {
if ($column_name === 'assigned_users') {
$assigned_users = get_post_meta($post_id, '_mkusers_assigned_users', true) ?: [];
$user_names = array_map(function ($user_id) {
$user = get_user_by('id', $user_id);
return $user ? $user->display_name : '';
}, $assigned_users);
echo esc_html(implode(', ', $user_names));
}
}
// Ограничение доступа для не назначенных пользователей
add_action('template_redirect', 'mkusers_restrict_access');
function mkusers_restrict_access() {
if (is_singular('customer') && !current_user_can('edit_posts')) {
global $post;
$assigned_users = get_post_meta($post->ID, '_mkusers_assigned_users', true) ?: [];
if (!in_array(get_current_user_id(), $assigned_users) && !current_user_can('administrator')) {
wp_redirect('https://mckenzieportal.com/access-restricted/');
exit;
}
}
}
// Подключение скрипта быстрой и массовой правки
add_action('admin_enqueue_scripts', 'mkusers_enqueue_quick_edit_script');
function mkusers_enqueue_quick_edit_script($hook) {
if ('edit.php' === $hook && get_current_screen()->post_type === 'customer') {
wp_enqueue_script(
'mkusers-quick-edit',
plugins_url('mkusers-quick-edit.js', __FILE__),
['jquery'],
'1.4',
true
);
}
}
Например: торговый представитель с именем пользователя 14A имеет 80 магазинов, назначенных на него. Каждый торговый представитель может просматривать только свои назначенные магазины.
Если торговый представитель попытается получить доступ к магазину, к которому он не назначен, его перенаправляют на страницу с ограниченным доступом (эта часть работает нормально).
Как отображаются магазины: Я использую виджет Loop Grid от Elementor на странице под названием Dashboard для отображения всех магазинов.
Проблема в том, что Grid Loop в настоящее время показывает все магазины для всех пользователей. Мне нужно, чтобы он динамически фильтровал и отображал только магазины, назначенные вошедшему в систему торговому представителю.
Что я пытался сделать: Я пытался модифицировать запрос Elementor с использованием PHP (через хуки, такие как elementor/query/*), чтобы фильтровать магазины на основе вошедшего пользователя.
Хотя логика кажется правильной, ни одно решение пока не сработало. Либо магазины не отображаются, либо возникают ошибки (например, неопределенные функции или проблемы с запросами).
Цель: На странице Dashboard:
Когда торговый представитель входит в систему, он должен видеть только магазины, назначенные ему в Loop Grid от Elementor.
Решение должно работать без проблем с системой запросов Elementor.
Я был бы очень признателен за любую помощь, предложения кода или идеи по поводу того, как это правильно реализовать. Спасибо!
Ответ или решение
Для решения задачи фильтрации отображения магазинов в Elementor Loop Grid по пользователям, вам потребуется применить несколько техник, основанных на предоставленном коде и функционале WordPress. Я опишу шаги и предложу код, который поможет вам отфильтровать магазины таким образом, чтобы каждый торговый представитель видел только свои назначенные магазины.
1. Описание проблемы
Вы создали пользовательский тип записи (CPT) для магазинов и используете Advanced Custom Fields (ACF) для назначения их определенным торговым представителям. У вас уже настроен механизм, который ограничивает доступ к записям на основе назначений с помощью метаданных. Однако Elementor Loop Grid в данный момент отображает все магазины, и вам необходимо его отфильтровать, чтобы показывать только те магазины, которые назначены текущему пользователю.
2. Решение проблемы
Для начала вам нужно использовать хук elementor/query/selector
для настройки запроса, который будет использоваться плагином Elementor. Комплексный код ниже добавит необходимые фильтры к запросу.
3. Код для фильтрации магазинов
Добавьте следующий код в файл functions.php
вашей темы или в ваш плагин:
// Фильтрация запросов Elementor для Loop Grid
add_action('elementor/query/assigned_stores', function ($query) {
if (!is_user_logged_in()) {
return; // Проверка на вход пользователя
}
// Получение идентификатора текущего пользователя
$current_user_id = get_current_user_id();
// Получение магазинов, назначенных текущему пользователю
$args = array(
'post_type' => 'customer', // Убедитесь, что ваш CPT называется 'customer'
'meta_query' => array(
array(
'key' => '_mkusers_assigned_users',
'value' => '"' . $current_user_id . '"',
'compare' => 'LIKE'
)
)
);
// Изменение основного запроса Elementor
$query->set('meta_query', $args['meta_query']);
});
4. Объяснение кода
- Проверка входа: Обеспечиваем, чтобы функция выполнялась только для вошедших пользователей.
- Получение ID пользователя: ID текущего пользователя извлекается с помощью
get_current_user_id()
. - Meta Query: Создается массив
args
с мета-запросом, который проверяет, есть ли текущий пользователь в массиве назначенных пользователей для каждого магазина. Это достигается с помощьюLIKE
для поиска в массиве. - Модификация запроса: Мы устанавливаем наш мета-запрос в запрос Elementor с помощью
$query->set()
.
5. Применение в Elementor
Теперь, чтобы данный фильтр заработал, вам нужно сделать следующее в Elementor:
- Откройте вашу страницу с Loop Grid.
- В разделе "Query" выберите "Кастомный запрос" и введите название вашего запроса (например,
assigned_stores
). - Сохраните изменения и проверьте результат.
6. Заключение
С помощью предоставленного кода вы достигнете желаемой цели, и каждый торговый представитель сможет видеть только те магазины, которые были назначены ему. Убедитесь, что ваш тип записи и метаданные настроены корректно, чтобы избежать возможных ошибок в запросах.
Если у вас возникнут вопросы или потребуется помощь с доработкой, не стесняйтесь обращаться. Успехов в реализации вашего проекта!