Раздел пользовательских атрибутов товара аукциона [Dokan и WooCommerce]

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

Я использую следующий код, который сразу же отображает предопределенные атрибуты продукта для товара аукциона Dokan с выпадающими меню (вместо того, чтобы пользователю вручную нажимать «Добавить атрибут»). Я также добавил два пользовательских раздела атрибутов: «Номер ссылки» и «Год выпуска», где пользователь сам вводит значения.

// Подключение Choices.js и его CSS
function enqueue_choices_js() {
    wp_enqueue_style('choices-css', 'https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css', array(), '1.0.0');
    wp_enqueue_script('choices-js', 'https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js', array('jquery'), '1.0.0', true);
}
add_action('wp_enqueue_scripts', 'enqueue_choices_js');

// Основная функция для обработки выпадающих списков с Choices.js, проверки собственности, номера ссылки и года выпуска
function dokan_predefined_attributes_with_choices_and_custom_fields_js() {
    global $post;
    $product_id = isset($post) ? $post->ID : 0;

    // Получение существующих значений для номера ссылки и года выпуска (если они есть)
    $existing_reference_number = $product_id ? get_post_meta($product_id, '_reference_number', true) : '';
    $existing_watch_year = $product_id ? get_post_meta($product_id, '_watch_year', true) : '';

    ?>
    <style>
        .dokan-attribute-obligatory {
            font-weight: bold;
            color: #d9534f;
        }
        .product-edit-new-container .dokan-attribute-variation-options .dokan-product-attribute-wrapper ul li.product-attribute-list .dokan-product-attribute-item {
            padding: 10px;
        }
        .choices[data-type*=select-one] .choices__button {
            margin-top: -20px;
            margin-right: 30px;
        }
        .dokan-attribute-missing {
            border: 1px solid #d9534f;
            padding: 10px;
            border-radius: 5px;
        }
        .ui-sortable-handle {
            touch-action: auto;
        }
        .dokan-btn-disabled {
            opacity: 0.5;
            pointer-events: none;
        }
        .ownership-confirmation {
            margin-bottom: 20px;
            padding: 10px;
            border: 2px solid #0a3d62;
            background-color: #f9f9f9;
            border-radius: 5px;
        }
        .ownership-confirmation h3 {
            margin-bottom: 10px;
            font-weight: bold;
            color: #0a3d62;
        }
        .choices__item--choice.is-highlighted {
            background-color: #0a3d62 !important;
            color: white !important;
        }
        /* Добавить отступ между атрибутами */
        .dokan-dashboard .dokan-dashboard-content ul li {
            margin-top: 15px;
            margin-bottom: 15px;
        }
        /* Красная звездочка для обязательных полей */
        .required-asterisk {
            color: #d9534f !important;
            font-weight: 700 !important;
        }
    </style>

    <script type="text/javascript">
    jQuery(document).ready(function($) {
        // Отслеживание состояния проверки собственности и статуса изображения продукта
        let ownershipPhotoUploaded = !!$('#ownership_photo_upload').val();
        let productImageUploaded = !!$('input.dokan-feat-image-id').val();

        // Список предопределенных атрибутов
        var predefined_attributes = [
            { slug: 'pa_brand', name: 'Бренд' },
            { slug: 'pa_condition', name: 'Состояние' },
            { slug: 'pa_box', name: 'Оригинальная коробка' },
            { slug: 'pa_papers', name: 'Оригинальные документы' },
            { slug: 'pa_case-size', name: 'Размер корпуса' },
            { slug: 'pa_material', name: 'Материал' },
            { slug: 'pa_dial-color', name: 'Цвет циферблата' }
        ];

        // Функция для загрузки сохраненных терминов атрибутов
        function getSavedAttributeValue(slug) {
            return $('[name="attribute_values_' + slug + '"]').val() || '';
        }

        function getAttributeTerms(attributeSlug, callback, selectedValue="") {
            $.ajax({
                url: '<?php echo admin_url('admin-ajax.php'); ?>',
                method: 'POST',
                dataType: 'json',
                data: {
                    action: 'get_wc_attribute_terms',
                    attribute_slug: attributeSlug
                },
                success: function(response) {
                    callback(response, selectedValue);
                },
                error: function() {
                    callback([], selectedValue);
                }
            });
        }

        function addPredefinedAttributesToList() {
            var $attributeList = $("ul.dokan-attribute-option-list");
            $attributeList.empty();

            $.each(predefined_attributes, function(index, attribute) {
                var savedValue = getSavedAttributeValue(attribute.slug);

                var attributeHtml = `
                    <li class="product-attribute-list taxonomy ${attribute.slug}" data-taxonomy="${attribute.slug}">
                        <div class="dokan-product-attribute-heading">
                            <span><i class="fas fa-bars" aria-hidden="true"></i>&nbsp;&nbsp;<strong>${attribute.name}</strong><span class="required-asterisk"> *</span></span>
                        </div>

                        <div class="dokan-product-attribute-item dokan-clearfix">
                            <div class="content-half-part">
                                <label class="form-label" for="">Название</label>
                                <strong>${attribute.name}</strong>
                                <input type="hidden" name="attribute_names[]" value="${attribute.slug}">
                                <input type="hidden" name="attribute_position[]" class="attribute_position" value="0">
                                <input type="hidden" name="attribute_is_taxonomy[]" value="1">
                                <label class="checkbox-item form-label">
                                    <input type="checkbox" checked="checked" name="attribute_visibility[]" value="1"> Видимо на странице товара
                                </label>
                            </div>

                            <div class="content-half-part dokan-attribute-values">
                                <label for="" class="form-label">Значение(я)</label>
                                <select style="width:100%;" class="dokan_attribute_values choices__input" name="attribute_values[${index}][]">
                                    <option value="">Загрузка...</option>
                                </select>
                            </div>
                        </div>
                    </li>
                `;

                $attributeList.append(attributeHtml);

                getAttributeTerms(attribute.slug, function(values, selectedValue) {
                    var optionsHtml="<option value="">Выберите значение</option>";
                    $.each(values, function(i, value) {
                        var selected = (value === selectedValue) ? 'selected' : '';
                        optionsHtml += '<option value="' + value + '" ' + selected + '>' + value + '</option>';
                    });

                    var $select = $(`select[name="attribute_values[${index}][]"]`);
                    $select.html(optionsHtml);

                    new Choices($select[0], {
                        searchEnabled: true,
                        removeItemButton: true,
                        itemSelectText: '',
                    });
                }, savedValue);
            });
        }

        function addCustomFields() {
            // Номер ссылки и год выпуска перемещены наверх и выделены, если отсутствуют
            var referenceNumberField = `
                <li class="product-attribute-list taxonomy reference-number" data-taxonomy="reference-number">
                    <div class="dokan-product-attribute-heading">
                        <span><i class="fas fa-bars" aria-hidden="true"></i>&nbsp;&nbsp;<strong>Номер ссылки</strong><span class="required-asterisk"> *</span></span>
                    </div>
                    <div class="dokan-product-attribute-item dokan-clearfix">
                        <div class="content-half-part">
                            <label class="form-label" for="reference_number">Номер ссылки</label>
                            <input type="text" id="reference_number" name="reference_number" class="dokan-form-control" placeholder="Введите номер ссылки" value="<?php echo esc_attr($existing_reference_number); ?>" required>
                        </div>
                    </div>
                </li>
            `;

            var watchYearField = `
                <li class="product-attribute-list taxonomy watch-year" data-taxonomy="watch-year">
                    <div class="dokan-product-attribute-heading">
                        <span><i class="fas fa-bars" aria-hidden="true"></i>&nbsp;&nbsp;<strong>Год выпуска</strong><span class="required-asterisk"> *</span></span>
                    </div>
                    <div class="dokan-product-attribute-item dokan-clearfix">
                        <div class="content-half-part">
                            <label class="form-label" for="watch_year">Год выпуска</label>
                            <input type="text" id="watch_year" name="watch_year" class="dokan-form-control" placeholder="Введите год выпуска" value="<?php echo esc_attr($existing_watch_year); ?>" required>
                        </div>
                    </div>
                </li>
            `;

            // Добавить пользовательские поля (номер ссылки и год выпуска) в начало списка атрибутов
            $("ul.dokan-attribute-option-list").prepend(watchYearField);
            $("ul.dokan-attribute-option-list").prepend(referenceNumberField);

            // Условно добавить проверку собственности в зависимости от статуса продукта
            if ($(".dokan-product-status-label:contains('Ожидает проверки'), .dokan-product-status-label:contains('В сети'), .dokan-product-status-label:contains('Черновик')").length === 0) {
                var ownershipField = `
                    <div class="ownership-confirmation">
                        <h3>Проверка собственности <span class="required-asterisk">*</span></h3>
                        <p>Для подтверждения собственности, пожалуйста, загрузите фото ваших часов с отображением времени <strong id="generated-time"></strong>.</p>
                        <div class="dokan-form-group">
                            <label class="dokan-control-label" for="ownership_photo_upload">Загрузите фото</label>
                            <input type="file" name="ownership_photo_upload" id="ownership_photo_upload" accept="image/*" required>
                        </div>
                    </div>
                `;
                $('.dokan-attribute-variation-options').prepend('<div class="dokan-custom-fields">' + ownershipField + '</div>');

                function generateRandomTime() {
                    var hours = Math.floor(Math.random() * 24);
                    var minutes = Math.floor(Math.random() * 60);
                    return ('0' + hours).slice(-2) + ':' + ('0' + minutes).slice(-2);
                }

                var randomTime = generateRandomTime();
                $('#generated-time').text(randomTime);
            }
        }

        addPredefinedAttributesToList();
        addCustomFields();

        function checkRequiredFields() {
            var attributesFilled = checkAttributes();
            var imageUploaded = checkFeaturedImage();
            var customFieldsValid = checkCustomFields();

            var $submitButton = $('input[name="update_auction_product"]');
            if (attributesFilled && imageUploaded && customFieldsValid) {
                $submitButton.prop('disabled', false).removeClass('dokan-btn-disabled');
            } else {
                $submitButton.prop('disabled', true).addClass('dokan-btn-disabled');
            }
        }

        function checkAttributes() {
            var missingValue = false;
            $('ul.dokan-attribute-option-list select.dokan_attribute_values').each(function() {
                if ($(this).val() === "" || $(this).val().length === 0) {
                    $(this).closest('.product-attribute-list').addClass('dokan-attribute-missing');
                    missingValue = true;
                } else {
                    $(this).closest('.product-attribute-list').removeClass('dokan-attribute-missing');
                }
            });

            // Проверка, отсутствует ли номер ссылки или год выпуска
            if ($('#reference_number').val() === "") {
                $('#reference_number').closest('.product-attribute-list').addClass('dokan-attribute-missing');
                missingValue = true;
            } else {
                $('#reference_number').closest('.product-attribute-list').removeClass('dokan-attribute-missing');
            }

            if ($('#watch_year').val() === "") {
                $('#watch_year').closest('.product-attribute-list').addClass('dokan-attribute-missing');
                missingValue = true;
            } else {
                $('#watch_year').closest('.product-attribute-list').removeClass('dokan-attribute-missing');
            }

            return !missingValue;
        }

        function checkFeaturedImage() {
            return !!$('input.dokan-feat-image-id').val() && $('input.dokan-feat-image-id').val() !== "0";
        }

        function checkCustomFields() {
            var referenceNumber = $('#reference_number').val();
            var watchYear = $('#watch_year').val();
            // Проверять подтверждение собственности только если оно видно
            var ownershipValid = $(".ownership-confirmation").length === 0 || !!$('#ownership_photo_upload').val();
            return referenceNumber && watchYear && ownershipValid;
        }

        $(document).on('change', 'input.dokan-feat-image-id', function() {
            productImageUploaded = !!$('input.dokan-feat-image-id').val();
            checkRequiredFields();
        });

        $(document).on('click', '.dokan-remove-feat-image', function() {
            setTimeout(function() {
                productImageUploaded = false;
                $('input.dokan-feat-image-id').val("0");
                checkRequiredFields();
            }, 100);
        });

        $(document).on('change', 'select.dokan_attribute_values, #ownership_photo_upload', function() {
            checkRequiredFields();
        });

        checkRequiredFields();
    });
    </script>
    <?php
}
add_action('wp_footer', 'dokan_predefined_attributes_with_choices_and_custom_fields_js');

