Вопрос или проблема
У меня есть поле, в которое я добавляю имя записи на оригинальном языке. И я перевожу заголовок на другой язык. При добавлении новой записи мне нужно проверять это поле на дублирование в других записях. Я сделал это благодаря этому коду, но проверка происходит только после публикации записи.
Как я могу сделать так, чтобы проверка проводилась в реальном времени, без публикации записи?
add_filter( 'posts_distinct', 'cf_search_distinct' );
// ПРЕДОТВРАТИТЬ ЗАПИСЬ, КОТОРАЯ УЖЕ СУЩЕСТВУЕТ
add_filter('acf/validate_value/name=original-title-mcpedl', 'validate_lien_video_filter', 10, 4);
add_filter('acf/load_value/name=hidden_post_id', 'add_post_id_as_value', 10, 3);
function add_post_id_as_value($value, $post_id, $field) {
return $post_id;
}
function validate_lien_video_filter($valid, $value, $field, $input) {
$post_id_field_key = 'field_61ab64673cad6';
if (!$valid || $value == '' || !isset($_POST['acf'][$post_id_field_key])) {
return $valid;
}
// измените ключ поля ниже на ключ поля для вашего
// hide_post_id_field
$post_id = $_POST['acf'][$post_id_field_key];
global $post;
$args = array(
'post__not_in' => array($post_id), // не проверять эту запись
'meta_query' => array(
array(
'key' => 'original-title-mcpedl',
'value' => $value
)
)
);
$query = new WP_Query($args);
if (count($query->posts)) {
// найдено хотя бы одна запись, которая
// уже имеет $value
$valid = 'Это дубликат!';
}
return $valid;
}
Вот способ достижения валидации во время ввода значения в поле ACF, поместите код в файл functions.php вашей темы или создайте для этого плагин:
Вам нужно будет изменить значения свойств $fieldName, $fieldKey на правильные значения вашего поля —это имя поля и ключ поля того же поля, которое мы валидируем—
class MyPrefix_MetaFieldValidation
{
protected static $instance;
protected $fieldName="original-title-mcpedl"; // <- Измените это на имя поля ACF
protected $fieldKey = 'field_61abaa164244a'; // <- Измените это на ключ поля
protected $ajaxAction = 'myprefix_validate_title';
protected function __construct()
{
}
public function run()
{
add_filter('acf/validate_value/key=' . $this->fieldKey, [$this, 'acfValidation'], 10, 2);
add_action('wp_ajax_' . $this->ajaxAction, [$this, 'ajaxValidation']);
add_action('admin_footer', [$this, 'ajaxValidationJS']);
}
/**
* Выводит JS валидации на экране редактирования поста.
*/
public function ajaxValidationJS()
{
if(! $this->isPostEditScreen()) {
return;
}
$nonce = wp_create_nonce($this->ajaxAction);
?>
<script>
jQuery(document).ready(function(){
jQuery('#acf-<?= $this->fieldKey; ?>').on('keyup', function (){
var field = jQuery(this);
var value = field.val();
if(! value) {
return;
}
var post_id = jQuery('#post_ID').val();
var formatError = function(msg) {
return '<div class="acf-notice -error acf-error-message"><p>' + msg + '</p></div>';
}
jQuery.ajax({
url: ajaxurl,
data: {"action": "<?= $this->ajaxAction; ?>", "nonce" : "<?= $nonce; ?>", "post_id" : post_id, "meta_value" : value },
type: "POST",
complete : function(response) {
var json = response.responseJSON;
field.parents('.acf-input').find('.acf-notice').remove();
if(response.status != 200) {
field.parents('.acf-input').prepend(formatError('Ошибка сети или сервера!'));
return;
}
if(! json.valid) {
field.parents('.acf-input').prepend(formatError(json.msg));
}
}
});
});
});
</script>
<?php
}
/**
* Выполняет ajax валидацию по мета полю
*/
public function ajaxValidation()
{
$postId = $_REQUEST['post_id'] ? intval($_REQUEST) : 0;
$metaValue = $_REQUEST['meta_value'] ? $_REQUEST['meta_value'] : '';
$nonce = $_REQUEST['nonce'] ? $_REQUEST['nonce'] : '';
if(! wp_verify_nonce($nonce, $this->ajaxAction)) {
$this->ajaxResponse([
'valid' => false,
'msg' => 'Неверный nonce'
]);
}
if(! $metaValue) {
$this->ajaxResponse([
'valid' => true,
'msg' => 'Пустое значение'
]);
}
$existingTitle = $this->metaValueExists($this->fieldName, $metaValue, $postId);
if($existingTitle) {
$this->ajaxResponse([
'valid' => false,
'msg' => 'Это заголовок уже существует для поста с id ' . $existingTitle->post_id,
]);
}
$this->ajaxResponse([
'valid' => true,
'msg' => '',
]);
}
public function acfValidation($valid, $value)
{
if(empty($value)) {
return $valid;
}
$postId = isset($_POST['post_id']) ? intval($_POST['post_id']) : null;
$existingTitle = $this->metaValueExists($this->fieldName, $value, $postId);
if($existingTitle) {
$valid = 'Этот заголовок уже существует для поста с id ' . $existingTitle->post_id;
}
return $valid;
}
/**
* Создает ajax ответ
* @param array $data
*/
protected function ajaxResponse($data)
{
header('Content-Type: application/json;charset=UTF-8');
echo json_encode($data);
exit;
}
/**
* Проверяет, находимся ли мы на экране редактирования поста
* @return bool
*/
protected function isPostEditScreen()
{
global $pagenow;
return (is_admin() && ('post.php' === $pagenow || 'post-new.php' === $pagenow));
}
/**
* Проверяет, существует ли данное мета значение в базе данных
* @param string $metaKey
* @param string $metaValue
* @param null|int $currentPostId
* @return false|object
*/
protected function metaValueExists($metaKey, $metaValue, $currentPostId = null) {
// Возврат, если заданный мета ключ или значение пусты
$metaValue = trim($metaValue);
if (empty($metaKey) || empty($metaValue)) {
return false;
}
// Убедитесь, что заданный id записи является целым числом
$currentPostId = intval($currentPostId);
// Запрос к таблице wp_postmeta для получения мета значения и мета ключа
global $wpdb;
$query = "SELECT * FROM $wpdb->postmeta WHERE meta_key LIKE '%s' AND meta_value LIKE '%s'";
// Исключите текущий id записи только если он не пуст
if($currentPostId) {
$query .= " AND post_id != $currentPostId";
}
$result = $wpdb->get_row($wpdb->prepare($query, $metaKey, $metaValue));
// Возврат объекта строки базы данных или false, если запись не найдена
return ($result) ?: false;
}
public static function getInstance()
{
if(! static::$instance) {
static::$instance = new static();
}
return static::$instance;
}
}
// Запустите наш плагин
MyPrefix_MetaFieldValidation::getInstance()->run();
Вот что мы сделали в предыдущем коде:
Я собрал весь наш код в отдельный класс “MyPrefix_MetaFieldValidation”
Я немного изменил код, который вы указали в своем вопросе, чтобы метод валидации “metaValueExists” был отделен от метода, который работает на “acf/validate_value”, потому что нам нужно использовать его в другом месте.
Кроме того, я изменил запрос, отвечающий за получение дубликата записи из базы данных, чтобы он был более легковесным.
Я убрал использование скрытого настраиваемого поля, которое хранит id поста, так как уже существует поле, которое хранит текущий id поста.
Я создал новое действие ajax для админки “ajaxValidation”, которое использует метод валидации и отвечает JSON-результатом.
JS код использует jQuery для создания ajax-запроса всякий раз, когда пользователь вводит значение в настраиваемое поле, которое мы хотим валидировать, и отображает ошибку, если введенное значение является дубликатом.
Примечание: Этот код будет показывать ошибку пользователю только при вставке значения в настраиваемое поле, но не будет препятствовать пользователю сохранить пост как черновик.
Ответ или решение
Для проверки уникальности поля ACF (Advanced Custom Fields) при добавлении записи в WordPress без публикации записи в реальном времени, вы можете воспользоваться следующими рекомендациями. Этот подход позволит вам осуществлять валидацию "на лету" во время ввода текста в поле, и выводить сообщение об ошибке, если введенное значение уже существует.
Основные шаги для реализации валидации ACF поля на уникальность
-
Создание AJAX-обработчика: Вам необходимо реализовать AJAX-обработчик, который будет проверять, существует ли введенное значение в базе данных.
-
AJAX-запрос: Используя jQuery, вы можете отправлять запрос на сервер при каждом событии ввода в поле.
-
Обновление кода функций: Код, который вы уже использовали для валидации, можно модифицировать для обработки данной логики.
Пример реализации
class MyPrefix_AcfFieldValidation
{
protected static $instance;
protected $fieldName = "original-title-mcpedl"; // Название поля ACF
protected $fieldKey = 'field_61abaa164244a'; // Ключ поля ACF
protected $ajaxAction = 'myprefix_validate_title';
public function run()
{
add_filter('acf/validate_value/key=' . $this->fieldKey, [$this, 'acfValidation'], 10, 2);
add_action('wp_ajax_' . $this->ajaxAction, [$this, 'ajaxValidation']);
add_action('admin_footer', [$this, 'enqueueScripts']);
}
public function enqueueScripts()
{
if (! $this->isPostEditScreen()) {
return;
}
$nonce = wp_create_nonce($this->ajaxAction);
?>
<script>
jQuery(document).ready(function(){
jQuery('#acf-<?= $this->fieldKey; ?>').on('keyup', function (){
var field = jQuery(this);
var value = field.val();
if (!value) return;
var post_id = jQuery('#post_ID').val();
var formatError = function(msg) {
return '<div class="acf-notice -error acf-error-message"><p>' + msg + '</p></div>';
}
jQuery.ajax({
url: ajaxurl,
data: {"action": "<?= $this->ajaxAction; ?>", "nonce" : "<?= $nonce; ?>", "post_id" : post_id, "meta_value" : value },
type: "POST",
complete: function(response) {
var json = response.responseJSON;
field.parents('.acf-input').find('.acf-notice').remove();
if (response.status != 200) {
field.parents('.acf-input').prepend(formatError('Ошибка сети или сервера!'));
return;
}
if (!json.valid) {
field.parents('.acf-input').prepend(formatError(json.msg));
}
}
});
});
});
</script>
<?php
}
public function ajaxValidation()
{
$postId = isset($_REQUEST['post_id']) ? intval($_REQUEST['post_id']) : 0;
$metaValue = isset($_REQUEST['meta_value']) ? sanitize_text_field($_REQUEST['meta_value']) : '';
$nonce = isset($_REQUEST['nonce']) ? $_REQUEST['nonce'] : '';
if (!wp_verify_nonce($nonce, $this->ajaxAction)) {
$this->ajaxResponse(['valid' => false, 'msg' => 'Недействительный токен.']);
}
if (!$metaValue) {
$this->ajaxResponse(['valid' => true, 'msg' => 'Пустое значение.']);
}
$existingTitle = $this->metaValueExists($this->fieldName, $metaValue, $postId);
if ($existingTitle) {
$this->ajaxResponse(['valid' => false, 'msg' => 'Это название уже существует для поста с ID ' . $existingTitle->post_id]);
}
$this->ajaxResponse(['valid' => true, 'msg' => '']);
}
protected function ajaxResponse($data)
{
wp_send_json($data);
}
protected function metaValueExists($metaKey, $metaValue, $currentPostId = null)
{
global $wpdb;
$currentPostId = intval($currentPostId);
$metaValue = trim($metaValue);
$query = $wpdb->prepare("SELECT * FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s", $metaKey, $metaValue);
if ($currentPostId) {
$query .= $wpdb->prepare(" AND post_id != %d", $currentPostId);
}
return $wpdb->get_row($query);
}
protected function isPostEditScreen()
{
global $pagenow;
return is_admin() && ($pagenow === 'post.php' || $pagenow === 'post-new.php');
}
public static function getInstance()
{
if (!static::$instance) {
static::$instance = new static();
}
return static::$instance;
}
}
MyPrefix_AcfFieldValidation::getInstance()->run();
Как работает этот код?
-
AJAX Запрос: Каждый раз при нажатии клавиши в поле ACF отправляется запрос на сервер для проверки уникальности введенного значения.
-
Валидация: Серверная функция
ajaxValidation
проверяет, существует ли значение в базе данных, и возвращает ответ в формате JSON. -
Обновление интерфейса: Если значение оказывается дублирующим, сообщение об ошибке отображается прямо под полем ACF без необходимости публиковать запись.
Примечания
- Код обеспечит реализацию валидации в реальном времени, однако учтите, что это не предотвратит сохранение записи в черновики; пользователи все равно смогут это делать.
Используя приведенный выше код, вы сможете обеспечить отличную пользовательскую практику, минимизируя количество ошибок при добавлении записей и улучшая качество контента на вашем сайте.