Я щойно завершив функцію для проекту, який використовує комбінацію спеціальних типів публікацій, імпорт даних і оновлення наявних публікацій під час видалення користувача (або групи користувачів).
Однак є одна проблема:
Скажімо, у вас є публікація, яка наразі опублікована (тобто для її «post_status» встановлено значення «publish»), але коли ви оновлюєте публікацію за допомогою wp_update_post, її атрибут post_status має значення «future».
По порядку, щоразу, коли ви програмно оновлюєте допис, статус допису встановлюється на «Заплановано» (відповідно до інтерфейсу користувача) і «майбутнє» (відповідно до стовпця бази даних).
Так що дає?
Статус публікації під час оновлення
Взагалі кажучи, я не прихильник того, щоб щось спрацювало, дізнався, що це так, і перейшов до наступного завдання, якщо я не розумію, чому воно не спрацювало [як очікувалося] спочатку.
Це не означає, що немає випадків, коли обмеження призвели до цього (принаймні в робочий час). Але якщо зараз опівночі, я виправляю помилку, а потім я не зовсім впевнений, що розумію, чому вона, очевидно, взагалі була зламана, я міг би не спати й розібратися, чи не так?
Це схоже на програмістську версію зміщення незворотних витрат.
У будь-якому випадку, як зазначено вище, ось суть проблеми:
- Дана публікація існує в WordPress.
- Коли користувача видалено, система перевіряє, чи існує певний вміст у даній публікації, і, якщо так, видаляє його.
- Потім публікація оновлюється за допомогою функції WordPress API.
Усе працює чудово, за винятком проблеми: кожного разу, коли допис програмно оновлюється, статус допису позначається як «майбутній» у базі даних, зрештою встановлюючи рядок допису в перегляді «Усі дописи» як «Заплановано».
Два рішення
Є два способи вирішити цю проблему:
- Виконайте швидкий запит безпосередньо до бази даних, щоб вирішити проблему.
- Оновіть аргументи публікації, щоб використовувати правильний час за GMT (я розповім про це трохи пізніше).
Якщо ви обираєте перше рішення, переконайтеся, що ви використовуєте функцію підготовки та переконайтеся, що виконуєте запит після повернення функції оновлення.
<?php
// First, update the post using the available WordPress API.
wp_update_post( $post );
/**
* If the post status attribute of the post that was just updated
* maintains the 'publish' post setatus, then manually update the
* database column.
*/
if ('publish' === $post->post_status) {
global $wpdb;
$wpdb->query(
$wpdb->prepare(
"
UPDATE $wpdb->posts
SET post_status = '%s'
WHERE ID = %d
",
'publish',
$post->ID) );
}
Якщо ви обираєте друге рішення, тоді явно оновіть властивості об’єкта публікації перед передачею його функції оновлення.
<?php
$event_post->post_date_gmt = gmdate( 'Y-m-d H:i:s', $event_post->post_date_gmt );
wp_update_post( $event_post );
Я не з тих, хто каже, що під час роботи над проектом немає місця жодному з цих рішень, але питання залишається:
Чому WordPress позначає публікацію, заплановану на майбутнє, коли публікацію було налаштовано на публікацію перед проходженням процедури оновлення?
І якщо ви не знайомі з рештою аргументів, які передаються WordPress під час оновлення публікації, знадобиться деякий час, щоб відстежити їх.
Розуміння проблеми
Щоб уникнути написання прямого запиту до бази даних, я вирішив скористатися другим рішенням, оскільки воно чистіше (хоча це не робить його менш заплутаним).
Щоб зрозуміти, чому така поведінка відбувається, ви можете запитати іншого розробника або налагодити код. Враховуючи, що я працював над цим пізно ввечері, я вибрав останнє.
Запуск налагоджувача та код трасування через post.php.
Я не збираюся розповідати про всі контрольні точки та годинники, які я встановлюю, тому я спробую зробити це якомога стисло.
По-перше, коли код проходить через функцію wp_update_post, він зустріне цей блок коду :
<?php
if ('attachment' !== $post_type) {
if ('publish' == $post_status) {
$now = gmdate('Y-m-d H:i:59');
if (mysql2date('U', $post_date_gmt, false) > mysql2date('U', $now, false)) {
$post_status = 'future';
}
} elseif ('future' == $post_status) {
$now = gmdate('Y-m-d H:i:59');
if (mysql2date('U', $post_date_gmt, false) <= mysql2date('U', $now, false)) {
$post_status = 'publish';
}
}
}
Зауважте, що в рамках умови ми працюємо не з вкладенням, а зі статусом публікації «опублікувати». Отже, ми збираємося натиснути на першу умову всередині зовнішньої умови (трохи заплутано, чи не так?).
Тепер є змінна $post_status, яка підтримує значення public, але вона не призначена жодній конкретній властивості поточної публікації. Якщо ви продовжите відстежувати код, ви натрапите на наступний рядок :
<?php
$post_parent = apply_filters( 'wp_insert_post_parent', $post_parent, $post_ID, compact( array_keys( $postarr) ), $postarr );
Тут ви побачите, що він працює з набором ключів масиву, які містяться в змінній $postarr. Ця змінна використовується раніше в коді (перегляньте її тут, на Trac). І якщо ви придивитесь уважніше, то побачите, що спочатку перевіряється, чи властивість post_date_gmt порожня, чи вона вся обнулена.
А оскільки це не так, він використовуватиме значення, яке ми вказали у властивості під час початкового виклику функції :
<?php
if (empty( $postarr['post_date_gmt']) || '0000-00-00 00:00:00' == $postarr['post_date_gmt']) {
if (! in_array( $post_status, array( 'draft', 'pending', 'auto-draft') )) {
$post_date_gmt = get_gmt_from_date( $post_date );
} else {
$post_date_gmt = '0000-00-00 00:00:00';
}
} else {
$post_date_gmt = $postarr['post_date_gmt'];
}
Зачекайте.
Навіщо повертатися до функції, якщо код уже пройшов мимо цього? Така природа налагодження. Іноді ви бачите змінні й нічого про них не думаєте, лише щоб побачити, як вони використовуються, встановлюються, маніпулюються, комбінуються чи будь-що інше пізніше в коді.
Це реальність.
Але в будь-якому разі через це властивість зберігатиме значення, яке ми надали перед викликом функції оновлення. Зазвичай це буде те саме значення, що й під час запуску функції, але іноді ви захочете, скажімо, викинути допис у смітник.
І це є підставою для використання wp_delete_post. Але це також має свої застереження, особливо щодо спеціальних типів публікацій.
Однак це стосується іншої публікації.