0

I'm trying to make it so there's a category that matches every page (excluding a couple).

So if I have the pages as such:

Pages

|-- Ninja Turtles
|      |--Turtles
|      |    |--Raphael
|      |    |--Leonardo
|      |    |--Donatello
|      |    |--Michelangelo
|      |
|      |--Other
|           |--Splinter
|           |--Shredder    
| 
|-- Painters
|      |--Rococo
|      |    |--Jean-Antoine Watteau
|      |    |--Francois Boucher
|      |
|      |--Renaissance
|           |--Raphael
|           |--Donatello
|           |--Michelangelo

Then I wanted to have categories with the exact same structure (also child/parent relations):

My plan is/was to make a function in functions.php and hook it onto the save_post-hook. So every time a page is saved, then it'll go through all pages and see if a category existed that had that name, - and if it didn't then create it. But I've encountered two problems:

  1. There's no built-in function called get_category_by_name() (only _by_slug, _by_path or _by_id).
  2. As can be seen on the page-tree above, then it needs to check along it's siblings only (since there's two pages called Raphael, with two different meanings). So every time I have a page, then I have to find a category that matches by title (somehow), and then check that the category's parent's title, matches the pages' parent's title. And in order to make the function perfect, then I have to do that all the way to the root-categories/pages.

So this quickly escalates in complexity.

Are there a better way (more light-weight-way) of achieving this?

  • get_categories accepts a name argument. You can also use get_term_by. Also have a look at the get_ancestors function which will give you a hierarchical array of object IDs for any type of object. – Milo Apr 9 '18 at 18:14
0

Here's how you do it... It's quite extensive. I have no idea if this is the best way or not, - but this is how I solved it. There might be a couple of errors in the code. But it works for me.

