0

This question is about a very specific circumstance.

  1. The site has one template file that can run a default category query with thumbnail, linked title and excerpt, or it can run a linked title only depending on a viewer choice. That is, once on the category default page, the viewer can click a List Only option. https:// domain/category/ glossary-of-wood-terms ?format=list This part works fine.

  2. The second option (linked title only) should pick up the whole dataset, not a paginated dataset.

  3. Using a pre_get_posts, I can set the query to get the whole dataset (not paginated) with

$query->set( 'posts_per_page', -1 );

and an if test that will only run that code on a particular URL. Here is where the problem lies.

It turns out that WordPress sees the address as https://domain/category/glossary-of-wood-terms?category_name=glossary-of-wood-terms with the query string hidden. But WordPress is treating it as a $_POST variable not a $_GET variable.

What address should I used in the pre_get_posts test?

Here is the code I originally added to functions.php

function get_all_terms($query) 
{
    // This will run for all main queries that are not in wp-admin.
    // You may want "is_archive()", "is_page('slug')" or some other condition.

    if(empty($Format))
      $Format = 'test 1';

    echo $Format;

    if ( ! is_admin() && $query->is_main_query() && ($Format == 'list'))
    {
        $query->set( 'posts_per_page', -1 );
    }
}
  add_action( 'pre_get_posts', 'get_all_terms' ); 

Here is code I added to see what WordPress shows as the URL. This code is in header.php. There is an echo on $current_url in the body

  global $wp; 
  if(!empty($_POST['format']))
    $Format = $_POST['format'];
  $current_url = add_query_arg( $wp->query_vars, home_url( $wp->request ));
  • Can you rephrase 1)? It's got a lot of options and it's very difficult to understand or visualize, I've re-read it a few times and don't understand what you meant, and it refers to things that haven't been mentioned. E.g. in 2) you refer to the second option but you've never mentioned this beforehand so it isn't clear what it is or where it comes from. Use the edit link under the post tags to modify the question so it's easier to understand and clearer – Tom J Nowell May 30 at 23:38
  • I also don't understand what you mean by treating it as a $_POST variable, WP doesn't handle query values differently if they're GET or POST. Can you include your full pre_get_posts filter in your question? It's very difficult to debug without seeing the code – Tom J Nowell May 30 at 23:40
  • Am I allowed to just add a link to the site? Sometimes I find the rules here to be subjectively enforced. – Nora McDougall-Collins May 31 at 18:16
  • Your answers may have actually pointed me to the answer. If, on the first instance, the page loads with the default archive query, but a viewer clicks the View List link, which has the query string ?format=list, can pre_get_posts run? That is does WordPress only reload the template file? – Nora McDougall-Collins May 31 at 18:25
  • pre_get_posts always run on queries, but as I said, your pre_get_posts filter is missing from your question, making it 1000x times more difficult to answer your question, if not impossible to answer. Update your question to include the code – Tom J Nowell May 31 at 20:40
1

Looking at the pre_get_posts hook, the problem becomes apparent, the $Format variable is being pulled out of thin air:

function get_all_terms($query) 
{
    if(empty($Format))
      $Format = 'test 1';

    echo $Format;

This variable is undefined, you cannot declare a variable in one file then use it in in other places like this. To do that you have to declare it as a global variable, and you have to declare this in every place you want to use it.

My recommendation: don't, there is no need for this variable, take a different approach.

Additionally, there's a misunderstanding about URLs. WordPress does not know or care what the URL by the time it gets to the pre_get_posts step, and sometimes there is no URL! E.g. in a WP CLI command.

Query variables are just the parameters that WP_Query takes. Rewrite rules convert pretty URLs into lists of query variables, and you can put a query variable in the URL to override most of the time. But query variables isn't meant to be a representation of what WP thinks the URL is. They can even disagree with the URL!

Instead, if you want to change behaviour based on the URL, just check the URL:

function nora_get_all_terms( \WP_Query $query ) {
    if ( !isset( $_GET['format' ] ) ) {
        return;
    }

Here we check the format URL parameter, and if it is not set, we return early. If it is set, continue the function and use $_GET['format'].

Some notes:

  • get_all_terms is a very generic name, at the very least prefix it or use a more specific name
  • global variables are bad, avoid them
  • Don't echo out, use a tool such as query monitor to look at the query variables, or better yet, use xdebug + and IDE such as PHPStorm to look at the variables directly with a debugger
  • keep variable names to lowercase, and follow the WP coding standards
  • A decent code editor will automatically indent for you, the codes indenting is broken and this can cause easy mistakes that are impossible when indenting is correct. At a minimum, fix indenting before showing the code to other people as broken indenting makes code difficult to read and understand
  • $_POST and $_GET are populated by PHP, not WordPress. $_POST won't be populated unless your browser made a POST request
  • Always start with the problem you were trying to solve, don't fall into the trap of an XY question
  • Consider looking into rewrite rules, you can add a custom query variable, and remove the URL parameter entirely by embedding it in the URL itself with a format/list on the end
  • whitelist and validate the format value, otherwise people can put malicious values in and if the format value is printed anywhere on the page you'll have a URL injection attack
| improve this answer | |
  • That is a marvelously comprehensive answer! I took the initial cheap trick way out by setting up a WordPress query object, but my client is always keen on doing things the best way where possible. So, your solution will be analyzed and used. – Nora McDougall-Collins Jun 1 at 20:15

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.