JonnyBorg JonnyBorg - 1 day ago 5
PHP Question

Get the_excerpt of a child page from within a parent page

Having trouble getting the excerpt of a child page from within a parent page. I have a static parent page, that within, lists excerpts of all child pages that are hierarchal of that parent.

For some reason I can return each title and publish date of each individual child page. But when I try to echo get_the_excerpt or the_excerpt all I get is the excerpt or content of the parent-page for every child page listed. Also I'm converting the standard excerpt into a custom trimmed excerpt. This worked on the "front-page" but not in a standard "parent-page".

Not sure what I'm doing wrong or overlooking.

This is what appears... note: "The parent page content" repetition.


Parent Page Title



The parent page content


Test Child Page Title A



The parent page content

Nov 16, 2016

Test Child Page Title B



The parent page content

Oct 5, 2016



Using this solution:

<?php
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>

// Begin listing all child pages
$childpages = get_pages( array( 'child_of' => $post->ID, 'sort_column' => 'post_date', 'sort_order' => 'desc' ) );
foreach( $childpages as $childpage ) { ?>
<div class="paged-entries">
<div class="entry">
<h3 class="page-headline"><?php echo $childpage->post_title; ?></h3>
<!-- .entry-summary -->
<div class="post-excerpt">
<?php
$content = get_extended( $childpage->post_content );
$page_excerpt = wpse0002_custom_wp_trim_excerpt($content);
//echo wpse0002_custom_wp_trim_excerpt($content); // echoes the word "Array" only.
//echo $page_excerpt = wpse0002_custom_wp_trim_excerpt($content); // echoes the word "Array" only.
//echo $content['main']; // prints the full child post expected.
//print_r ($page_excerpt); // prints full post with some php wrapping: Array ( [main] => This is some fresh music. [extended] => [more_text] => )
print_r( $childpage->post_content ); // prints the full child post expected.
?>
</div>
<!-- end .entry-summary -->
<h6>Published: <?php echo date("M Y", strtotime($pfx_date = get_the_date( $format, $childpage->ID ))); // d m y ?></h6>
</div>
</div>
</article>
<?php
}
?>


This is what I'm after:


Parent Page Title



The parent page content


Test Child Page Title A



The child page excerpt A

Nov 16, 2016

Test Child Page Title B



The child page excerpt B

Oct 5, 2016



Here is my custom Trimmer

// Custom Excerpts

remove_filter('get_the_excerpt', 'wp_trim_excerpt');
add_filter('get_the_excerpt', 'wpse_custom_wp_trim_excerpt');
$wpse_excerpt = strip_tags($wpse_excerpt, wpse_allowedtags());
function wpse_allowedtags() {
return '<video>,<audio>,<embed>,<iframe>,<figure>,<figcaption>';
}

if ( ! function_exists( 'wpse0002_custom_wp_trim_excerpt' ) ) :

function wpse0002_custom_wp_trim_excerpt($wpse0002_excerpt) {
global $post;
$raw_excerpt = $wpse0002_excerpt;
if ( '' == $wpse0002_excerpt ) {

$wpse0002_excerpt = get_the_content('');
$wpse0002_excerpt = strip_shortcodes( $wpse0002_excerpt );
$wpse0002_excerpt = apply_filters('the_content', $wpse0002_excerpt);
$wpse0002_excerpt = substr( $wpse0002_excerpt, 0, strpos( $wpse0002_excerpt, '</p>' ) + 4 );
$wpse0002_excerpt = str_replace(']]>', ']]&gt;', $wpse0002_excerpt);
$wpse0002_excerpt = strip_tags($wpse0002_excerpt, wpse_allowedtags()); /*IF you need to allow just certain tags. Delete if all tags are allowed */

return $wpse0002_excerpt;

}
return apply_filters('wpse0002_custom_wp_trim_excerpt', $wpse0002_excerpt, $raw_excerpt);
}

endif;

remove_filter('get_the_excerpt', 'wp_trim_excerpt');
add_filter('get_the_excerpt', 'wpse0002_custom_wp_trim_excerpt');

Dre Dre
Answer

At the risk of sounding patronising, I'm going to over-explain everything so that other folks might find this useful. Having gone through it all, I've got a pretty good idea of what you're trying to achieve: there's more than one way to go about it, so rather than just giving you a chunk of code, I'm going to walk through the logic behind it all so you can pick the bits that are applicable.

Debugging

