Add feature to cache story cards

This commit is contained in:
Tetrakern 2024-08-13 19:18:51 +02:00
parent 9e06156c9c
commit 8c77b23260
8 changed files with 733 additions and 443 deletions

View File

@ -1597,6 +1597,8 @@ define( 'CONSTANT_NAME', value );
| FICTIONEER_QUERY_ID_ARRAY_LIMIT | integer | Maximum allowed IDs in 'post__{not}_in' query arguments. Default `100`.
| FICTIONEER_PATREON_EXPIRATION_TIME | integer | Time until a users Patreon data expires in seconds. Default `WEEK_IN_SECONDS`.
| FICTIONEER_PARTIAL_CACHE_EXPIRATION_TIME | integer | Time until a cached partial expires in seconds. Default `4 * HOUR_IN_SECONDS`.
| FICTIONEER_CARD_CACHE_LIMIT | integer | Number of cards cached by the story card cache feature if enabled. Default `50`.
| FICTIONEER_CARD_CACHE_EXPIRATION_TIME | integer | Time until the whole story card cache expires in seconds. Default `HOUR_IN_SECONDS`.
| FICTIONEER_CACHE_PURGE_ASSIST | boolean | Whether to call the cache purge assist function on post updates. Default `true`.
| FICTIONEER_RELATIONSHIP_PURGE_ASSIST | boolean | Whether to purge related post caches. Default `true`.
| FICTIONEER_CHAPTER_LIST_TRANSIENTS | boolean | Whether to cache chapter lists on story pages as Transients. Default `true`.

View File

@ -288,6 +288,16 @@ if ( ! defined( 'FICTIONEER_PARTIAL_CACHE_EXPIRATION_TIME' ) ) {
define( 'FICTIONEER_PARTIAL_CACHE_EXPIRATION_TIME', 4 * HOUR_IN_SECONDS );
}
// Integer: Maximum number of cards in card cache
if ( ! defined( 'FICTIONEER_CARD_CACHE_LIMIT' ) ) {
define( 'FICTIONEER_CARD_CACHE_LIMIT', 50 );
}
// Integer: Time until the card cache expires
if ( ! defined( 'FICTIONEER_CARD_CACHE_EXPIRATION_TIME' ) ) {
define( 'FICTIONEER_CARD_CACHE_EXPIRATION_TIME', HOUR_IN_SECONDS );
}
/*
* Booleans
*/

View File