// Обработчик AJAX для получения терминов WooCommerce для атрибута
function get_wc_attribute_terms() {
    $attribute_slug = isset($_POST['attribute_slug']) ? sanitize_text_field($_POST['attribute_slug']) : '';
    $taxonomy = $attribute_slug;
    $terms = get_terms(array(
        'taxonomy' => $taxonomy,
        'hide_empty' => false,
    ));

    $term_names = array();
    if (!is_wp_error($terms)) {
        foreach ($terms as $term) {
            $term_names[] = $term->name;
        }
    }

    wp_send_json($term_names);
}
add_action('wp_ajax_get_wc_attribute_terms', 'get_wc_attribute_terms');
add_action('wp_ajax_nopriv_get_wc_attribute_terms', 'get_wc_attribute_terms');

// Хук для сохранения пользовательских полей (номер ссылки и год выпуска) как атрибутов продукта WooCommerce
function save_reference_and_year_attributes($product_id) {
    if (isset($_POST['reference_number'])) {
        $reference_number = sanitize_text_field($_POST['reference_number']);
        wp_set_object_terms($product_id, $reference_number, 'pa_reference-number');
    }

    if (isset($_POST['watch_year'])) {
        $watch_year = sanitize_text_field($_POST['watch_year']);
        wp_set_object_terms($product_id, $watch_year, 'pa_watch-year');
    }
}
add_action('dokan_process_product_meta', 'save_reference_and_year_attributes', 10, 2);

