2

I've created the following pages and hierarchy:

  • Grandparent
    • Parent
    • Parent
    • Parent
      • Child
      • Child
      • Child
    • Parent
    • Parent
  • Grandparent

The Grandparent pages have an ACF custom field to pick the colour. ACF Page Type

I'd like that colour selection to be applied for the Grandparent, Parent and Child pages.

Within the loop, I've written the following code to grab the Grandparent colour selection:

<div class="<?= get_field('colour', $post->post_parent); ?>">

Let's say I've picked the colour "red".

Here's the front-end result:

  • Grandparent (Red)
    • Parent (Red)
    • Parent (Red)
    • Parent (Red)
      • Child (Null)
      • Child (Null)
      • Child (Null)
    • Parent (Red)
    • Parent (Red)
  • Grandparent

The colour is only being applied to the Grandparent and Parent Pages. Child pages are returning no colour (null).

How do I include the Child pages too?

3

As far as I'm aware there's no limit to how many levels of ancestry Pages can have, so any solution should not depend on specifically referencing a particular ancestor. The best solution would be to get a list of all ancestors for the page and loop over them until you find a value for your field. Then stop looping over them and return the found value. The function get_post_ancestors() can be used for this:

$colour = null;

/**
 * Get the IDs of the page's ancestors into a list.
 */
$page_ids = get_post_ancestors( $post );

/**
 * Put the current page's ID at the beginning of the list so we don't have to 
 * handle it separately.
 */
array_unshift( $page_ids, $post->ID );

/**
 * Loop over pages and find the first one with a colour.
 */
foreach ( $page_ids as $page_id ) {
    $maybe_colour = get_post_meta( 'colour', $page_id, true );

    /**
     * If the page has a value for colour, put it in the colour variable and 
     * stop the loop.
     */
    if ( $maybe_colour ) {
        $colour = $maybe_colour;
        break;
    }
}

echo $colour;

To save space in your template you could add this to a function and use the function in your template instead:

function wpse_310439_get_the_colour( $post = 0 ) {
    $post = get_post( $post );

    $page_ids = get_post_ancestors( $post );

    array_unshift( $page_ids, $post->ID );

    $colour = null;

    foreach ( $page_ids as $page_id ) {
        $maybe_colour = get_post_meta( 'colour', $page_id, true );

        if ( $maybe_colour ) {
            $colour = $maybe_colour;
            break;
        }
    }

    return $colour;
}

Then in your template:

<div class="<?php echo esc_attr( wpse_310439_get_the_colour() ); ?>">
1

I'm not quite sure how your grandparent pages are getting the style since your code says to grab the field from the parent. The grandparents' post parents should be 0, meaning they don't have parents. Perhaps there's something I'm missing about your hierarchy.

A conditional should work here:

(assumes you've already specified global $post in your template):

<div class="<?php
// if this is a top-level post, grab its field
if($post->post_parent == 0) { ?>
    // output the field - no need to pass an ID as it is the ID of this post
    the_field('colour');
// if this is not a top-level post (child, grandchild, or so forth)
} else {
    // get all of this post's ancestors: parent, grandparent, and so forth
    $ancestors = get_post_ancestors();
    // get the last item in the array - the "top" parent (grandparent)
    $id = end($ancestors);
    // now pull its field
    the_field('colour', $id);
} ?>
">

A non-ACF way to achieve the same effect would be to add your desired classes to the <body> class in functions.php:

<?php // add a filter to the body_class() function
add_filter('body_class', 'wpse_310439');
function wpse_310429($classes) {
    // pull ancestors of the current page/post/cpt
    $ancestors = get_post_ancestors();
    // if this is a particular page ID, or a child of that ID
    if(is_page('12345') || end($ancestors) == '12345') {
        // add a class to the <body>
        $classes[] = 'blue';
    } elseif(is_page('54321') || end($ancestors == '54321') {
        $classes[] = 'yellow';
    }
    // always return $classes so body gets regular classes
    return $classes;
} ?>

With this way you're setting the classes in theme files, and you'll also need to use the cascade to target whichever div needs the coloring (instead of .blue you would use something like body.blue div.styleme), but no plugin is needed. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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