Правильное удаление записей с метаданными и вложениями

17 февр. 2014 г., 13:22:27
Просмотры: 19K
Голосов: 10

У меня есть обзор пользовательских типов записей. У них есть пользовательские таксономии, а также вложения.

В моем обзоре мне нужно предоставить ссылки для удаления записей. При этом мне также нужно удалить вложение и метаданные.

Я использовал это:

    if ( !current_user_can( 'delete_bkroadkill', $post->ID ) )
        return;

    $link = "<a href='" . wp_nonce_url( get_bloginfo('url') . "/wp-admin/post.php?action=delete&amp;post=" . $post->ID, 'delete-post_' . $post->ID) . "'>".$link."</a>";
    echo $before . $link . $after;

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

Этот код удаляет только запись, но ничего больше. Как правильно это сделать?

2
Комментарии

Насколько мне известно, встроенного способа сделать это не существует, вам нужно будет создать собственное решение. Я не уверен, на каком уровне вы владеете PHP/WP-кодингом, но по сути вам нужно будет выполнить запрос для поиска всех вложений записи, другой запрос для их удаления, еще один для поиска метаданных записи и еще один для их удаления. Полагаю, что когда вы затем удалите запись, WordPress сам обработает обновление всех таблиц таксономий, поэтому там не должно потребоваться никаких действий.

David Gard David Gard
17 февр. 2014 г. 16:27:13

Метаданные записи должны удаляться при удалении записи (но не при ее перемещении в корзину). Вложения станут "осиротевшими", но не будут удалены. Вам нужно будет позаботиться об этом самостоятельно.

s_ha_dum s_ha_dum
17 февр. 2014 г. 16:30:23
Все ответы на вопрос 2
5
10

Я использую этот код для удаления связанных медиафайлов с записью. Если вам нужно проверить определённый тип записи, вы можете использовать глобальную переменную global $post_type. По сути, код получает все вложения и удаляет их по одному. Ссылка на источник

function delete_associated_media( $id ) {
    $media = get_children( array(
        'post_parent' => $id,
        'post_type'   => 'attachment'
    ) );

    if( empty( $media ) ) {
        return;
    }

    foreach( $media as $file ) {
        wp_delete_attachment( $file->ID );
    }
}
add_action( 'before_delete_post', 'delete_associated_media' );
17 февр. 2014 г. 16:58:29
Комментарии

Хотя это работает, я бы предложил создать массив ID для удаления, затем сформировать кастомный запрос и выполнить его. Ваш вариант выполнит несколько запросов для удаления вложений; возможно, это не проблема для 3 или 4, но если у вас 100 вложений, это может значительно замедлить работу.

David Gard David Gard
17 февр. 2014 г. 17:11:34

Этот код выполняет не больше запросов, чем ваш. Он делает один запрос для получения вложений, затем один запрос для их удаления, всё через WordPress, без кастомного SQL.

Howdy_McGee Howdy_McGee
17 февр. 2014 г. 17:19:29

Неверно. Вы делаете один запрос к базе данных для получения всех вложений, а затем по одному запросу на каждое вложение при их удалении; 10 вложений = 11 запросов в сумме. Я делаю один запрос для получения всех вложений и один запрос для удаления всех связанных записей; 10 вложений = 2 запроса. Стоит отметить, что ваш код также удаляет файл, тогда как мой просто удаляет проблемную строку из базы данных. Думаю, это вопрос предпочтений, но для производительности я бы сделал по-своему, а затем запустил бы очистку для удаления несвязанных вложений (это мог бы обработать простой плагин).

David Gard David Gard
17 февр. 2014 г. 17:27:51

Я понимаю, о чем вы. Я думал, что смысл в том, чтобы полностью удалять вложения при удалении записи.

Howdy_McGee Howdy_McGee
17 февр. 2014 г. 17:37:18

Возможно, автор вопроса имел это в виду, наверное, зависит от интерпретации. Исходя из прошлого опыта, я заметил, что исключение потенциально трудоемких задач по обслуживанию из пользовательского интерфейса способствует лучшему взаимодействию, поэтому я интерпретировал это именно так.

David Gard David Gard
17 февр. 2014 г. 17:46:24
3

@s_ha_dum предполагает, что метаданные записи будут удаляться автоматически. Поэтому, учитывая его репутацию, можно предположить, что он знает, о чем говорит. Данное решение обрабатывает только вложения записи.

Рекомендую ознакомиться с документацией по хуку before_delete_post(), так как он позволяет проверять тип удаляемой записи и другие параметры.

add_action('before_delete_post', 'delete_post_attachments');
function delete_post_attachments($post_id){

    global $post_type;   
    if($post_type !== 'my_custom_post_type') return;

    global $wpdb;

    $args = array(
        'post_type'         => 'attachment',
        'post_status'       => 'any',
        'posts_per_page'    => -1,
        'post_parent'       => $post_id
    );
    $attachments = new WP_Query($args);
    $attachment_ids = array();
    if($attachments->have_posts()) : while($attachments->have_posts()) : $attachments->the_post();
            $attachment_ids[] = get_the_id();
        endwhile;
    endif;
    wp_reset_postdata();

    if(!empty($attachment_ids)) :
        $delete_attachments_query = $wpdb->prepare('DELETE FROM %1$s WHERE %1$s.ID IN (%2$s)', $wpdb->posts, join(',', $attachment_ids));
        $wpdb->query($delete_attachments_query);
    endif;

}

Важное примечание из упомянутой документации -

Важно отметить, что хук срабатывает только тогда, когда пользователь WordPress очищает корзину. Если вы используете этот хук, имейте в виду, что он не сработает при удалении вложения, поскольку вложения удаляются сразу, минуя корзину. Вместо этого используйте хук delete_post().

Еще одно примечание

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

Причина этого — производительность. В зависимости от количества вложений, их удаление по одному может занять много времени. Я считаю, что лучше сначала удалить только записи из базы данных для всех вложений, чтобы улучшить пользовательский опыт, а затем выполнить дополнительную очистку для удаления самих файлов вложений (достаточно просто найти и удалить файлы, не связанные с записями). В общем, меньше запросов + меньше работы во время взаимодействия с пользователем = меньше времени.

17 февр. 2014 г. 16:51:47
Комментарии

Как проверить тип записи?

4ndro1d 4ndro1d
17 февр. 2014 г. 16:58:00

Я обновил свой ответ (очевидно, замените my_custom_post_type на соответствующий тип записи). Также проверьте ссылку в посте на хук before_delete_post(), там всё подробно объяснено.

David Gard David Gard
17 февр. 2014 г. 17:13:10

Также нужен запрос к $wpdb->postmeta, иначе в таблице postmeta останутся "осиротевшие" значения, которые никогда не будут удалены.

Howdy_McGee Howdy_McGee
9 июн. 2016 г. 01:45:25