У меня есть две проблемы:

  1. Когда пользователь нажимает «Сохранить продукт», все значения атрибутов успешно сохраняются для продукта, за исключением номера ссылки и года выпуска, которые вводятся текстом.

  2. Значения атрибутов не загружаются, когда я редактирую загруженный продукт (со статусом «Ожидает проверки», «Черновик» или «В сети»). Вместо этого пользователю придется заново выбирать все значения, если он захочет внести изменения и снова сохранить продукт.

Не мог бы кто-нибудь помочь мне?

С наилучшими пожеланиями,

Мартин

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

Добрый день, Мартин!

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

Проблема 1: Сохранение значений атрибутов "Номер Refernce" и "Год Часа"

Ваша текущая реализация сохранения значений атрибутов, введённых в текстовые поля, вероятно, не работает, потому что вы не сохраняете их как метаданные продукта.

Вот обновленный код для обработки сохранения этих значений:

function save_reference_and_year_attributes($product_id) {
    if (isset($_POST['reference_number'])) {
        $reference_number = sanitize_text_field($_POST['reference_number']);
        update_post_meta($product_id, '_reference_number', $reference_number); // Сохраняем как метаданные
        wp_set_object_terms($product_id, $reference_number, 'pa_reference-number'); // Если нужно установить как термин атрибута
    }

    if (isset($_POST['watch_year'])) {
        $watch_year = sanitize_text_field($_POST['watch_year']);
        update_post_meta($product_id, '_watch_year', $watch_year); // Сохраняем как метаданные
        wp_set_object_terms($product_id, $watch_year, 'pa_watch-year'); // Если нужно установить как термин атрибута
    }
}
add_action('dokan_process_product_meta', 'save_reference_and_year_attributes', 10, 2);

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

