• DatagoniaWeb

    (@datagoniaweb)


    Good Morning,

    I am currently building a website that will be a blog about music and am implementing custom fields and custom post types in several different manners. On my archive-event.php page I specifically want to modify the main loop for that page so that instead of ordering by the date the post (each event is a post of type “event”) was published I want them sorted in the archive by the date of a custom field called “start_date”.

    Currently each start_date field is in the form “yyyy-m->set-dd” and I want to omit posts with a start_date before the current date (so previous events would not show up). Additionally I would like to order the archive of the posts by start_date so that the first post would be the soonest upcoming event.

    I was reading a little bit about the pre_get_posts function but was not able to implement it correctly. The two main areas of code for attempting to modify the query are in functions.php and archive-event.php:

    Functions.php:

    //specifically alter the main query *after* it has been called
    add_action('pre_get_posts','alter_events_query');
    function alter_events_query($query){
          if( $query->is_main_query() ){
            $query->set( array('meta_key' => 'start_date', 'meta_value' => date('o-m-d'), 'meta_compare' => '>=' ));
          }
    }

    Event-archive.php:
    alter_events_query( );

    It should be noted that the “alter_events_query();” line is called immediately after the “get_header” call at the beginning of archive-event.php.

    My website is: RaveIsKing.Danconia.US and the page, including error message, is the following (debugging is currently on):
    http://raveisking.danconia.us/event/

    Any help that could point me in the right direction would be appreciated. I don’t know if I am sending the right parameters to the wp_query->set method and it seems that documentation on that method and the “pre_get_posts” isn’t very well documented either. Thank you!