Firstly, you've got a bunch of PHP errors all over the place, which is leading to some unpredictable behaviour, which suggests you don't have debugging turned on. Add the following to wp-config.php:

// Enable WP_DEBUG mode
define( 'WP_DEBUG', true );

This will display warnings and help you track down where the errors are coming form. One notable error is that you're calling wpse0001_custom_wp_trim_excerpt() without passing an argument.

Excerpts

By default, pages -- unlike posts -- don't have a separate excerpt field, which is why you've been using get_extended() to extract the content before the more tag. However, you can add the excerpt field to pages by adding the following to functions.php. I'd recommend doing this, as it makes all the subsequent excerpt-related behaviour easier. It also means WP should automatically handle the fallbacks for pages that don't have excerpts; no sense re-inventing the wheel.

/**
 * Adds excerpt field to pages
 * @args void
 * @returns void
 */
function my_add_excerpts_to_pages() {
     add_post_type_support( 'page', 'excerpt' );
}
add_action( 'init', 'my_add_excerpts_to_pages' );

In the admin area, you might need to check the Excerpt option under Screen Options to display the field. This lets you use WP's default functions and filters for displaying and manipulating excerpts. It also populates the $post->post_excerpt key, so you can use: echo $post->post_excerpt if you ever need to output it without filtering.

The other 'gotcha' with excerpts is that get_the_excerpt() has to be used in the loop; you can't pass it an ID of a post. I'm pretty sure you used to be able to do that and it's since been deprecated: methinks the online documentation needs updating, so I'll try and log a change there to spare others confusion in the future. C'est la vie. That brings us on to our next topic...

Secondary Loops

You're using get_pages() to fetch the child pages, which is fine, but because of the way excerpts work you're stuck having to use your own functions for fetching and filtering them. Another approach would be to use WP_Query rather than get_pages(). As it is the class that powers get_pages(), it provides you with some additional control. In this instance, we're interested in the fact that it overwrites the global $post variable, which allows the_excerpt() and get_the_excerpt() to work correctly. Here's how you can create a secondary loop to fetch the correct child pages:

$child_args = array( 
    'post_parent' => $post->ID,     // This assumes you're calling this on the parent music page
    'sort_column' => 'post_date',
    'sort_order' => 'desc',
    'ignore_sticky_posts' => true,  // Don't display any sticky posts
    'post_type' => 'page'           // Only fetch pages, not posts
); 
$child_query = new WP_Query( $child_args );

// Usual Loop logic
if ( $child_query->have_posts() ) : while ( $child_query->have_posts() ) : $child_query->the_post();

    // You can pretty much do whatever you want in here...
    the_title();    // Should output the child page title
    the_excerpt();  // Should output the child page excerpt
    $excerpt = get_the_excerpt(); // Assigns the excerpt to a variable in case you want to do other things to it.


    endwhile; else: 

    // 'No posts' content would go here

endif;

wp_reset_postdata(); // This is important; it sets $post back to the parent page. Always reset it after a loop.

You'd probably want to put this in page-music.php, but my personal preference would be to put that in a template part and then call it using get_template_part(), which would allow you to easily re-use it on other pages. For example, you should be able to use it the featured design page without having to change a thing.

Also, note the use of wp_reset_postdata(): you need to do this at the end of the loop, as it reset the $post back to the original page. You want to also do this on front-page.php, just after your loop for music pages (round line 81).

For more info on WP_Query you can check out this SO answer

Filtering

Now, if you use secondary loops as described above, you can rely on $post being present. This lets you apply conditional filtering on the excerpt, based on the tags, parent page etc. To save having to create different output functions based on the page parent -- as you're currently doing -- you can instead apply a filter function, by adding this to functions.php:

/**
 * Custom function for when we need to filter the excerpt differently for particular pages
 * Needs to be used within the loop
 * @args $excerpt(string)       - The incoming excerpt to filter
 * @args $post(obj)             - The post that the excerpt comes from (WP 4.5+)
 * @returns $excerpt(string)    - The filtered excerpt
 */
