• Hi,

    I’m having an issue with custom taxonomies in that I need the entry for one custom taxonomy to be linked to the same entry in others.

    I am building a film review site and have set up taxonomies for Actors, Writers and Directors but of course, some people do both. At the moment, the taxonomies are all separate, so if someone clicks eg. Ben Affleck under the actor category, they will see a list of content where he is tagged as an actor.

    I’d like when a user on the front-end clicks on Ben Affleck in any category, they would see a list of everything he’s done as an actor, a writer and a director. Is it possible to link individual taxonomies across different sections like this? Or any way to have it appear that way on the front-end?

    Any help is much appreciated.

Viewing 15 replies - 16 through 30 (of 52 total)
  • Moderator bcworkz

    (@bcworkz)

    Yes, you’ve got it right, repeat 3 times with only the taxonomy changing. The idea is only one of the conditions could possibly be true, which ever related term that is will be assigned to $person. The rest do nothing for this request, but may be true for another request. It doesn’t appear to be working, probably because I’ve not correctly anticipated the default query vars. That can be solved.

    Adding if ( ! isset( $person )) return; after the 3 taxonomies code should solve the other posts not showing up problem.

    To solve the undefined person issue, please temporarily add the following right before the setting of post_type, but after the return line.
    echo '<pre>', print_r( $query->query_vars, true ), '</pre>';

    Make a typical request for a person’s taxonomy term archive, something like httр://example.com/actors/ben-affleck/ or what ever is appropriate for getting the desired results if it were working correctly. The echo/print_r output should appear at the very top of the page. If you just get a big blank area, view the page source (usually with ctrl/cmd-U) instead. Please copy the output and post it here. You may then comment out that one line. Leave it in as a comment though, we may still need it later. We are really close! Even if it still doesn’t work ??

    Thread Starter a_mulg

    (@a_mulg)

    I added if ( ! isset( $person )) return; and now the other pages work correctly.

    However, as soon as I add the echo/print_r line, I just get this error on every page: Parse error: syntax error, unexpected ‘echo’ (T_ECHO) .

    I think it’s in the right place, but just to confirm, my current function looks like this…

    if ( ! is_archive() && $query->is_main_query() )
          return
    
            echo '<pre>', print_r( $query->query_vars, true ), '</pre>';
    
            $query->set( 'post-type', array( 'post' , 'reviews' , 'trailer' , 'feature' ));
            
            $tentative = $query->get('actors');
              if ('' != $tentative ) $person = $tentative;
            
            $tentative = $query->get('writers');
              if ('' != $tentative ) $person = $tentative;
    
            $tentative = $query->get('directors');
              if ('' != $tentative ) $person = $tentative;
    
            if ( ! isset( $person )) return;
    
              $taxquery = array(
                'relation' => 'OR',
                  array(
                     'taxonomy' => 'actors',
                     'field'    => 'slug',
                     'terms'    =>  $person,
                  ),
                  array(
                     'taxonomy' => 'writers',
                     'field'    => 'slug',
                     'terms'    =>  $person,
                  ),
                  array(
                     'taxonomy' => 'directors',
                     'field'    => 'slug',
                     'terms'    =>  $person,
                  ),
              );
    
          $query->set( 'tax_query', $taxquery );
            
        }
    
    add_action('pre_get_posts','archive');
    Thread Starter a_mulg

    (@a_mulg)

    Ok for some reason the forum won’t let my replies through with my current code but I have added the echo line between…

    if ( ! is_archive() && $query->is_main_query() )
          return

    …and…

    $query->set( 'post-type', array( 'post' , 'reviews' , 'trailer' , 'feature' ));

    Which I think is the right place? At the moment it’s returning a parse error, unexpected echo, on all pages, with no other content coming through. Is it possible I’ve formatted it wrong and/or put it in the wrong place?

    Moderator bcworkz

    (@bcworkz)

    That’s the right spot. The problem actually isn’t the echo line, it’s there is no semi-colon after the return above! Just add one and it’ll be fine. return; It worked without before because the following $query->set() line returns a value, so PHP (almost) harmlessly conflated the two together. Insert the echo statement which doesn’t return anything and PHP throws a fit ??

    I forget terminating semi-colons all the time, so I’ve gotten used to the misleading message. Essentially, any time PHP complains of unexpected [anything], the problem isn’t what it’s complaining about, but somewhere before. Sometimes much before. There’s many examples of misleading error messages, and it’s not unique to PHP either ??

    Thread Starter a_mulg

    (@a_mulg)

    Ah, I knew it would be a simple formatting thing somewhere! There are no posts showing yet but the echo is spitting out the following…

    Array ( 
    	[actors] => ben-affleck 
    	[error] => 
    	[m] => 
    	[p] => 0 
    	[post_parent] => 
    	[subpost] => 
    	[subpost_id] => 
    	[attachment] => 
    	[attachment_id] => 0 
    	[name] => 
    	[static] => 
    	[pagename] => 
    	[page_id] => 0 
    	[second] => 
    	[minute] => 
    	[hour] => 
    	[day] => 0 
    	[monthnum] => 0 
    	[year] => 0 
    	[w] => 0 
    	[category_name] => 
    	[tag] => 
    	[cat] => 
    	[tag_id] => 
    	[author] => 
    	[author_name] => 
    	[feed] => 
    	[tb] => 
    	[paged] => 0 
    	[meta_key] => 
    	[meta_value] => 
    	[preview] => 
    	[s] => 
    	[sentence] => 
    	[title] => 
    	[fields] => 
    	[menu_order] => 
    	[embed] => 
    	[category__in] => Array 
    		( 
    		) 
    	[category__not_in] => Array 
    		( 
    		) 
    	[category__and] => Array 
    		( 
    		) 
    	[post__in] => Array 
    		( 
    		) 
    	[post__not_in] => Array 
    		( 
    		) 
    	[post_name__in] => Array 
    		( 
    		) 
    	[tag__in] => Array 
    		( 
    		) 
    	[tag__not_in] => Array 
    		( 
    		) 
    	[tag__and] => Array 
    		( 
    		) 
    	[tag_slug__in] => Array 
    		( 
    		) 
    	[tag_slug__and] => Array 
    		( 
    		) 
    	[post_parent__in] => Array 
    		( 
    		) 
    	[post_parent__not_in] => Array 
    		( 
    		) 
    	[author__in] => Array 
    		( 
    		) 
    	[author__not_in] => Array 
    		( 
    		) 
    )

    It looks like it isn’t pulling anything from the writers or directors taxonomies?

    Moderator bcworkz

    (@bcworkz)

    Yes, I wouldn’t expect it to pull in the other taxonomies at this point. That is the job of our subsequent code. Since that part has not changed, I wasn’t expecting this change would fix anything. I needed to see what query vars were set by default so I could determine exactly what we need to change.

    Please replace everything below the echo line (which should be commented out) and above the $taxquery assignment with this version:

            $tentative = $query->get('actors');
              if ('' != $tentative ) {
                  $person = $tentative;
                  unset( $query->query_vars['actors']);
              }        
            $tentative = $query->get('writers');
              if ('' != $tentative ) {
                  $person = $tentative;
                  unset( $query->query_vars['writers']);
              }        
            $tentative = $query->get('directors');
              if ('' != $tentative ) {
                  $person = $tentative;
                  unset( $query->query_vars['directors']);
              }
            if ( ! isset( $person )) return;
    
            $query->is_tax = false;

    I’m hoping this change will finally get the results you’ve been so patiently waiting for.

    Thread Starter a_mulg

    (@a_mulg)

    Ok, I’ve tried the above code and still having some issues, but think I’ve managed to narrow it down to which line is causing problems!

    When I got to /actors/ben-affleck now, it’s directing me to the 404 page. However, when I remove the $query->is_tax = false; line, I’m seeing the correct archive, though no posts are displaying.

    Just to make sure I’ve put everything in the right place, my current code looks like this…

    if ( ! is_archive() && $query->is_main_query() )
          return;
    
            // echo '<pre>', print_r( $query->query_vars, true ), '</pre>';
            
            $tentative = $query->get('actors');
              if ('' != $tentative ) {
                  $person = $tentative;
                  unset( $query->query_vars['actors']);
              }        
            $tentative = $query->get('writers');
              if ('' != $tentative ) {
                  $person = $tentative;
                  unset( $query->query_vars['writers']);
              }        
            $tentative = $query->get('directors');
              if ('' != $tentative ) {
                  $person = $tentative;
                  unset( $query->query_vars['directors']);
              }
            if ( ! isset( $person )) return;
    
            $query->is_tax = false;
    
              $taxquery = array(
                'relation' => 'OR',
                  array(
                     'taxonomy' => 'actors',
                     'field'    => 'slug',
                     'terms'    =>  $person,
                  ),
                  array(
                     'taxonomy' => 'writers',
                     'field'    => 'slug',
                     'terms'    =>  $person,
                  ),
                  array(
                     'taxonomy' => 'directors',
                     'field'    => 'slug',
                     'terms'    =>  $person,
                  ),
              );
    
          $query->set( 'tax_query', $taxquery );
            
        }
    
    add_action('pre_get_posts','archive');
    Moderator bcworkz

    (@bcworkz)

    Yeah, that looks correct. I wasn’t too sure about the $query->is_tax part. It didn’t make sense but came through as false for similar queries on my site. Good sleuthing to isolate the 404 cause! At least nothing is broken now, but getting no posts is disappointing. A very similar setup on my site worked fine with this. Naturally that does not necessarily translate to yours since we have different data.

    I’d like to investigate the resulting query now. That should tell us why it’s not working. Add the following code below our current code, below the add_action() call. It’ll show up just like the query vars on the page, except it’ll be a long SQL query instead of an array listing.
    add_filter('posts_request', function( $sql ) { echo "<pre>$sql</pre>\n"; return $sql; });

    The same deal, once you get the query copied into a post here, you can comment out that line.

    Thread Starter a_mulg

    (@a_mulg)

    Ok cool, I’ve done that and got the following…

    SELECT SQL_CALC_FOUND_ROWS wp_posts.* FROM wp_posts LEFT JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) WHERE 1=1 AND (
    wp_term_relationships.term_taxonomy_id IN (268) 
    OR wp_term_relationships.term_taxonomy_id IN (1992) 
    OR wp_term_relationships.term_taxonomy_id IN (1993) 
    ) AND wp_posts.post_type IN ('post', 'trailer', 'feature', 'reviews') AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private') GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10

    While I’m not sure what most of this means, I can see the taxonomy ID 268 does match Ben Affleck’s ID in the Actors taxonomy.

    Moderator bcworkz

    (@bcworkz)

    The term_taxonomy_id is different from the term_id we are used to seeing and working with. It’s mainly used internally by WP. Verifying the IDs is a bit convoluted. You will need to use phpMyAdmin to investigate. It’s usually accessed through your hosting control panel. If you are on localhost with XAMPP or similar, the server control widget should have a mySQL database admin button which gives you access. Sometimes you can just type localhost/phpmyadmin/ or 127.0.0.1/phpmyadmin/ in your address bar. Or whatever hosts name you use to access a localhost installation.

    Look at your wp-config.php file and double check your database name. You may need the user and password too. When you go to phpMyAdmin, click on the same database in the left nav sidebar. Click on the wp_term_taxonomy table and locate the proper ID. The search tab is useful if there are a lot of rows to look through. Verify the taxonomy is correct and note the term_id.

    Go to the wp_terms table and locate the correct term_id. Is the corresponding name or slug correct? Do so for each ID used in the query (268, 1992, 1993). I suspect that this will all check out.

    What looks queer to me is the wp_posts.post_author = 1 AND part. If all posts were authored by user 1, (the first user created during installation) that’s not so bad. It’s that it’s stuck into a series of post status OR clauses. That is not where such a clause belongs! As it sits, it’s saying get posts where either status is published AND private OR where post author=1 AND status is private. The first is impossible and the second unlikely!

    Furthermore, the author query vars from pre_get_posts are not set, so this author clause is being added after WP builds the query. Possibly added through a hook into ‘posts_where_request’ or ‘posts_clauses_request’. Does any of this sound familiar? Is there any reason any query beyond an author’s archive that would need to be restricted to author 1 only? This feels like a poorly implemented hack.

    You could rule out any plugins doing this be deactivating all of them, then checking the query again. If the author clause goes away, a plugin is at fault. Reactivate them one by one until the clause reappears. The last activated plugin is at fault.

    If it’s still there with no plugins, your theme is at fault. Go through its functions.php and any files you see included or required by code on that page. Look for any posts_* filter or action hooks involving author = 1. While we could strip it out of the query from ‘posts_request’, it’d be better to eliminate it at the source.

    Thread Starter a_mulg

    (@a_mulg)

    Yep, the taxonomy_term_id, term_id and slug all match up for all three IDs.

    I’ve checked the posts_where_request and _posts_clauses_request and they seem to be untouched. There’s nothing else hooked into them that I can see. Every post on the site at the moment is by author 1 but that won’t always be the case and I haven’t set anything to restrict it to author 1, at least not on purpose!

    I’ve searched my theme for instances of author and the only ones that appear are <?php echo get_author_posts_url( get_the_author_meta( 'ID' ) ); ?> and <?php the_author(); ?>, which I’m using across a few different template files. I can’t see anything untoward in my functions file either.

    I’ve also tried disabling plugins, however there are two that I need to keep active or my custom posts and taxonomies will stop working. With the others disabled, the author still displays in the query.

    Moderator bcworkz

    (@bcworkz)

    One solution would be to have a hook into “posts_request” that searches for “wp_posts.post_author = 1 AND” and replaces it with an empty string. The resulting query will then be correct. A possible drawback is any future author specific searches involving user ID 1 will not work correctly. If all posts have this author, removal would have no effect because it would cause an author 1 query to return all posts, even if other authors would be introduced.

    String patching like this is inherently weak, but if it breaks in the future, the fix is simple to implement. The problem is recalling this code weak code is in there and is the likely cause of queries not working. It ought to be OK though, I’m just thrilled about the solution. It’s all I can think of if the actual source cannot be found. Replace the SQL echo code we used for debugging this with:
    add_filter('posts_request', function( $sql ) { return str_replace('wp_posts.post_author = 1 AND ', '', $sql ); });

    That ought to do it, but I could be wrong since I’ve no good way to test this.

    Thread Starter a_mulg

    (@a_mulg)

    Hmm unfortunately there are still no posts showing. I’ll copy in the code I’ve got in case there’s some issue with placement…

        if ( ! is_archive() && $query->is_main_query() )
          return;
            
            $tentative = $query->get('actors');
              if ('' != $tentative ) {
                  $person = $tentative;
                  unset( $query->query_vars['actors']);
              }        
            $tentative = $query->get('writers');
              if ('' != $tentative ) {
                  $person = $tentative;
                  unset( $query->query_vars['writers']);
              }        
            $tentative = $query->get('directors');
              if ('' != $tentative ) {
                  $person = $tentative;
                  unset( $query->query_vars['directors']);
              }
            if ( ! isset( $person )) return;
    
              $taxquery = array(
                'relation' => 'OR',
                  array(
                     'taxonomy' => 'actors',
                     'field'    => 'slug',
                     'terms'    =>  $person,
                  ),
                  array(
                     'taxonomy' => 'writers',
                     'field'    => 'slug',
                     'terms'    =>  $person,
                  ),
                  array(
                     'taxonomy' => 'directors',
                     'field'    => 'slug',
                     'terms'    =>  $person,
                  ),
              );
    
          $query->set( 'tax_query', $taxquery );
            
        }
    
    add_action('pre_get_posts','archive');
    
    add_filter('posts_request', function( $sql ) { return str_replace('wp_posts.post_author = 1 AND ', '', $sql ); });

    The contents of my archive page are just pasted from the default, with the pre_get_posts added in. I tried removing that line, thinking it might just display the results from one taxonomy as normal, but it still didn’t display posts so perhaps there’s an issue with my archive template? Here is the code…

    <?php
    /*
    Template Name: Archives
    */
    get_header(); ?>
    
    <div id="container">
    	<div id="content" role="main">
    
    		<?php add_action( 'pre_get_posts', 'archive' ); ?>
    
    		<?php the_post(); ?>
    		<h1 class="entry-title"><?php single_tag_title() ?></h1>
    		
    		<h2>Archives by Month:</h2>
    		<ul>
    			<?php wp_get_archives('type=monthly'); ?>
    		</ul>
    		
    		<h2>Archives by Subject:</h2>
    		<ul>
    			 <?php wp_list_categories(); ?>
    		</ul>
    
    	</div><!-- #content -->
    </div><!-- #container -->
    
    <?php get_footer(); ?>
    Moderator bcworkz

    (@bcworkz)

    Ooo! Good thinking, there is an issue with that template! I was expecting a “normal” archive template that runs a Loop, like the one in the twentysixteen theme for example. Yours is more like the old archives.php (plural) that used to be used in older themes to list time based links that led to archive (singular) templates that ran loops.

    Is there not an archive.php (singular) template that runs a loop? Isn’t the one you posted archives.php (plural)? Are you sure the one you posted is the one your theme is using for a person request? Try temporarily putting “Hello world!” or whatever on the template in an HTML area, such as right after the “content” div. Clear your browser cache, then make a person request and verify Hello world! appears near the top of the page, but below the header content. If not, you have the wrong template.

    I had one other thought, the author removal patch code I provided could be executing before another filter is adding it. Lets change my code a bit to ensure it runs later. At the end of the line, it looks like this: ); });. Please insert a comma 100 like so: ); }, 100 );

    I’d also like to double check the resulting SQL again. Please add back the following debug code that the author patch code replaced. This version is slightly different to ensure it runs last, even later than the author patch code:
    add_filter('posts_request', function( $sql ) { echo "<pre>$sql</pre>\n"; return $sql; }, 1000 );

    Like before, post the SQL output that appears at the top of the page here. Thanks.

    Thread Starter a_mulg

    (@a_mulg)

    So I think there’s some progress, though it’s still not really working yet! You’re right about the archive page, although I was on archive.php, I had copied the code into it from archives.php – doh! However, I have now reverted back to my original archives.php file but swapped out the query for this:

    <?php add_action( 'pre_get_posts', 'archive' ); ?>
    
    		<?php if ( have_posts() ) : ?>

    Now, the interesting part is, when I unpublish any posts featuring Ben Affleck tagged as a writer or director, it returns only one post featuring him as an actor. However, when I republish his director and writer posts, no posts show at all. This is all via the /actors/ben-affleck URL; no posts show at all on /writers/ben-affleck and /directors/ben-affleck, regardless of what is published and what isn’t. Apologies if that is quite confusing but I feel it’s worth mentioning just in case!

    I’ve added the amendment to the author removal patch and added in the the debug code. I think what it has returned is the same as before…

    SELECT SQL_CALC_FOUND_ROWS wp_posts.* FROM wp_posts LEFT JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) WHERE 1=1 AND (
    wp_term_relationships.term_taxonomy_id IN (268) 
    OR 
    wp_term_relationships.term_taxonomy_id IN (1992) 
    OR 
    wp_term_relationships.term_taxonomy_id IN (1993) 
    ) AND wp_posts.post_type IN ('post', 'trailer', 'feature', 'reviews') AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10
Viewing 15 replies - 16 through 30 (of 52 total)
  • The topic ‘Linking custom taxonomies’ is closed to new replies.