Query custom post type by custom field

General Info

I used the plugin WP Types (wp-types.com) to create a custom post type called “artist“.
In WP Types you can also set another post type as parent post type. This is not possible by default; they implemented this feature by a meta query with the key:
“_wpcf_belongs_%POSTTYPE%_id” (in my case “_wpcf_belongs_artist_id“)
and the value is the ID of the parent post.

The Problem

Normally if you set a page X as a child of page Y you would have the following permalink structure:
/page-y/page-x/

Read More

I thought this plugin would adopt this but it does not. For example I use “painting” with the ID 24 as the parent page and “picasso” with the ID 72 as the child post with the post type “artist”.
So this:
/painting/picasso/
does redirect me to:
/artist/picasso/
but outputs the right content.

My solution approaches

As the paid support could not help me I have to try on my own.
For writing my own “custom” rewrite rules I have to know the correct query at first. I tried to get my content manually:

index.php?_wpcf_belongs_artist_id=24&artist=picasso
redirects me to:
/artist/picasso?_wpcf_belongs_artist_id=24&artist=picasso
with the correct output of the single artist post.

This:
index.php?_wpcf_belongs_artist_id=24
just outputs my front page.

The only more or less working variant is:
/painting/picasso/?artist=picasso&_wpcf_belongs_artist_id=24 and:
/painting/picasso/?artist=picasso
These don’t redirect me to /artist/picasso/… or anywhere but output the correct single artist post.

Here you can find some more variants and the whole thread in the support forums of WP Types.

First step probably would be to get the right content with a queryvar like “parent_id”. After that I can start dealing with the rewrite rules itself, there might be problems because of the double usage of the top level (%pagename% and %parent_id%). This probably will confuse WordPress.

UPDATE 12/17

I tried this code to get all posts with the meta field ‘_wpcf_belongs_artist_id’ and the meta value retrieved from the URL (/?parent_id=24). Unfortunately, it doesn’t seem to work. It crashes my submenu’s auxiliary queries and outputs just the result of the normal query.

add_filter('query_vars', 'custom_query_vars');
function custom_query_vars($vars) {
    $vars[] = 'parent_id';
    return $vars;
}

function parent_query( $query ) {
    // Exclude
    if( $query->is_admin == 1 && !$query->is_main_query() && !$query->is_archive == 1 ) {
        return;
    }
    // retrieve field name / value from URL if exist
    $custom_field = '_wpcf_belongs_artist_id';
    $custom_value = ( $_GET['parent_id'] ) ? stripslashes( $_GET['parent_id'] ) : '';
    if( $custom_field ) {
        // add meta key requirement -- we’ll return all posts with some ‘color’
        $query->set( 'meta_key', '_wpcf_belongs_artist_id' );
        if( $custom_value ) {
            // build meta value requirement -- we’ll return only blue
            $query->set( 'meta_value', $custom_value );
        }
    }
}
add_action( 'pre_get_posts', 'parent_query' );

Related posts

3 comments

  1. You got it pretty close. Here’s a working example of what you’re trying to do:

    class Wpse_126374 {
    
        public function __construct() {
            add_action( 'init', array( $this, 'rewrites' ) );
            add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
        }
    
        public function rewrites() {
            add_rewrite_tag( '%parent_id%', '(d+)' );
        }
    
        public function pre_get_posts( $query ) {
            // Don't run this on admin pages or secondary queries
            if ( is_admin() || ! $query->is_main_query() || $query->is_archive() )
                return;
    
            if ( '' != ( $parent_id = get_query_var( 'parent_id' ) ) ) {
                $query->set( 'meta_key', '_wpcf_belongs_artist_id' );
                $query->set( 'meta_value', $parent_id );
            }
        }
    
    }
    $wpse_126374 = new Wpse_126374;
    

    This should allow you to have parent_id=24 in the URL.

  2. I think the problem is ill-posed. What happens as soon as you add two paintings by Picasso? You want both /painting1/picasso and /painting2/picasso to point to the same ‘single artist post’? I suppose this is possible but I’m pretty sure it’s unnecessarily complicated (and may cause SEO issues).

    On the other hand, because artists and paintings are different kind of entities, I think it’s perfectly fine that Picasso is displayed under /artist/picasso, and that this post is linked to from any paintings he authored.

    Using the parent relationship is wrong. A painting has (usually) only one author, but an artist can produce many paintings. But a post can only have one parent, so an artist post should not be made the child of one of its paintings, or else you’re in trouble as soon as you add another painting by the same author.

    Instead, you should have the artist be a property of paintings. You can do this by adding a custom field for paintings, and maybe using Types’ posts relationships or some other device to enforce that the value of this custom field refers to an artist post type.

    With this approach, your problems are not ‘solved’, they just go away.

  3. You don’t need to return anything from your function when using pre_get_posts:

    function 
    if( $query->is_admin() || !$query->is_main_query() || $query->is_archive() ) {
    //Do your stuff
    

Comments are closed.