Проблема 2: Загрузка атрибутов при редактировании продукта

Вам необходимо убедиться, что при редактировании товара значения для "Номер Refernce" и "Год Часа" загружаются в ваши текстовые поля. Вы уже закладываете это в код, но чтобы убедиться, что метаданные правильно загружаются, измените свой JavaScript код следующим образом:

// Функция для добавления пользовательских полей
function addCustomFields() {
    var referenceNumberField = `
        <li class="product-attribute-list taxonomy reference-number" data-taxonomy="reference-number">
            <div class="dokan-product-attribute-heading">
                <span><i class="fas fa-bars" aria-hidden="true"></i>&nbsp;&nbsp;<strong>Reference Number</strong><span class="required-asterisk">*</span></span>
            </div>
            <div class="dokan-product-attribute-item dokan-clearfix">
                <div class="content-half-part">
                    <label class="form-label" for="reference_number">Reference Number</label>
                    <input type="text" id="reference_number" name="reference_number" class="dokan-form-control" placeholder="Enter reference number" value="<?php echo esc_attr($existing_reference_number); ?>" required>
                </div>
            </div>
        </li>
    `;

    var watchYearField = `
        <li class="product-attribute-list taxonomy watch-year" data-taxonomy="watch-year">
            <div class="dokan-product-attribute-heading">
                <span><i class="fas fa-bars" aria-hidden="true"></i>&nbsp;&nbsp;<strong>Watch Year</strong><span class="required-asterisk">*</span></span>
            </div>
            <div class="dokan-product-attribute-item dokan-clearfix">
                <div class="content-half-part">
                    <label class="form-label" for="watch_year">Watch Year</label>
                    <input type="text" id="watch_year" name="watch_year" class="dokan-form-control" placeholder="Enter watch year" value="<?php echo esc_attr($existing_watch_year); ?>" required>
                </div>
            </div>
        </li>
    `;

    // Добавляем поля в верхнюю часть списка атрибутов
    $("ul.dokan-attribute-option-list").prepend(watchYearField);
    $("ul.dokan-attribute-option-list").prepend(referenceNumberField);
}

Убедитесь, что в вашем PHP коде доступ к $product_id происходит сразу после проверки наличия $_POST данных.

Заключение

После реализации изменений в коде вы должны убедиться, что значения "Номер Refernce" и "Год Часа" успешно сохраняются и загружаются при редактировании товара.

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

С уважением,
[Ваше Имя]

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

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