Viewing 10 replies - 1 through 10 (of 10 total)
  • Thread Starter DatagoniaWeb

    (@datagoniaweb)

    Bump. Any help would be great! I know I’m not the only one wondering how to modify the main loop this way.

    Moderator bcworkz

    (@bcworkz)

    Not only is bumping not allowed in these forums, it really does work against you. While it does have the effect of moving your topic near the top of the conventional list, most members in a position to help you use the “No Replies” filter. When you bump, your topic falls off of this list. And others not using the filter assume that someone is helping you because there is more than one post, so they skip over your topic.

    Sorting queries by date strings is problematic and should be avoided. The posts are sorted alphabetically, where June 28 will appear before June 3 and both appear before May 1. Storing timestamps and ordering numerically works MUCH better. If you are stuck with date strings, there might still be hope. You use the ‘posts_orderby’ filter to alter the ORDER BY clause created by WordPress. You want it to use the mySQL CONVERT() function to change the date string to a datetime value.

    A better approach if you’re stuck with date strings is to add another meta key that contains the actual timestamp equivalent of the date string. If you orderby that key, everything should work fine.

    The errors you are getting are because you are calling alter_events_query() from the template page. This is unnecessary because you added the function as a callback to the ‘pre_get_posts’ actions. The action hook calls your function at the correct time to modify the query. Calling it again from the template is incorrect, the query has been run and cannot be altered at this point.

    Thread Starter DatagoniaWeb

    (@datagoniaweb)

    Sorry about bumping. Navigating these forums can be tricky at times.

    As luck would have it the custom field (start_date) that I am using to order the posts are in yyyy-mm-dd format so if they were to be ordered alphabetically they would be put in the order that I am, indeed, seeking.

    I just need to know how to make the process use that specific parameter to put them in order. I have taken out the call within the archive-event.php file and here is the code that I have in functions.php currently:

    add_action('pre_get_posts','alter_events_query');
    function alter_events_query( $query ){
        if( $query->is_main_query() && is_post_type_archive( "event" )  ) {
            $query->set('orderby', "start_date[0]");
        }
    }

    It does not seem to be ordering by start_date (which I have reformatted and echoed on the “Starts: __________” line. I am trying to look closer into what order it is putting them in by default (I have a feeling it will be original post date in ascending order but I could be wrong). Any help would be appreciated!

    It just seems like a lot of the tutorials and codex don’t quite go into enough detail when it comes to this stuff.

    Thread Starter DatagoniaWeb

    (@datagoniaweb)

    So it seems that I have almost gotten it to work. Here is my current code:

    //specifically alter the main query *before* it takes place
    add_action('pre_get_posts','alter_events_query');
    function alter_events_query( $query ){
        if( $query->is_main_query() && $query->is_post_type_archive( "event" )  ) {
            //$query->query_vars['meta_key'] = 'start_date';
            //$query->query_vars['meta_value'] = date('o-m-d');
            //$query->query_vars['meta_compare'] = '>=';
            $query->set('orderby', "start_date[0]");
        }
    }

    Unfortunately because the ‘start_date’ is in the postmeta table I think this is causing a problem. I have tried using “ID” instead of “start_date[0]” and that worked fine (ordering posts by post ID in descending order).

    How would I go about putting them in order by their start_date field if that field is in the postmeta table rather than the wp_posts table? Would I have to do a custom query with a MySQL join?

    Thread Starter DatagoniaWeb

    (@datagoniaweb)

    Okay I am making some good progress now. I have figured out how to do a meta query thanks to the following link:
    http://www.billerickson.net/customize-the-wordpress-query/

    However I am still running into the issue of date comparisons without using a Unix timestamp. It seems WordPress (or MySQL, more specifically) has problems comparing dates when in yyyy-mm-dd form. If I’m only using dates then I could probably use yyyymmdd form and get away with things or I could use Unix timestamp.

    The one problem I am currently having with Unix timestamp is how to use a MySQL function as part of the array arguments for the meta query. The following code, for example, pulls up no results:

    //specifically alter the main query *before* it takes place
    add_action('pre_get_posts','alter_events_query');
    function alter_events_query( $query ){
        if( $query->is_main_query() && $query->is_post_type_archive( "event" )  ) {
            $event_meta_query = array(
                array(
                        'key' => 'UNIX_TIMESTAMP("start_date")', //MySQL function doesn't seem to be working
                        'value' => time(), //will give unix timestamp of current time
                        'compare' => '>='
                )
            );
            $query->set( 'meta_query', $event_meta_query );
            $query->set( 'orderby', 'UNIX_TIMESTAMP(start_date)' ); //MySQL function doesn't seem to be working
            $query->set( 'order', 'ASC' );
            $query->set( 'posts_per_page', '20' );        
    
        }
    }

    Any help with the Unix timestamp issue would be awesome. Thank you.

    Moderator bcworkz

    (@bcworkz)

    I’m not sure why alphabetic sorting wouldn’t work with dates in that format, though you would need to get the rest of the code correct for it to work πŸ˜‰ Your first and second posts after my reply have this: $query->set('orderby', "start_date[0]");. ‘start_date’ appears to be an array reference due to the ‘[0]’, but it cannot be a PHP variable without the ‘$’. If you are trying to reference a value in an array, even with the $ it will not work because it is in quotes. Unless this is a column name? A rather unusual name, but maybe possible.

    None of that really matters except for info on what not to do. The thing is you can only set 'orderby' to a very limited set of values listed in the Codex [[Class_Reference/WP_Query#Order_.26_Orderby_Parameters|WP_Query Reference]]. Your only possible choice here is 'meta_value'. Note that you must also set 'meta_key' for this to work. Setting 'orderby' to ‘start_date[0]’, ‘any_random_string’, or even ‘UNIX_TIMESTAMP(start_date)’ can never work, it has to be one of the arguments listed.

    You don’t seem to quite understand ‘key’ arguments because in your last post you are trying set it to the result of UNIX_TIMESTAMP(). There should never be any need for this as ‘key’ values are always predetermined string values. ‘key’ and ‘meta_key’ are both set to the tag name under which meta data is stored and retrieved. For example, when you retrieve post meta, you might use this code:
    $my_data = get_post_meta( $post->ID, 'my_tag_name');
    ‘my_tag_name’ is the same tag name you would assign as a value when setting various ‘key’ arguments:
    $query->set('meta_key', 'my_tag_name');

    Assuming the yyyy-mm-dd date string values are stored in postmeta under the key ‘start_time’, the following should result in alphabetic ordering by that date string:

    $query->set('meta_key', 'start_time');
    $query->set('orderby', 'meta_value' );

    If the ordering doesn’t quite work out, you might also try ‘meta_value_num’ instead of ‘meta_value’ for the 'orderby' argument.

    You can use 'meta_query' if need be, but it is not required to establish an order. In fact, there’s evidence that 'meta_query' could actually make ordering by a meta value more difficult.

    If for any reason you do find the need to insert a mySQL function like UNIX_TIMESTAMP(), you now know you cannot pass it as a WP_Query argument. The way to insert mySQL functions is to hook into one of the various ‘posts_*’ filters and add it to the appropriate clause. Explaining this aspect is an entire How-To article in itself. You can perhaps gain better understanding of how to use these filters by examining the source code where the filters are applied. You would be most interested in ‘posts_orderby’ at line 3004. The results of all these filters are assembled at line 3189.

    You should now have a better idea of what and what not to do. You may get your code working straight off. If you do not, I’ve found the ‘posts_request’ filter (Line 3192) very useful in debugging my WP_Query arguments. By hooking this filter, your callback is passed the final mySQL query string just before it is sent to mySQL. By using print_r() to output the query string, you can often examine it and discover exactly why the query is not working like you think it should.

    I hope all of this gets you straightened out. Good luck!

    Thread Starter DatagoniaWeb

    (@datagoniaweb)

    bcworkz,

    Dude, I can’t thank you enough. I consider myself relatively advanced when it comes to PHP but not as much with WordPress’s API. I guess the point of having a meta key and meta value is to tell WordPress first that you want order by a meta value / key pair and then the meta_key part tells it specifically which meta values.

    You are right that alphabetical order should work for both yyyymmdd and yyyy-mm-dd formats (as well as timestamps) but I thought it might be possible that the dashes mess the ordering up or something.

    Anyway just wanted to say thank you for your help and patience!

    Thread Starter DatagoniaWeb

    (@datagoniaweb)

    Hmmm it looks like I am having issues with the comparison clause.

    Here is what I currently have:

    //specifically alter the main query *before* it takes place
    add_action('pre_get_posts','alter_events_query');
    function alter_events_query( $query ){
        if( $query->is_main_query() && $query->is_post_type_archive( "event" )  ) {
            $event_meta_query = array(
                array(
                    'meta_key' => 'start_date',
                    'meta_value' => time(),
                    'meta_compare' => '>='
                ),
            );
            $query->set( 'meta_query', $event_meta_query );
            $query->set( 'order', 'ASC' );
            $query->set( 'posts_per_page', '20' );
            $query->set( 'meta_key', 'start_date' );
            $query->set( 'orderby', 'meta_value' );
        }
    }

    Currently it does not look like the dates are being limited to those in the future (eg “upcoming” events). Any idea what’s wrong with the query? Keep in mind that the ‘start_date’ is a postmeta column / field.

    Moderator bcworkz

    (@bcworkz)

    You’re not the first one to run into this issue. AFAICT the query parser gets confused when there is both a meta_query and a meta_key argument. I suggest you dump for inspection the final query string using the ‘posts_request’ filter as mentioned at the end of my previous post.

    By examining the query string, hopefully you can see where the problem lies and concoct a plan to correct the problem. A possible plan might be to drop the orderby and meta_key arguments and add in a custom orderby clause using the ‘posts_orderby’ filter instead. You should be able to use the meta_key reference from the meta_query WHERE clause to specify the orderby column.

    If all else fails, just get the results unordered and sort the returned objects with PHP using uasort() or something. Good luck!

    Thread Starter DatagoniaWeb

    (@datagoniaweb)

    Bcworkz, just wanted to say thank you again. You’re right it was putting the events in order anyway so I used the meta query and compared my start dates. I also had the order of the meta_value and actual meta value backwards.

    Crazy how you take a couple of weeks off and come back and things make sense. Thanks a ton for your help!

Viewing 10 replies - 1 through 10 (of 10 total)
  • The topic ‘Modifying the Main Loop Query (wp_query->set)’ is closed to new replies.