0

NOTE: The issue I'm encountering is specific to Block Themes; I understand how to filter the text returned from the_excerpt in a Classic Theme.

What I would like to only show read more text when when wp:post-excerpt meets excerptLength. However, when I use, for example,

<!-- wp:post-excerpt {"moreText":"Read More","excerptLength":40, "showMoreOnNewLine":false} /-->

in a template/pattern, all posts on the pages that use this template/pattern have a "Read More" link, even if the except text length had not met the value excerptLength (shows even if there is no excerpt).

Referencing the source for Excerpt linked to from https://developer.wordpress.org/block-editor/reference-guides/core-blocks/#excerpt, I see an easy fix: move the except_more filter logic into the if ( isset( $excerpt_length ) conditional. I suppose then my question is more of a general one: how would I go about overriding function render_block_core_post_excerpt (source: https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/post-excerpt/index.php)? Add logic in functions.php that checks for the existence of function render_block_core_post_excerpt, deregister it, then redefine and register the override? Would the action hook be init?

To be perfectly clear, I already have a excerpt_more filter in functions.php that works as expected in a Classic Theme style query loop that calls the_excerpt. It does not affect the results of <!-- wp:post-excerpt -->.


EDIT: For anyone curious, following from WordPress Buddha's answer, the override function I ended up using is as follows,

// Source: https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/post-excerpt/index.php
function render_block_core_post_excerpt_override($attributes, $content, $block)
{
    if (! isset($block->context['postId'])) {
        return '';
    }

    /*
    * The purpose of the excerpt length setting is to limit the length of both
    * automatically generated and user-created excerpts.
    * Because the excerpt_length filter only applies to auto generated excerpts,
    * wp_trim_words is used instead.
    */
    $excerpt_length     = $attributes['excerptLength'];
    $excerpt            = get_the_excerpt($block->context['postId']);

    if (isset($excerpt_length) && (str_word_count($excerpt) > $excerpt_length)) {
        $excerpt = wp_trim_words($excerpt, $excerpt_length);

        $more_text           = ! empty($attributes['moreText']) ? '<a class="wp-block-post-excerpt__more-link" href="' . esc_url(get_the_permalink($block->context['postId'])) . '">' . wp_kses_post($attributes['moreText']) . '</a>' : '';

        $filter_excerpt_more = static function ($more) use ($more_text) {
            return empty($more_text) ? $more : '';
        };

        /**
         * Some themes might use `excerpt_more` filter to handle the
         * `more` link displayed after a trimmed excerpt. Since the
         * block has a `more text` attribute we have to check and
         * override if needed the return value from this filter.
         * So if the block's attribute is not empty override the
         * `excerpt_more` filter and return nothing. This will
         * result in showing only one `read more` link at a time.
         */
        add_filter('excerpt_more', $filter_excerpt_more);

        $content               = '<p class="wp-block-post-excerpt__excerpt">' . $excerpt;
        $show_more_on_new_line = ! isset($attributes['showMoreOnNewLine']) || $attributes['showMoreOnNewLine'];
        if ($show_more_on_new_line && ! empty($more_text)) {
            $content .= '</p><p class="wp-block-post-excerpt__more-text">' . $more_text . '</p>';
        } else {
            $content .= " $more_text</p>";
        }
        remove_filter('excerpt_more', $filter_excerpt_more);
    } else {
        $content = '<p class="wp-block-post-excerpt__excerpt">' . $excerpt . '</p>';
    }

    $classes = array();
    if (isset($attributes['textAlign'])) {
        $classes[] = 'has-text-align-' . $attributes['textAlign'];
    }
    if (isset($attributes['style']['elements']['link']['color']['text'])) {
        $classes[] = 'has-link-color';
    }
    $wrapper_attributes = get_block_wrapper_attributes(array('class' => implode(' ', $classes)));

    return sprintf('<div %1$s>%2$s</div>', $wrapper_attributes, $content);
}

This function will,

  1. trim the excerpt if excerptLength is set and the number of words in the excerpt exceeds that value.
  2. add a 'Read More' link if the excerpt will be trimmed.
2
  • That function (render_block_core_post_excerpt()) is not pluggable, i.e. it's not wrapped with a if ( ! function_exists() ) check. You could override the post excerpt block's render_callback, but perhaps you can first try overriding the excerpt_length value (to max 40)? (That block also overrides the value, to 100, but only on the admin and REST API sides - see source)
    – Sally CJ
    Jan 31 at 5:20
  • 1
    Okay, these are good trees to bark up. Thanks!
    – JDQ
    Jan 31 at 5:22

1 Answer 1

2

To override the behavior of the render_block_core_post_excerpt function in a Block Theme, especially to customize the "Read More" link behavior based on the excerptLength, you'll need to unregister the original block and register your own customized version of it. This process involves a few steps:

Deregister the Original Block: You need to deregister the original post-excerpt block.

Register a Custom Block: Create and register a custom block that mimics the original block but includes your custom logic.

Override the Render Function: Implement your custom logic in the render callback for your new block.

Here's a basic outline of how you might approach this:

Step 1: Deregister the Original Block

You should deregister the original block. This is typically done on the init hook.

function wpb_custom_init() {
    unregister_block_type('core/post-excerpt');
}
add_action('init', 'wpb_custom_init');

Step 2: Register a Custom Block

Next, you'll need to register your custom block. You can mostly reuse the settings from the original block, but you'll replace the render callback with your own function.

function wpb_register_custom_post_excerpt_block() {
    register_block_type('core/post-excerpt', [
        // Copy the settings from the original block, but override the 'render_callback'
        'render_callback' => 'wpb_render_custom_post_excerpt',
    ]);
}
add_action('init', 'wpb_register_custom_post_excerpt_block');

Step 3: Implement the Custom Render Function

Now, implement your custom render function. This is where you'll add your custom logic to handle the "Read More" text based on the excerptLength.

function wpb_render_custom_post_excerpt( $attributes, $content ) {
    // Your custom logic here. You can refer to the original function's source code and modify as needed.

    // Example:
    $excerpt_length = isset($attributes['excerptLength']) ? $attributes['excerptLength'] : 55;
    $more_text = isset($attributes['moreText']) ? $attributes['moreText'] : 'Read More';

    $excerpt = get_the_excerpt();

    if (strlen($excerpt) > $excerpt_length && !empty($more_text)) {
        // Add your custom "Read More" logic here
    }

    // Return the modified excerpt
    return $excerpt;
}

In this function, you will apply your own logic to determine whether to show the "Read More" text based on the excerptLength. You can refer to the source code of the original render_block_core_post_excerpt function for guidance and modify it according to your requirements.

Remember, this approach requires a good understanding of how block types are registered and rendered in WordPress, as well as familiarity with PHP and the WordPress block editor's internals. Testing in a development environment first is highly recommended to ensure everything works as expected.

1
  • 1
    Excellent answer to both the general question and specific application.
    – JDQ
    Jan 31 at 21:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.