@ -83,6 +83,16 @@ if ( ! defined( 'FICTIONEER_ENABLE_PARTIAL_CACHING' ) ) {
);
}
// Boolean: Partial caching enabled?
if ( ! defined( 'FICTIONEER_ENABLE_STORY_CARD_CACHING' ) ) {
define(
'FICTIONEER_ENABLE_STORY_CARD_CACHING',
get_option( 'fictioneer_enable_story_card_caching' ) &&
! is_customize_preview() &&
! fictioneer_caching_active( 'story_card_caching' )
);
}
// =============================================================================
// ENABLE SHORTCODE TRANSIENTS?
// =============================================================================
@ -192,6 +202,11 @@ if ( ! function_exists( 'fictioneer_purge_all_caches' ) ) {
// Cached HTML partials
fictioneer_clear_all_cached_partials();
// Cached story cards
if ( FICTIONEER_ENABLE_STORY_CARD_CACHING ) {
fictioneer_purge_story_card_cache();
}
}
}
@ -281,6 +296,11 @@ if ( ! function_exists( 'fictioneer_purge_post_cache' ) ) {
if ( FICTIONEER_ENABLE_PARTIAL_CACHING ) {
fictioneer_clear_cached_content( $post_id );
}
// Cached story card
if ( FICTIONEER_ENABLE_STORY_CARD_CACHING ) {
fictioneer_delete_cached_story_card( $post_id );
}
}
}
@ -1072,3 +1092,182 @@ if ( FICTIONEER_ENABLE_PARTIAL_CACHING ) {
add_action( $hook, 'fictioneer_clear_cached_content' );
}
}
// =============================================================================
// TRANSIENT CARD CACHE
// =============================================================================
/**
* Returns the Transient array with all cached story cards
*
* @since 5.22.1
*
* @return array Array of cached story cards; empty array if disabled.
*/
function fictioneer_get_story_card_cache() {
// Abort if...
if ( ! FICTIONEER_ENABLE_STORY_CARD_CACHING ) {
return [];
}
// Initialize global cache variable
global $fictioneer_story_card_cache;
if ( ! $fictioneer_story_card_cache ) {
$fictioneer_story_card_cache = get_transient( 'fictioneer_card_cache' ) ?: [];
}
return $fictioneer_story_card_cache;
}
function fictioneer_set_story_card_cache( $key, $card ) {
// Abort if...
if ( ! FICTIONEER_ENABLE_STORY_CARD_CACHING ) {
return;
}
// Initialize global cache variable
global $fictioneer_story_card_cache;
// Setup
if ( ! $fictioneer_story_card_cache ) {
$fictioneer_story_card_cache = fictioneer_get_story_card_cache();
}
// Remove cached card...
unset( $fictioneer_story_card_cache[ $key ] );
// ... and append at the end
$fictioneer_story_card_cache[ $key ] = $card;
}
function fictioneer_save_story_card_cache() {
// Abort if...
if ( ! FICTIONEER_ENABLE_STORY_CARD_CACHING ) {
return;
}
// Initialize global cache variable
global $fictioneer_story_card_cache;
// Anything to save?
if ( is_array( $fictioneer_story_card_cache ) ) {
set_transient( 'fictioneer_card_cache', $fictioneer_story_card_cache, FICTIONEER_CARD_CACHE_EXPIRATION_TIME );
}
}
add_action( 'shutdown', 'fictioneer_save_story_card_cache' );
function fictioneer_purge_story_card_cache() {
// Abort if...
if ( ! FICTIONEER_ENABLE_STORY_CARD_CACHING ) {
return;
}
global $fictioneer_story_card_cache;
$fictioneer_story_card_cache = [];
delete_transient( 'fictioneer_card_cache' );
}
function fictioneer_delete_cached_story_card( $post_id ) {
// Abort if...
if ( ! FICTIONEER_ENABLE_STORY_CARD_CACHING ) {
return;
}
// Initialize global cache variable
global $fictioneer_story_card_cache;
// Setup
if ( ! $fictioneer_story_card_cache ) {
$fictioneer_story_card_cache = fictioneer_get_story_card_cache();
}
// Find matching keys
foreach ( $fictioneer_story_card_cache as $key => $value ) {
if ( strpos( $key, $post_id . '_' ) === 0 ) {
unset( $fictioneer_story_card_cache[ $key ] );
// Update comment count in story meta cache (if story)
if ( FICTIONEER_ENABLE_STORY_DATA_META_CACHE ) {
$story_data = fictioneer_get_story_data( $post_id );
if ( $story_data ) {
$story_data['comment_count'] = fictioneer_get_story_comment_count( $post_id );
$story_data['comment_count_timestamp'] = time();
update_post_meta( $post_id, 'fictioneer_story_data_collection', $story_data );
}
}
}
}
}
function fictioneer_delete_cached_story_card_by_comment( $comment_id ) {
// Setup
$comment = get_comment( $comment_id );
if ( $comment ) {
$story_id = get_post_meta( $comment->comment_post_ID, 'fictioneer_chapter_story', true );
if ( $story_id ) {
fictioneer_delete_cached_story_card( $story_id );
}
}
}
add_action( 'wp_insert_comment', 'fictioneer_delete_cached_story_card_by_comment' );
add_action( 'delete_comment', 'fictioneer_delete_cached_story_card_by_comment' );
function fictioneer_get_cached_story_card( $key ) {
// Abort if...
if ( ! FICTIONEER_ENABLE_STORY_CARD_CACHING ) {
return null;
}
// Initialize global cache variable
global $fictioneer_story_card_cache;
// Setup
if ( ! $fictioneer_story_card_cache ) {
$fictioneer_story_card_cache = fictioneer_get_story_card_cache();
}
// Look in currently cached cards...
if ( $card = ( $fictioneer_story_card_cache[ $key ] ?? 0 ) ) {
// Remove cached card...
unset( $fictioneer_story_card_cache[ $key ] );
// ... and append at the end
$fictioneer_story_card_cache[ $key ] = $card;
} else {
return null;
}
// Limit cache to 50 latest cards
if ( count( $fictioneer_story_card_cache ) > FICTIONEER_CARD_CACHE_LIMIT ) {
$fictioneer_story_card_cache = array_slice(
$fictioneer_story_card_cache,
-1 * FICTIONEER_CARD_CACHE_LIMIT,
FICTIONEER_CARD_CACHE_LIMIT,
true
);
}
// Return cached card
return $card;
}

