Вопрос или проблема
У меня есть пользовательский тип записи под названием “Envios”. В нем я создал индивидуальное мета-поле, которое позволяет выбирать пользователей и записи. Когда один из этих пользовательских типов записей публикуется, он отправляет электронное письмо выбранным пользователям с содержимым выбранных записей.
Для выбора записей я использую плагин ACF с полем Post Object.
Проблема в том, что иногда письма отправляются правильно, но в большинстве случаев ничего не доставляется.
Вот код:
// Добавляет пользовательское мета-поле
function email_delivery_munda_add_custom_box() {
$screens = ['envios', 'md_cpt'];
foreach ($screens as $screen) {
add_meta_box(
'md_email_delivery_box_id', // Уникальный ID
'Detalles de email', // Заголовок поля
'md_email_delivery_custom_box_html', // Callback содержимого, должен быть типа callable
$screen, // Тип записи
'side',
'default'
);
}
}
add_action('add_meta_boxes', 'email_delivery_munda_add_custom_box');
// Добавляет содержимое пользовательского мета-поля
function md_email_delivery_custom_box_html($post)
{
$asunto = get_post_meta($post->ID, 'email_delivery_asunto', true);
wp_nonce_field( 'save_md_email', 'md_email_nonce' );
if ($asunto == "") {
$asunto = "Informe - " . date("d-m-Y");
}
?>
<div class="email-delivery-asunto">
<label for="email_delivery_asunto">Asunto</label>
<input type="text" name="email_delivery_asunto" id="email_delivery_asunto" class="postbox" value=" <?php echo $asunto ?>" placeholder="Informe - <?php echo date("d-m-Y") ?>">
</div>
<div class="email-delivery-receptores">
<label for="select-emails">Destinatarios</label>
<?php
$blogusers = get_users();
// Массив объектов WP_User.
echo '<select name="clients-email-input[]" id="select-emails" class="admin-email-receptores" multiple="multiple"><option></option>';
foreach ( $blogusers as $user ) {
echo '<option value="'. esc_html( $user->user_email ) .'">' . esc_html( $user->display_name ) . '</option>';
}
echo '</select>';
?>
</div>
<?php $enviados = get_post_meta($post->ID, 'email_send', true); ?>
<div class="email-delivery-asunto">
<label for="email_send">Ultimo envio</label>
<p id="email_send"><?php echo $enviados ?></p>
</div>
<?php
}
// Сохраняет данные записи
function md_email_save_postdata($post_id) {
if ( ! isset( $_POST['md_email_nonce'] ) ) {
return $post_id;
}
if ( ! wp_verify_nonce( $_POST['md_email_nonce'], 'save_md_email' ) ) {
return $post_id;
}
$asunto = sanitize_text_field( $_POST['email_delivery_asunto'] );
update_post_meta( $post_id, 'email_delivery_asunto', $asunto );
}
add_action('save_post', 'md_email_save_postdata');
// Отправляет письмо, когда публикуется пользовательский тип записи Envios
function md_email_publish_postdata($post_id) {
$asunto = sanitize_text_field( $_POST['email_delivery_asunto'] );
$headers = array(
'Content-Type: text/html; charset=UTF-8'
);
$enviados = sanitize_text_field( $_POST['email_send'] );
$blogusers = get_users();
foreach ($_REQUEST['clients-email-input'] as $selectedOption) {
array_push($headers, "BCC:" . $selectedOption);
foreach ( $blogusers as $user ) {
if ($user->user_email === $selectedOption) {
$enviados .= "<b>" . esc_html( $user->display_name ) . "</b> " . esc_html( $user->user_email) . "<br>";
}
}
}
update_post_meta( $post_id, 'email_send', $enviados );
$content="";
// Удаление отсюда заставляет работать.
$posts = get_field('casos',$post_id);
if( $posts ): ?>
<?php foreach( $posts as $post): // переменная должна называться $post (ВАЖНО) ?>
<?php setup_postdata($post);
$content .= '<h1>' . get_the_title( $post ) . '</h1><br>';
$content .= '<p>' . get_field( 'descripcion_breve', $post) . '</p><br>';
endforeach; ?>
<?php wp_reset_postdata(); // ВАЖНО - перезагрузка объекта $post, чтобы остальная часть страницы работала правильно ?>
<?php endif;
// до сюда.
$clients_list = "[email protected]";
wp_mail( $clients_list, $asunto , $content, $headers );
}
add_action('publish_envios', 'md_email_publish_postdata', 10, 2 );
Если я удалю часть ACF из // Удаление отсюда заставляет работать. до // до сюда. письма отправляются корректно каждый раз, но без содержимого выбранных записей.
Я не могу точно сказать, в чем проблема, но, возможно, вы сможете дать какие-то советы на эту тему.
Заранее спасибо!
РЕШЕНИЕ:
ACF использует экшен acf/save_post ПОСЛЕ того, как запись опубликована, так что я пытался получить значение, которое еще не было доступно.
Поэтому я изменил экшен на acf save_post, и внутри него использую if (get_post_status($post_id) == 'publish') {
, чтобы проверить статус записи.
Кроме того, я обновляю вывод Object Post следующим образом:
$posts = get_field( 'casos', $post_id );
if( $posts ):
foreach( $posts as $post):
$content .= '<h1>' . get_the_title( $post ) . '</h1><br>';
if ( get_field( 'descripcion_breve', $post->ID )) {
$content .= '<p>' . get_field( 'descripcion_breve', $post->ID ) . '</p><br>';
} else {
$content .= '<p>Description empty</p><br>';
}
endforeach;
endif;
…Удалил свои предыдущие ‘решения’…
Итак, реальная проблема здесь заключается в том, что эта функция привязана к экшену publish_post
, в этот момент может быть так, что запись еще не сохранена, если это новая запись. Только если это запись, которая уже была сохранена, но с другим статусом, мы можем получить значения ACF, используя get_field()
.
Решение может заключаться в использовании хука acf/save_post
с приоритетом 10 или более, так как он выполняется после того, как запись была сохранена, что позволяет нам получить значения, используя get_field()
.
Вот короткий пример использования этого хука:
function my_acf_save_post( $post_id ) {
// выход, если неверный тип записи
if( get_post_type( $post_id ) !== 'envios' ) {
return;
}
// получать новое значение
$posts = get_field( 'casos', $post_id );
// что-то сделать
}
add_action('acf/save_post', 'my_acf_save_post', 20);
И даже если я изменю свой add_action на webhook “save_post_post-type” вместо webhook “publish_post-type”, поля acf по-прежнему не заполнены до тех пор, пока запись не опубликована и затем обновлена!
.
Ответ или решение
Ваша задача заключается в корректной отправке email-сообщений при публикации пользовательского типа записей "Envios" в WordPress, используя мета-поля, созданные с помощью ACF (Advanced Custom Fields). Однако вы столкнулись с проблемой, когда письма не всегда отправляются корректно, особенно если данные ACF ещё не сохранены в момент вызова функции отправки email.
Теория
Проблема заключается в последовательности выполнения хуков в WordPress. Когда вы используете действие publish_post
, оно срабатывает до того, как данные, установленные ACF, будут сохранены в базе данных. Это означает, что в вашей функции отправки email данные ещё недоступны. Решение состоит в том, чтобы использовать более подходящий хук, который сработает после сохранения данных.
ACF предоставляет хук acf/save_post
, который можно использовать для выполнения действий после того, как все данные полей ACF были успешно сохранены. Это позволит гарантировать, что все необходимые данные доступны для чтения и использования.
Пример
Рассмотрим, как именно можно интегрировать данное решение в существующую структуру вашего кода. Вместо стандартного использования publish_post
, мы будем использовать acf/save_post
, что даёт гарантию, что данные полей ACF надёжно сохранены к моменту выполнения вашей логики отправки email.
function my_acf_save_post( $post_id ) {
// Исключаем обработку авто-сохранений и недопустимых типов записей
if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) || get_post_type( $post_id ) !== 'envios' ) {
return;
}
// Проверяем, опубликован ли пост
if ( get_post_status( $post_id ) == 'publish' ) {
// Получаем значения полей ACF
$posts = get_field( 'casos', $post_id );
if ($posts) {
$content = "";
foreach ($posts as $post) {
setup_postdata($post);
$content .= '<h1>' . get_the_title($post) . '</h1><br>';
$descripcion = get_field('descripcion_breve', $post->ID);
if ($descripcion) {
$content .= '<p>' . $descripcion . '</p><br>';
} else {
$content .= '<p>Description empty</p><br>';
}
}
wp_reset_postdata();
// Отправка email
$asunto = sanitize_text_field($_POST['email_delivery_asunto']);
$headers = array(
'Content-Type: text/html; charset=UTF-8'
);
$receptores = $_REQUEST['clients-email-input'];
foreach ($receptores as $email) {
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
wp_mail($email, $asunto, $content, $headers);
}
}
// Обновляем мета-данные с указанием, кому было отправлено
update_post_meta($post_id, 'email_send', implode(", ", $receptores));
}
}
}
add_action('acf/save_post', 'my_acf_save_post', 20);
Применение
При использовании этого метода важно понимать, что вы запланируете отправку email только после того, как данные ACF будут надёжно сохранены. В результате, вы избегаете ошибки, когда данные ещё не были записаны при публикации. Использование acf/save_post
гарантирует, что все изменения, сделанные через мета-поля, будут доступны и корректно обработаны.
Таким образом, ваша система теперь работает на более надёжной основе, минимизируя риск некорректной обработки данных и обеспечивая своевременную отправку email при всех необходимых условиях. Это пример улучшенной обработки пользовательских действий и синхронизация с процессами сохранения данных в WordPress.
Теперь, благодаря этому подходу, вы должны ощутить значительное улучшение в стабильности и надежности вашей системы уведомлений по email.