I don’t know if this is the optimal, it’s one suggestion.
For me I’d created the qa
post type as hierachical.
Then for every page in the book I’d created a child qa
post using menu_order
field to handle the page number.
This child has a 3rd level child for problems.
Hooking intro pre_get_posts
for qa
archive, you can set the post_parent
to 0, so in the archive view you get only books, and not the big list of pages and problems.
Last thing to is create a rewrite rule that convert the url into some query vars.
E.g.
<domain>/qa/<book-name>/<page-number>/<problem-number>
should be rewritten in
<domain>/index.php?name=<book-name>&post_type=qa&number=<page-number>&problem=<problem-number>
That url will end in a very simple query where post id = <problem-number>
.
Example code:
function my_book_rules() {
add_rewrite_rule( 'qa/([^/]+)/([0-9]+)/([0-9]+)$', 'index.php?&post_type=qa&name=$matches[1]&number=$matches[2]&problem=$matches[3]', 'top' );
add_rewrite_rule( 'qa/([^/]+)/([0-9]+)$', 'index.php?&post_type=qa&name=$matches[1]&number=$matches[2]', 'top');
}
add_action('init', 'my_book_rules');
function my_book_query_vars($vars) {
$vars[] = 'problem';
$vars[] = 'number';
return $vars;
}
add_filter('query_vars', 'my_book_query_vars');
function my_book_query( $query ) {
if ( is_admin() || ! is_main_query() ) return;
if ( is_archive() && $query->get('post_type') == 'qa') {
$query->set('post_parent', 0);
} elseif ( is_single() && get_post_type() == 'qa') {
$post = get_queried_object();
if ( $post->post_parent == 0 && $query->get('number') && ! $query->get('problem') ) {
// a page number is requested
$query->set('name', '');
global $wpdb;
$n = intval($query->get('number'));
$p = (int) $wpdb->get_var("SELECT ID FROM $wpdb->posts WHERE post_status="publish" AND post_type="qa" AND post_parent = $post->ID AND menu_order = $n");
$query->set('p', $n);
} elseif( $query->get('problem') ) {
// a problem is required
$query->set('name', '');
$n = intval($query->get('problem'));
$query->set('p', $n);
}
}
}
add_action('pre_get_posts', 'my_book_query');
In the single view template single-qa.php
you can load sub tempalte for books, pages or problems, something like:
if ( $post->post_parent == 0 ) {
get_template_part('book', 'content');
} else {
$parent = get_post( $post->post_parent );
if ( $parent->post_parent == 0 ) {
get_template_part('page-number', 'content');
} else {
get_template_part('problem', 'content');
}
}
Another snippet is for display the book title and the page number inside the problem template (problem-content.php
according to previous snippet),
$thepage = get_post( $post->post_parent);
$pagenumber = $thepage->menu_order
$the_book = get_post( $thepage->post_parent )->post_title
echo "The problem " . get_the_title() .
" is in the book " . $the_book->post_title .
" at the page number " . $pagenumber;
Note that the code is a proof of concept and untested.