Upprätthålla en inläggsstatus när du uppdaterar ett WordPress-inlägg
Jag har precis avslutat en funktion för ett projekt som använder en kombination av anpassade inläggstyper, dataimport och uppdatering av befintliga inlägg när en användare (eller uppsättning användare) tas bort.
Det finns dock ett problem:
Säg att du har ett inlägg som för närvarande är publicerat (det vill säga att ’post_status’ är inställt på ’publish’), men när du uppdaterar inlägget via wp_update_post är dess post_status-attribut inställt på ’future’.
Med andra ord, när du programmatiskt uppdaterar ett inlägg, ställs inläggets status in på "Schemalagt" (enligt användargränssnittet) och "framtid" (enligt databaskolumnen).
Så vad ger?
Poststatus vid uppdatering
Generellt sett är jag inte ett fan av att få något att fungera, ta reda på att det gör det och gå vidare till nästa uppgift om jag inte förstår exakt varför det inte fungerade [som förväntat] i första hand.
Detta är inte att säga att det inte finns tillfällen då begränsningar har orsakat detta att hända (åtminstone under arbetstid). Men om det är midnatt så fixar jag en bugg, och då är jag inte helt säker på att jag förstår varför den tydligen gick sönder från början, jag kan lika gärna stanna uppe och lista ut det eller hur?
Det är som programmerarens version av sunk cost bias.
Hur som helst, som beskrivits ovan, här är kärnan i problemet:
- Ett givet inlägg finns i WordPress.
- När en användare raderas ser systemet efter om visst innehåll finns i ett givet inlägg och tar i så fall bort det.
- Inlägget uppdateras sedan med hjälp av en WordPress API-funktion.
Allt fungerar utmärkt förutom att det finns ett problem: Närhelst inlägget uppdateras programmatiskt markerar det inläggets status som "framtid" i databasen och ställer slutligen in postens rad i vyn Alla inlägg som "Schemalagt".
Två lösningar
Det finns två sätt att lösa det här problemet:
- Kör en snabbfråga direkt mot databasen för att åtgärda problemet.
- Uppdatera inläggets argument för att använda rätt tid i GMT (jag kommer att täcka detta mer om ett ögonblick).
Om du väljer den första lösningen, se till att du använder förberedelsefunktionen och se till att du kör frågan efter att uppdateringsfunktionen har returnerats.
<?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) );
}
Om du väljer den andra lösningen, uppdatera då uttryckligen egenskaperna för postobjektet innan du skickar det till uppdateringsfunktionen.
<?php
$event_post->post_date_gmt = gmdate( 'Y-m-d H:i:s', $event_post->post_date_gmt );
wp_update_post( $event_post );
Jag är inte en som säger att det inte finns plats för någon av dessa lösningar när du arbetar med ett projekt, men frågan kvarstår:
Varför markerar WordPress ett inlägg som är schemalagt för framtiden när inlägget, innan det gick igenom uppdateringsproceduren, var inställt på att publiceras?
Och om du inte är bekant med resten av argumenten som skickas till WordPress när du uppdaterar ett inlägg, kommer det att ta lite tid att spåra det.
Förstå problemet
För att undvika att skriva en direkt databasfråga väljer jag att gå med den andra lösningen eftersom den är renare (även om det inte gör det mindre förvirrande).
För att ta reda på varför detta beteende händer kan du fråga en annan utvecklare eller felsöka koden. Med tanke på att jag jobbade med detta sent på kvällen valde jag det senare.
Kör felsökaren och spårningskoden via post.php.
Jag tänker inte prata igenom alla brytpunkter och klockor som jag ställt in, så jag ska försöka göra detta så kortfattat som möjligt.
Först, när koden körs genom funktionen wp_update_post kommer den att stöta på detta kodblock :
<?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';
}
}
}
Observera att inom villkoret arbetar vi inte med en bilaga och vi arbetar med inläggsstatusen "publicera". Så vi kommer att träffa den första villkorliga inom den yttre villkorliga (lite förvirrande är det inte?).
Nu finns det en variabel, $post_status, som bibehåller värdet publicera men den är inte tilldelad någon speciell egenskap för det aktuella inlägget. Om du fortsätter att spåra koden kommer du att stöta på nästa rad :
<?php
$post_parent = apply_filters( 'wp_insert_post_parent', $post_parent, $post_ID, compact( array_keys( $postarr) ), $postarr );
Här kommer du att se att det fungerar med en uppsättning arraynycklar som finns i variabeln $postarr. Denna variabel används tidigare i koden (se den här på Trac). Och om du tittar noga kommer du att se att den först kontrollerar om post_date_gmt-egenskapen är tom eller om den nollställs.
Och eftersom det inte är det kommer det att använda värdet som vi angav på egenskapen när vi först anropade funktionen :
<?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'];
}
Vänta.
Varför gå tillbaka till funktionen när koden redan har körts förbi detta? Det är felsökningens natur. Ibland ser du variabler och tänker inte på dem bara för att se att de används, ställs in, manipuleras, kombineras eller vad som helst senare i koden.
Det är verkligheten.
Men hur som helst, på grund av det kommer fastigheten att behålla värdet som vi angav innan uppdateringsfunktionen anropades. Vanligtvis kommer detta att vara samma värde som när funktionen startade, men det kan finnas tillfällen då du till exempel vill slänga ett inlägg.
Och det är skäl för att använda wp_delete_post. Men det har sina varningar, särskilt kring anpassade inläggstyper.
Det hamnar dock i ett annat inlägg.