• I’ve been trying to solve this problem for a couple of days. I am using custom post types and advanced custom fields and what I am trying to do is return posts where values from a custom field from one custom post type match values on anotehr custom field from another custom post type.

    My set up is like this:

    custom post type1: ‘stores’
    acf field1: ‘store_brands’ (text field) comma seperated values e.g. (Asda, Morrisons, Sainsburys)

    custom post type2: ‘brands’
    acf field1: ‘brand_name’ (text field) one values e.g. (Ford)
    acf field2: ‘brands_name_alt’ (text field) comma seperated values e.g. (FordCar, Fored)

    I have been using wp_wuery. I nearly got it working, but not quite. It worked when single values were in brands_name_alt. but not if there was multiple values e.g (FordCar, Fored).

    The code I had so far in the stores post template is:

    $list = get_field( "store_brands" );
    
    $array = explode(',', $list);
    
    $args = array(
        'post_type' => array('stores', 'brands'),
        'meta_query' => array(
            'relation' => 'OR',
            array(
                'key'     => 'brand_name',
                'value'   => $array,
                'compare' => 'IN'
            ),
            array(
                'key'     => 'brands_name_alt',
                'value'   => $array,
                'compare' => 'IN'
            ),
        ),
    );
    
    $custom_query = new WP_Query($args);
    while($custom_query->have_posts()) : $custom_query->the_post(); ?>

    This works if there is a single value in the brands_name_alt. But not otherwise.

    I guess what I need to be able to do is compare two arrays and return posts where there is a match. It would work if I could use the ‘LIKE’ comapre, but this is not possible with an array value.

    Is there another approach I could take to this? I know storing the values this way is messy. But its the easiest way as there could be 100’s of store_brands.