View File

@ -677,6 +677,12 @@ define( 'FICTIONEER_OPTIONS', array(
'group' => 'fictioneer-settings-general-group',
'sanitize_callback' => 'fictioneer_sanitize_checkbox',
'default' => 0
),
'fictioneer_enable_story_card_caching' => array(
'name' => 'fictioneer_enable_story_card_caching',
'group' => 'fictioneer-settings-general-group',
'sanitize_callback' => 'fictioneer_sanitize_checkbox',
'default' => 0
)
),
'integers' => array(
@ -1141,6 +1147,7 @@ function fictioneer_get_option_label( $option ) {
'fictioneer_enable_custom_fields' => __( 'Enable custom fields', 'fictioneer' ),
'fictioneer_disable_anti_flicker' => __( 'Disable anti-flicker script', 'fictioneer' ),
'fictioneer_hide_categories' => __( 'Hide categories on posts', 'fictioneer' ),
'fictioneer_enable_story_card_caching' => __( 'Enable caching of story cards', 'fictioneer' ),
);
}

View File

@ -481,6 +481,9 @@ function fictioneer_purge_theme_caches() {
fictioneer_clear_all_cached_partials();
// Cached story cards
fictioneer_purge_story_card_cache();
// Cache busting string
fictioneer_regenerate_cache_bust();

View File

@ -734,6 +734,20 @@
?>
</div>
<div class="fictioneer-card__row">
<?php
fictioneer_settings_label_checkbox(
'fictioneer_enable_story_card_caching',
__( 'Enable caching of story cards', 'fictioneer' ),
sprintf(
__( 'Caches the latest %d story cards in the database to speed up loading. Do not use this together with a cache plugin.', 'fictioneer' ),
FICTIONEER_CARD_CACHE_LIMIT
),
__( '<p>Rendering story cards is resource-intensive due to multiple complex queries. The more cards you display at once, the slower the page will load. This feature mitigates the slowdown by caching the HTML of the last rendered cards in the database, which typically results in faster loading times for the most recent stories.</p><p>You can use the <code>FICTIONEER_CARD_CACHE_LIMIT</code> constant to change the number of cached cards (default is 50). Be aware that increasing this number will result in higher RAM consumption.</p>', 'fictioneer' )
);
?>
</div>
</div>
</div>
</div>

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,19 @@
// No direct access!
defined( 'ABSPATH' ) OR exit;
// Card cache?
$card_cache_active = get_option( 'fictioneer_enable_story_card_caching' );
if ( $card_cache_active ) {
$cache_key = $post->ID . '_' . date( 'Y-m-d-H-i-s', strtotime( $post->post_modified_gmt ) ) .
'_' . md5( json_encode( $args ) );
if ( $cache = fictioneer_get_cached_story_card( $cache_key ) ) {
echo $cache;
return;
}
}
// Setup
$post_id = $post->ID;
$story = fictioneer_get_story_data( $post_id );
@ -72,6 +85,11 @@ $thumbnail_args = array(
'class' => 'no-auto-lightbox'
);
// Buffer HTML for cache if active
if ( $card_cache_active ) {
ob_start();
}
?>
<li
@ -253,3 +271,17 @@ $thumbnail_args = array(
</div>
</li>
<?php
// Capture, store, and output cache if active
if ( $card_cache_active ) {
// Get buffered HTML
$cache = fictioneer_minify_html( ob_get_clean() );
// Save in cache
fictioneer_set_story_card_cache( $cache_key, $cache );
// Render card
echo $cache;
}