if ( !function_exists('custom_excerpt_filter')) {
    function custom_excerpt_filter($excerpt='', $post=null) {

        // Fallback for WP 4.4 and below, which don't pass $post
        if (!$post) {
            global $post;
        }

        // Get the unfiltered excerpt if you really want it:
        // if ( !empty($post->post_excerpt) ) {
        //  $excerpt = $post->post_excerpt; // Use an excerpt if there is one.
        // } else {
        //  $excerpt = get_extended($post->post_content)['main']; // Use the content before
        //}


        // Now you can do whatever string manipulations you want with $excerpt here

        // Filtering based on tags...
        if ( has_tag('featured-music',$post) ) {
            // Could apply additional filtering for music page excerpts here
        }

        // Or filtering based on the parent page...
        $parent = get_page_by_path('featured-music'); // Get parent post via the slug
        if ( $post->post_parent == $parent->ID ) {
            // Additional filtering for music child pages here...
        }

        return apply_filters('the_excerpt', $excerpt, $post); // Apply other filters so they can be chained
    }
}
add_filter('get_the_excerpt','custom_excerpt_filter', 10, 2);

I've left out the actual string manipulations on $excerpt as you can do pretty much what you like with it.

Now when you use the_excerpt() within the loop, your filter can take into account the page tag/parent of the excerpt in question and modify it how ever you want. This way your logic is kept all in one place, and is applied consistently across all templates.

Inserting media into the excerpt

Since you want to insert video and audio into your excerpt, you'll hit a snag when using the excerpt field in the admin: it won't let you insert any HTML tags. Not to worry, you can still insert it in the content, and extract via our filter. We'll can use get_media_embedded_in_content() to extract it from the page content, and then insert it into the excerpt:

/**
 * Custom function for when we need to filter the excerpt differently for particular pages
 * Needs to be used within the loop
 * @args $excerpt(string)       - The incoming excerpt to filter
 * @args $post(obj)             - The post that the excerpt comes from.
 * @returns $excerpt(string)    - The filtered excerpt
 */
if ( !function_exists('custom_excerpt_filter')) {
    function custom_excerpt_filter($excerpt='', $post=null) {


        // Fallback for WP 4.4 and below, which didn't pass $post
        // Assumes you're calling this in the loop
        if (!$post) {
            global $post;
        }

        // Get the unfiltered excerpt if you really want it:
        // if ( !empty($post->post_excerpt) ) {
        //  $excerpt = $post->post_excerpt; // Use an excerpt if there is one.
        // } else {
        //  $excerpt = get_extended($post->post_content)['main']; // Use the content before
        //}


        // Now you can do whatever string manipulations you want with $excerpt here

        // Filtering based on tags...
        if ( has_tag('featured-music',$post) ) {
            // Could apply additional filtering for music page excerpts here
            $excerpt .= ' has featured music tag';
        }

        // Filtering based on parent...
        $parent = get_page_by_path('featured-music'); // Get parent post via the slug

        if ( $post->post_parent == $parent->ID ) {

            $excerpt = ''; // We ignore the current excerpt as we're making our own

            // First we grab all the content
            $content = apply_filters( 'the_content',  $post->post_content); // Apply content filters so shortcodes get applied

            // Get the audio
            $audio = get_media_embedded_in_content($content, array('audio')); // Returns an array
            if (!empty($audio)) { 
                $audio = $audio[0]; // Since we want the first audio embed, grab the first element in the array
                $excerpt .= $audio; // Add the audio to the excerpt
            } 

            // Get the video
            $video = get_media_embedded_in_content($content, array('video', 'iframe'));
            if (!empty($video)) {
                $video = $video[0];
                $excerpt .= $video; // Add the video to the excerpt
            }

            // Now to get the first paragraph
            // There's a bunch of ways you can do this; you just need to bear in mind that there's media in the content that you'll need to work around
            // Here's one possible way:

            // First, strip out all media...
            $content = preg_replace("/<img[^>]+\>/i", "", $content);
            $content = preg_replace("/<iframe[^>]+\>/i", "", $content);
            $content = preg_replace("/<audio[^>]+\>/i", "", $content);

            // ...here we're relying on the fact that Wp doggedly wraps everything in <p> tags 
            // Explode the content into an array
            $content = explode('</p>', $content);

            $excerpt .= $content[0];
        }

        return apply_filters('the_excerpt', $excerpt, $post); // Apply other filters so they can be chained
    }
}
add_filter('get_the_excerpt','custom_excerpt_filter', 10, 2);

Getting the first paragraph

The last thing you want to wrap it all up is to grab the first paragraph. There's a bunch of different ways you could do this; the above function includes one method. You could also:

  1. Grabbing the content before the more tag using get_extended() and then strip out any media using wp_strip_all_tags().
  2. Use the separate excerpt field.

Conclusion

By using WP_query and a bit of logic inside your filtering function, you can simplify a lot of your templates, which in turn should in turn help you get everything working correctly. Hopefully it all helps!

Comments