Viewing 15 replies - 1 through 15 (of 25 total)
  • Moderator keesiemeijer

    (@keesiemeijer)

    Just to make sure there are no spaces in the comma seperated list try changing this:

    $array = explode(',', $list);

    to this

    $array = array_map( 'trim', (array)  explode(',', $list) );

    Thread Starter pdg87

    (@pdg87)

    I just realised the values I have above for store_brands are wrong e.g. (Asda, Morrisons, Sainsburys). These should realte to the brands e.g (Ford, Mercedes) Can I not edit this post?

    Thanks Keesie,

    I printed the array for this part and it seemed okay. I think its the fact that the key includes comma seperated values.

    So if I try to compare the array values from store_brands [Ford] [Mercedes]

    with the key brand_names_alt value: [Ford, Fored]

    It is returning none, as it is comparing ‘IN’ and looking for an exact match of the full string, rather than seperate values..

    Moderator keesiemeijer

    (@keesiemeijer)

    I don’t think it’s possible with a (single) meta query. Let me know if this scenario is correct:

    On a ‘stores’ single post page you’re querying for brands posts that have a ‘brand_name’ custom field value that’s in the ‘store_brands’ custom fields values. I assume the ‘brands_name_alt’ values are never the same as the ‘store_brands’ or ‘brand_name’ values?

    Thread Starter pdg87

    (@pdg87)

    That’s almost correct,

    brands_name_alt values will never be the same as brand_name values.

    But brand_name_alt could be present in store_brands.

    The query works fine with brand_name, because this is always a single value. e.g. [Mercedes] so the ‘IN’ compare works well.

    However because brand name alt is a list e.g. [Mercedes-Benz, MercedesBenz] and it doesnt work with that.

    store_brands might include [Mercedes] or it might include [Mercedes-Benz] or it might contain [MercedesBenz]

    Moderator keesiemeijer

    (@keesiemeijer)

    I think you should first get all meta values for the ‘brands_name_alt’ key where posts have the ‘brand_name’ key in the ‘store_brands’ values. Then use the values in the WP_Query.

    Try it with this:

    global $wpdb;
    
    $list = get_post_meta( $post->ID, "store_brands", true );
    $array = array_filter( array_map( 'trim', (array)  explode( ',', $list ) ) );
    
    if ( !empty( $array ) ) {
    
    	$post_types = array( 'brands', 'stores' );
    
    	$post_type_sql  = "'" . implode( "', '", array_map( 'esc_sql', $post_types ) ) . "'";
    	$meta_value_sql = "'" . implode( "', '", array_map( 'esc_sql', $array ) ) . "'";
    
    	// Firs query to get the brands_name_alt meta values
    	$query = "SELECT $wpdb->postmeta.meta_value FROM $wpdb->posts
              INNER JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id )
              INNER JOIN $wpdb->postmeta AS mt1 ON ( $wpdb->posts.ID = mt1.post_id )
              WHERE $wpdb->posts.post_type IN ( $post_type_sql )
              AND $wpdb->posts.post_status = 'publish'
              AND (
              	   $wpdb->postmeta.meta_key = 'brands_name_alt'
    	           AND mt1.meta_key = 'brand_name'
    	           AND CAST( mt1.meta_value AS CHAR) IN ( $meta_value_sql )
                  )
              GROUP BY wp_posts.ID
              ORDER BY wp_posts.post_date DESC";
    
    	$meta_values = $wpdb->get_col(  $query );
    
    	$args = array(
    		'post_type' => $post_types,
    		'meta_query' => array(
    			'relation' => 'OR',
    			array(
    				'key'     => 'brand_name',
    				'value'   => $array,
    				'compare' => 'IN'
    			),
    		),
    	);
    
    	// Check if meta values were found for the brands_name_alt key
    	if ( !empty( $meta_values ) ) {
    
    		// merge all meta values
    		$all_values = array();
    		foreach ( $meta_values as $value ) {
    			$value = array_filter( array_map( 'trim', (array)  explode( ',', $value ) ) );
    			$all_values = array_merge( $all_values, $value );
    		}
    
    		// get all brands_name_alt posts
    		$args['meta_query'][] =  array(
    			'key'     => 'brands_name_alt',
    			'value'   => $all_values,
    			'compare' => 'IN'
    		);
    
    		// get all store_brands posts
    		$args['meta_query'][] =  array(
    			'key'     => 'store_brands',
    			'value'   => $all_values,
    			'compare' => 'IN'
    		);
    	}
    
    	$custom_query = new WP_Query( $args );
    
    	// The rest of your loop
    }

    btw:
    consider creating a child theme instead of editing your theme directly – if you upgrade the theme all your modifications will be lost.

    Thread Starter pdg87

    (@pdg87)

    Thanks a lot for the help. I will give that a try as soon as I get home. My php/sql is weak I’m more design/front end.

    I want to place brand posts on store posts where there is a match in store_brands and brand_name OR store_brands brands_name_alt.

    I still think it will be a problem because its checking array values ($all_values) against a key which contains a string (brands_name_alt) using operator IN so it doesnt find an exact match. This field would have to have a single value? E.g. [Mercedes Benz] rather than: [Mercedes Benz, Mercedes-Benz]

    I could very easily be wrong so will give it a try this evening 🙂

    Moderator keesiemeijer

    (@keesiemeijer)

    When merging the $all_values array it creates a [value] array like this:

    // part of the $args array
    [2] => Array
                    (
                        [key] => store_brands
                        [value] => Array
                            (
                                [0] => FordCar
                                [1] => Fored
                            )
    
                        [compare] => IN
                    )

    As you see this can be used by the meta_query.

    Thread Starter pdg87

    (@pdg87)

    Okay thanks looking forward to giving it a try. I’m away from the code now which is annoying.

    But in my first example, changing the following params seemed to not work/work.

    presuming the value of brand_name_alt is [FordCar, Fored]

    [key] => brand_name_alt
    [value] => Array
    (
    [0] => FordCar
    [1] => Fored
    )
    [compare] => IN

    returned nothing

    [key] => brand_name_alt
    [value] => "FordCar"
    [compare] => LIKE

    Worked, but I need an array as the value so no use.

    Moderator keesiemeijer

    (@keesiemeijer)

    That’s probably because you want it to return posts from the ‘store_brands’ posts which has no brand_name_alt key values.

    But this should work.

    $list = get_post_meta( $post->ID, "store_brands", true );
    $array = array_filter( array_map( 'trim', (array)  explode( ',', $list ) ) );
    
    $args = array(
        'post_type' => array('stores', 'brands'),
        'meta_query' => array(
            'relation' => 'OR',
            array(
                'key'     => 'brand_name',
                'value'   => $array,
                'compare' => 'IN'
            ),
            array(
                'key'     => 'store_brands',
                // just hardcoding the values
                'value'   => array('FordCar', 'Fored'),
                'compare' => 'IN'
            ),
        ),
    );
    
    $custom_query = new WP_Query($args);

    It should return ‘brands’ post type posts with the brand name ‘Ford’ and posts with the store_brands key values FordCar or Fored.

    Thread Starter pdg87

    (@pdg87)

    great, I will report back. Thanks for taking your time to help Keesie, Much apprecited!

    Moderator keesiemeijer

    (@keesiemeijer)

    Aha, It will indeed not work. as the value for ‘store_brands’ is a comma seperated string.

    The only way this will work is if you add multiple ‘store_brand’ custom field values instead of the comma seperated ‘store_brand’ value.

    Thread Starter pdg87

    (@pdg87)

    Ah, thought so Keesie 🙁 There could be 100+ brands to enter into store_brands for every store so it would take ages to have seperate fields for each brand..

    Any other options? Is it achievable using wpdb sql query?

    Or someway to break csv entered values into seperate fields?

    Moderator keesiemeijer

    (@keesiemeijer)

    This in your theme’s functions.php will add the individual custom fields everytime you update the comma seperated custom field ‘store_brands’.
    You will not see these individual custom fields (in the edit post screen) because of the starting underscore in the custom field key name ‘_store_brands’.

    add_action ( 'update_postmeta', 'update_hidden_store_brands_meta', 10, 4  );
    
    function update_hidden_store_brands_meta( $meta_id, $object_id, $meta_key, $meta_value ) {
    
    	$post = get_post( $object_id );
    
    	if ( !$post ) {
    		return;
    	}
    
    	if ( ( 'store_brands' === $meta_key ) && ( 'stores' === $post->post_type ) ) {
    
    		delete_post_meta( $object_id, '_store_brands' );
    		$meta_value = array_filter( array_map( 'trim', (array)  explode( ',', $meta_value ) ) );
    
    		if ( !empty( $meta_value ) ) {
    			foreach ( $meta_value as $value ) {
    				add_post_meta( $object_id, '_store_brands', $value, false );
    			}
    
    		}
    	}
    }

    You still have to update a post or the custom field ‘store_brands’ for the individual _store_brands fields to be added to a post.

    Now use this code for the query:

    global $wpdb;
    
    $post_id    = absint( $post->ID );
    $post_types = array( 'brands', 'stores' );
    $list       = (array) get_post_meta( $post_id, "_store_brands", true );
    $array      = array_filter( array_map( 'trim', $list ) );
    
    if ( !empty( $array ) &&  $post_id ) {
    
    	$post_type_sql  = "'" . implode( "', '", array_map( 'esc_sql', $post_types ) ) . "'";
    	$meta_value_sql = "'" . implode( "', '", array_map( 'esc_sql', $array ) ) . "'";
    
    	// First query to get the brands_name_alt meta values
    	$query = "SELECT $wpdb->postmeta.meta_value FROM $wpdb->posts
              INNER JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id )
              INNER JOIN $wpdb->postmeta AS mt1 ON ( $wpdb->posts.ID = mt1.post_id )
              WHERE $wpdb->posts.post_type IN ( $post_type_sql )
              AND $wpdb->posts.post_status = 'publish'
              AND $wpdb->posts.ID NOT IN ( $post_id )
              AND (
              	   $wpdb->postmeta.meta_key = 'brands_name_alt'
    	           AND mt1.meta_key = 'brand_name'
    	           AND CAST( mt1.meta_value AS CHAR) IN ( $meta_value_sql )
                  )
              GROUP BY wp_posts.ID
              ORDER BY wp_posts.post_date DESC";
    
    	$meta_values = $wpdb->get_col(  $query );
    
    	$args = array(
    		'post_type'    => $post_types,
    		'meta_query'   => array(
    
    			'relation' => 'OR',
    
    			array(
    
    				// Nested queries are allowed since WordPress 4.1
    				'relation' => 'OR',
    
    				array(
    					'key'     => 'brand_name',
    					'value'   => $array,
    					'compare' => 'IN'
    				),
    
    				array(
    					'key'     => '_store_brands',
    					'value'   => $array,
    					'compare' => 'IN'
    				),
    			)
    		),
    	);
    
    	// Check if meta values were found for the brands_name_alt key
    	if ( !empty( $meta_values ) ) {
    
    		// merge all meta values
    		$all_values = array();
    		foreach ( $meta_values as $value ) {
    			$value = array_filter( array_map( 'trim', (array)  explode( ',', $value ) ) );
    			$all_values = array_merge( $all_values, $value );
    		}
    
    		// Query for all posts with the store_brands values in the brands_name_alt values
    		$args['meta_query'][] =  array(
    			'key'     => '_store_brands',
    			'value'   => $all_values,
    			'compare' => 'IN'
    		);
    	}
    
    	$custom_query = new WP_Query( $args );
    
    	// The rest of your loop
    }

    Thread Starter pdg87

    (@pdg87)

    Wow thanks for that, I was about to give up on it. However I think this will return stores? rather than brands?

    the list array (value?) should be made from the csv store_brands from the stores post type.

    Then this is checked against brand_name (key?) and brands_name_alt(key?) custom field in the brand post type.

    To return a list of brand posts on a store post.

    Hopefully just a case of changing some of the field names around?

    Moderator keesiemeijer

    (@keesiemeijer)

    However I think this will return stores? rather than brands?

    Did you test it after updating some posts with the (comma seperated) ‘store_brands’ custom field?

    In my test site it returns brands and stores posts.

    1 brand_name CF values posts that are in the _store_brands CF values of the current post
    2 _store_brands CF values posts that are in the _store_brands CF values of the current post
    3 _store_brands CF values posts that have _store_brands CF values in the brands_name_alt CF values (from the posts in #1)

    It’s getting increasingly more difficult to describe 🙂

Viewing 15 replies - 1 through 15 (of 25 total)
  • The topic ‘Match values from two arrays to return posts. wp_query?’ is closed to new replies.