Support » Plugins » Hacks » pre_get_posts AND add_query_arg

  • chmchm


    Just trying to sort results, I don’t seem to be able to make these two play nice.
    If I change the sort order of the results using pre_get_posts the add_query_arg either makes no changes or returns no results. Actually, when I reset the query or try to edit it at all, I get no joy with adding the query args.

    I am trying to make the default of my custom post type return most popular on a field called views in the meta table but I also want to be able to let my users sort by title and by rating.

    Thanks in advance for any information!

Viewing 3 replies - 1 through 3 (of 3 total)
  • Moderator bcworkz


    You use add_query_arg() to generate an URL reflecting the user choice of sorting. In order to work with the default behavior set in ‘pre_get_posts’, your callback must check the current query vars for those choices set by add_query_arg() and not change anything when found.

    If not found, use the set() method on the passed query object to cause the default orderby to be by meta value in views. Assuming your callback assigns the passed query object to the variable $query, do something like this in your callback:

    $query->set('orderby', 'meta_value_num');
    $query->set('meta_key', 'views');



    Can I clarify?

    If you set(), you can’t then use add_query_arg() to modify what you just set.

    If I wanted a default on views, I would then need to create separate query for any other sort options, like by title or rating. I would not be able to use add_query_arg().

    Would you know what the difference is in speed of use in running a new query for sort options versus using add_query_arg()? I am assuming that add_query_arg() modifies, not re-queries.

    Thanks again!

    Moderator bcworkz


    Your latest statements are more or less correct, but it appears you do not truly understand how these various ways of altering page results really work. I’ll try to explain, which I hope will serve your better than brief answers with no explanation.

    You probably know how a basic request works in WP, but I’ll go over it so we are starting on the same ground. A user types an URL or clicks a link with an URL in their browser. This HTTP request is sent to your server. The server tries to find an associated file, which if the request is for WP content, the search will fail, so the request is sent to WP’s index.php. This causes WP to parse the URL and based on what it finds in the URL, assigns the values to various query variables, or “query vars”. The query vars are used to build a SQL query that will best retrieve the records matching the URL parameters. The DB is queried, the results returned, and displayed via the “Loop”.

    What add_query_arg() does is modify the link a user clicks. Thus it is what actually initiates the request so it is the very earliest possible modification of the request. The added query args (added to the URL) are assigned to query vars (belonging to the query object), which are then used to construct a query, so the returned results reflect these added args. Note that query args and query vars, while related, each belong to very different parts of the process.

    ‘pre_get_posts’ occurs after the query vars are assigned, but before the SQL query is built and the DB queried. Thus it occurs much later than add_query_arg() and can countermand what was added with add_query_arg(). Since add_query_arg() occurs earlier than ‘pre_get_posts’, it cannot possibly change what ‘pre_get_posts’ does.

    By themselves, either method can easily alter the query just as well. Together, the methods can either contradict or supplement each other, depending on what they are trying to do. In case of conflict, ‘pre_get_posts’ always has final say.

    You can now see why add_query_arg() could never possibly change what’s done by ‘pre_get_posts’. You should also see that add_query_arg() could be used for a modified default view, but ‘pre_get_posts’ is probably better because it has final say. However, the callback must look for any changes from the unmodified query vars and respect them, as they would represent specific user requests. Without checking for changes, ‘pre_get_posts’ would be totalitarian and not allow any variations from what it does, as it has the final say. That kind of power comes with responsibility.

    You can also see neither of these runs a new query, they both influence the query before it is even assembled. Thus either of these are preferable to methods that discard the main query and rerun a new one, such as get_posts() or creating a new WP_Query object after the main query is run. These methods do have their place when additional data is need to supplement the main query, but should not be used in place of the main query.

    It’s impossible to say which approach is faster because action hooks are highly variable based on how complex the callback functions are. At best, ‘pre_get_posts’ can be extremely fast, or it could bog down with recursive walker functions. add_query_arg() does have some overhead that runs each time it is called. It’s not much, but if a lot of args are added, it could add up. I prefer ‘pre_get_posts’ for changing default behavior and add_query_arg() for user preferences simply because the resulting URLs are aligned with user expectations. The speed difference is typically negligible.

    This entire concept is important to understanding how WP really works, so if you are still not clear on something, feel free to ask for clarification.

Viewing 3 replies - 1 through 3 (of 3 total)
  • The topic ‘pre_get_posts AND add_query_arg’ is closed to new replies.