This is a quite specific wish for a functionality. Another way to go about it would be to just make the category.php-page do the same thing (since it basically does the same thing.

Generate a list of the pages that should be emitted from getting a category created

function generate_the_excluded_array() {

    global $excluded_pages;
    $excluded_pages = array(); // Add all pages with the search-template
    $search_pages = get_pages(array(
        'meta_key' => '_wp_page_template',
        'meta_value' => 'page-search.php'
    ));

    $excluded_pages[] = get_option( 'page_on_front' ); // Add home page
    foreach($search_pages as $page){
        // Add all pages that has the search-page-template activated
        $excluded_pages[] = $page->ID;
    }
}
add_action( 'after_setup_theme', 'generate_the_excluded_array' );

Upon save, - then get a list of all pages and iterate through them

function custom_set_category_on_cpt() {

    global $page_category_mapping;
    global $excluded_pages;
    $local_excluded_pages = implode(', ', $excluded_pages );

    $pages_args = array(
        'sort_order' => 'asc',
        'sort_column' => 'post_title',
        'hierarchical' => 1,
        'exclude' => $local_excluded_pages,
        'include' => '',
        'meta_key' => '',
        'meta_value' => '',
        'authors' => '',
        'child_of' => 0,
        'parent' => 0,
        'exclude_tree' => '',
        'number' => '',
        'offset' => 0,
        'post_type' => 'page',
        'post_status' => 'publish'
    );
    $pages = get_pages( $pages_args );
    foreach( $pages as $page ){
        custom_create_category_and_check_for_page_children( $page, 0 );
    }
    purge_categories(); // A function for deleting categories that don't have a corresponding page.

}
add_action( 'save_post', 'custom_set_category_on_cpt' );

A function for purging the categories

function purge_categories( $page_category_mapping_) {

    global $page_category_mapping;

    $category_args = array(
        'hide_empty' => 0,
        'exclude' => array(1),
    );
    $categories = get_categories( $category_args );
    foreach($categories as $category){

        $category_args = array(
            'name' => $category->name,
            'hide_empty' => false,
            'parent' => $category->category_parent
        );
        $category_array = get_terms( 'category', $category_args );
        $page_ID_with_corresponding_name = '';

        foreach ( $page_category_mapping as $key => $val ) {
            if( $category_array[0]->term_id === $val[ 'category_ID' ] ){
                $page_ID_with_corresponding_name = $key;
            }
        }
        if( empty( $page_ID_with_corresponding_name ) ) {
            wp_delete_category( $category_array[0]->term_id );
        }
    }
}

A function for determining if a page has children or not

function custom_page_has_children( $post_ID ) {
    $children = get_pages( array( 'child_of' => $post_ID ) );
    if( count( $children ) == 0 ) {
        return false;
    } else {
        return true;
    }
}

A (recursive) function for creating the category and check if that page has any children

function custom_create_category_and_check_for_page_children( $page, $page_parent ){

    custom_create_category_for_page( $page );
    if( custom_page_has_children( $page->ID ) ){
        $list_childpages_arg = array(
            'sort_order'   => 'asc',
            'sort_column'  => 'post_title',
            'hierarchical' => 1,
            'exclude'      => '',
            'include'      => '',
            'meta_key'     => '',
            'meta_value'   => '',
            'authors'      => '',
            'child_of'     => 0,
            'parent'       => $page->ID,
            'exclude_tree' => '',
            'number'       => '',
            'offset'       => 0,
            'post_type'    => 'page',
            'post_status'  => 'publish',
        );
        $childpages = get_pages( $list_childpages_arg);
        foreach( $childpages as $childpage ){
            custome_create_category_and_check_for_page_children( $childpage, $page->ID );
            custom_create_category_for_page( $childpage );
        }
    }
}

And this function has this as a part of it:

function custom_create_category_for_page( $page ){

    global $page_category_mapping;
    if( $page->post_parent == 0 ){
        $categorys_parents_ID = 0;
    } else {
        $categorys_parents_ID = $page_category_mapping[ $page->post_parent ]['category_ID'];
    }

    $category_args = array(
        'name' => $page->post_title,
        'hide_empty' => false,
        'parent' => $categorys_parents_ID
    );
    $category_array = get_terms( 'category', $category_args );
    $category_ID = $category_array[0]->term_id;

    if( empty( $category_array) ) {
        $category_ID = wp_create_category( $page->post_title, $categorys_parents_ID );
    }

    if ( ! array_key_exists( $page->ID, $page_category_mapping ) ){
        $page_category_mapping[ $page->ID ] = array(
            'category_ID' => $category_ID,
            'category_parent_ID' => $categorys_parents_ID
        );
    }
}

Creating a page-to-category-mapping, to be utilized other places

function build_page_category_mapping() {

    global $excluded_pages;
    $local_excluded_pages = implode(', ', $excluded_pages );
    // Initializing empty array
    $page_category_mapping = array();

    $pages_args = array(
        'sort_order'   => 'asc',
        'sort_column'  => 'post_title',
        'hierarchical' => 1,
        'exclude'      => $local_excluded_pages,
        'include'      => '',
        'meta_key'     => '',
        'meta_value'   => '',
        'authors'      => '',
        'child_of'     => 0,
        'parent'       => - 1,
        'exclude_tree' => '',
        'number'       => '',
        'offset'       => 0,
        'post_type'    => 'page',
        'post_status'  => 'publish'
    );

    $all_pages = get_pages( $pages_args );
    foreach ( $all_pages as $page ) {
        $page_ancestors_array = get_ancestors( $page->ID, 'page', 'post_type' );
        $page_depth = count( $page_ancestors_array );
// GOOD FOR DEBUGGING
//      echo '<br /><br /><br /><br /><br />';
//      echo $page->post_title . ' is the post title<br />';
//      echo 'Depth: ' . $page_depth . '<br />';
        if ( $page_depth == 0 ) {
            $page_parent_ID = 0;
            $page_parent_name = null;
            $categorys_parents_ID = 0;
        } elseif ( $page_depth == 1 ){
            $page_parent_ID = $page_ancestors_array[0];
            $page_parent_name = get_post( $page_parent_ID )->post_title;

            $parent_category_args = array(
                'name' => $page_parent_name,
                'hide_empty' => false,
                'parent' => 0
            );
            $categorys_parents= get_terms( 'category', $parent_category_args );
            $categorys_parents_ID = $categorys_parents[0]->term_id;

        }   else {
            // Depth > 1
            $page_ancestors_array = array_reverse( $page_ancestors_array );

            // Two variables to determine how deep in the tree 'we've gotten'.
            $pages_temp_topmost = -1;
            $pages_temp_nextonedown = -1;
            $page_parent_name = '';

            $i = 0; // Iteration counter

            $find_the_right_category_array = array(); // Array for the output (category ancestry).
            foreach ( $page_ancestors_array as $ancestor ) {
                if( $pages_temp_topmost === -1 && $pages_temp_nextonedown === -1 ){
                    $pages_temp_topmost = $ancestor;

                    $ancestor_category_args = array(
                        'name' => get_post( $ancestor )->post_title,
                        'hide_empty' => false,
                        'parent' => 0
                    );
                    $ancestor_category_array = get_terms( 'category', $ancestor_category_args );

                    if( count( $ancestor_category_array ) == 1){
                        $find_the_right_category_array[ $i ] = $ancestor_category_array[0];
                    } else {
                        throw new Exception("Exception code: 1 - This shouldn't be possible, -     but several categories was found with that parent and name");
                    }
                } elseif ( $pages_temp_topmost != -1 && $pages_temp_nextonedown === -1 ) {
                    $pages_temp_nextonedown = $ancestor;
                    $temp_var = (int) ($i - 1);
                    $ancestor_category_args = array(
                        'name' => get_post( $ancestor )->post_title,
                        'hide_empty' => false,
                        'parent' => $find_the_right_category_array[ $temp_var ]->term_id
                    );
                    $ancestor_category_array = get_terms( 'category', $ancestor_category_args );

                    if( count( $ancestor_category_array ) == 1){
                        $find_the_right_category_array[ $i ] = $ancestor_category_array[0];
                    } else {
                        throw new Exception("Exception code: 2 - This shouldn't be possible, -     but several categories was found with that parent and name");
                    }
                } else {
                    $temp_var = (int) ($i - 1);
                    $ancestor_category_args = array(
                        'name' => get_post( $ancestor )->post_title,
                        'hide_empty' => false,
                        'parent' => $find_the_right_category_array[ $temp_var ]->term_id
                    );
                    $ancestor_category_array = get_terms( 'category', $ancestor_category_args );
                    if( count( $ancestor_category_array ) == 1){
                        $find_the_right_category_array[ $i ] = $ancestor_category_array[0];
                    } else {
                        throw new Exception("Exception code: 3 - This shouldn't be possible, -     but several categories was found with that parent and name");
                    }
                }
                $i += 1;
            }
        end($find_the_right_category_array);
        $key = key( $find_the_right_category_array );
        $categorys_parents_ID = $find_the_right_category_array[ $key ]->term_id;
        }

        $category_args = array(
            'name' => $page->post_title,
            'hide_empty' => false,
            'parent' => $categorys_parents_ID
        );

        $category_array = get_terms( 'category', $category_args );
        // Since that can only return one category, then we can do the following:

        $category_ID = $category_array[0]->term_id;
// GOOD FOR DEBUGGING       
//      echo '<pre>';
//      echo 'Page: ' . $page->ID . ' - with the name: ' . $page->post_title . ' <br />';
//      echo 'Pages parent ' . $page_parent_ID . ' - with the name: ' . $page_parent_name . ' <br     />';
//      echo 'Category: ' . $category_ID . ' - with the name: ' . $page->post_title . ' <br />';
//      echo 'Categorys parent: ' . $categorys_parents_ID;
//      echo '</pre>';

        if ( ! array_key_exists( $page->ID, $page_category_mapping ) ){
            $page_category_mapping[ $page->ID ] = array(
                'category_ID' => $category_ID,
                'category_parent_ID' => $categorys_parents_ID
            );
        }
    }

    return $page_category_mapping;
}
add_action( 'init', 'build_page_category_mapping' );


// This might be redundant, but I'm not sure. 
global $page_category_mapping;
$page_category_mapping = build_page_category_mapping();

I know this is a huge mess... But it was the best (only) way I could make it happen.

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.