Support

Account

Home Forums General Issues How to use WP Term Meta, the easy way!

Solved

How to use WP Term Meta, the easy way!

  • If you want to use the new Term Meta on WordPress, try this trick!

    Suppose you have the taxonomy store and have some fields created on ACF like storemeta_state, storemeta_city. The name could be any other, but starting with taxonomymeta makes it hard to break with other field names that ACF is manipulating.

    You just have to hook the update_value and load_value to save and read the values on Term Meta table. ACF will still be saving data on Options table too, in both sides now.

    function acf_update_term_meta($value, $post_id, $field) {
    	$term_id = intval(filter_var($post_id, FILTER_SANITIZE_NUMBER_INT));
    	if($term_id > 0)
    		update_term_meta($term_id, $field['name'], $value);
    	return $value;
    }
    add_filter('acf/update_value/name=storemeta_state', 'acf_update_term_meta', 10, 3);
    add_filter('acf/update_value/name=storemeta_city', 'acf_update_term_meta', 10, 3);
    
    function acf_load_term_meta($value, $post_id, $field) {
    	$term_id = intval(filter_var($post_id, FILTER_SANITIZE_NUMBER_INT));
    	if($term_id > 0)
    		$value = get_term_meta($term_id, $field['name'], true);
    	return $value;
    }
    add_filter('acf/load_value/name=storemeta_state', 'acf_load_term_meta', 10, 3);
    add_filter('acf/load_value/name=storemeta_city', 'acf_load_term_meta', 10, 3);

    Then you can use the Term Meta Query that WordPress provide.

    $terms = get_terms('store', array(
        'meta_key' => 'storemeta_state',
        'meta_value' => 'SP'
    ));
  • Hey @edir, thanks for this.. really great. I am using this to update all of my meta fields instead of providing individual filters for each field, the only catch is that each field name has to be prefixed with “meta”. Here’s the code I am using:

    
    /**
     * Update term meta based on ACF term meta fields
     */
    function namespace_acf_update_term_meta( $value, $post_id, $field ) {
      $term_id = intval( filter_var( $post_id, FILTER_SANITIZE_NUMBER_INT ) );
    
      if( substr( $field['name'], 0, 5 ) === 'meta_' ) {
        if( $term_id > 0 ) {
          update_term_meta( $term_id, $field['name'], $value );
        }
      }
      
      return $value;
    }
    add_filter( 'acf/update_value', 'namespace_acf_update_term_meta', 10, 3 );
    add_filter( 'acf/update_value', 'namespace_acf_update_term_meta', 10, 3 );
    
    /**
     * Load term meta in for ACF meta fields
     */
    function namespace_acf_load_term_meta( $value, $post_id, $field ) {
      $term_id = intval( filter_var( $post_id, FILTER_SANITIZE_NUMBER_INT ) );
    
      if( substr( $field['name'], 0, 5 ) === 'meta_' ) {
    
        if( $term_id > 0 ) {
          $value = get_term_meta( $term_id, $field['name'], $value );
        }
      }
    
      return $value;
    }
    add_filter( 'acf/load_value', 'namespace_acf_load_term_meta', 10, 3 );
    add_filter( 'acf/load_value', 'namespace_acf_load_term_meta', 10, 3 );
    
  • @truheart

    Good idea to automate! I just would use “termmeta_” as the prefix, makes it more reliable to avoid mistakes and put the filter after check field name.

    function ...( $value, $post_id, $field ) {
    	if( strpos( $field['name'], 'termmeta_' ) === 0 ) {
    		$term_id = intval( filter_var( $post_id, FILTER_SANITIZE_NUMBER_INT ) );
    		if( $term_id > 0 )
    			[...]
    	}
    	return $value;
    }
  • @truheart

    @edir

    Here is an even better way. It clears out the options table. so you aren’t double saving values. And if ACF ever decides to start using the termmeta table you will be all set and can just remove these functions.

    You would want to modify the “if ( $field_group[‘location’][0][0][‘param’] == ‘taxonomy’ )” line if you were using complex location rules.

    
    add_action( 'acf/init', 'prefix_acf_init' );
    /**
     * Prepare for any crazy stuff we are about to do
     */
    function prefix_acf_init() {
    	global $prefix_acf_delete_values;
    
    	$prefix_acf_delete_values = [ ];
    }
    
    add_action( 'acf/save_post', 'prefix_after_save_post', 100001 );
    /**
     * Cleanup after everything is done
     */
    function prefix_after_save_post() {
    	global $prefix_acf_delete_values;
    
    	if ( ! empty( $prefix_acf_delete_values ) ) {
    		foreach ( $prefix_acf_delete_values as $delete ) {
    			acf_delete_value( $delete['post_id'], $delete['field'] );
    		}
    	}
    }
    
    add_filter( 'acf/update_value', 'prefix_update_term_meta', 10, 3 );
    /**
     * Update term meta based on ACF term meta fields
     *
     * @param $value
     * @param $post_id
     * @param $field
     *
     * @return
     */
    function prefix_update_term_meta( $value, $post_id, $field ) {
    	global $prefix_acf_delete_values;
    
    	$field_group = acf_get_field_group( $field['parent'] );
    
    	if ( $field_group['location'][0][0]['param'] == 'taxonomy' ) {
    		$term_id = intval( filter_var( $post_id, FILTER_SANITIZE_NUMBER_INT ) );
    
    		if ( $term_id > 0 ) {
    			$update_term_meta = update_term_meta( $term_id, $field['name'], $value );
    
    			if ( ! is_wp_error( $update_term_meta ) )
    				$prefix_acf_delete_values[ $field['key'] ] = [ 'post_id' => $post_id, 'field' => $field ];
    		}
    	}
    
    	return $value;
    }
    
    add_filter( 'acf/load_value', 'prefix_load_term_meta', 10, 3 );
    /**
     * Load term meta in for ACF meta fields
     *
     * @param $value
     * @param $post_id
     * @param $field
     *
     * @return mixed
     */
    function prefix_load_term_meta( $value, $post_id, $field ) {
    	$field_group = acf_get_field_group( $field['parent'] );
    
    	if (
    		$field_group['location'][0][0]['param'] == 'taxonomy' ||
    		(
    			! in_array( $post_id, [ 'option', 'options' ] ) &&
    			! $field_group &&
    			strpos( $post_id, '_' ) !== false
    		)
    	) {
    		$term_id = intval( filter_var( $post_id, FILTER_SANITIZE_NUMBER_INT ) );
    
    		if ( $term_id > 0 ) {
    			$value = get_term_meta( $term_id, $field['name'], true );
    		}
    	}
    
    	return $value;
    }
    
  • I’m of a different opinion. The way I look at this, the only purpose for storing anything in term_meta until ACF does it is to have those values in a place to make it easy to search for terms using get_terms(). I don’t see any point in slowing down the admin to put everything into the term meta table or going to the extra effort of getting it from there, or making the effort of deleting anything from the options table. All of these things will just slow down the site simply to keep the database clean.

    Since trying to search for things where serialized data is being stored or when the field is a repeater or flex field can be ridiculous I don’t see any point in putting this type of data into the term meta table. Anything that I’m going to want to use it get_terms() is going to be a simple field type. More complicated fields I’d create a custom function just for that field.

    Anyway, here’s what I’m using.

    
    <?php 
      
      add_fitler('acf/update_value', 'temp_acf_update_term_meta', 10, 3);
      
      function temp_acf_update_term_meta($value, $post_id, $field) {
        if (is_array($value) || is_object($value) || acf_is_sub_field($field)) {
          return $value;
        }
        $object = get_queried_object();
        $term_id = intval(filter_var($post_id, FILTER_SANITIZE_NUMBER_INT));
        if (!isset($object->taxonomy) || !isset($object->term_id) || $term_id != $object->term_id) {
          // the current object is not a term
          return $value;
        }
        // good value to store
        update_term_meta($term_id, $field['name'], $value);
        return $value;
      } // end function temp_acf_update_term_meta
      
    ?>
    

    I’m also not in an all fired hurry for ACF to eliminate using the options table for term meta anyway because I have not used ACF function in theme templates in a long time opting to use get_post_meta() and get_option() instead. This came about because I build plugins for ACF and using ACF functions in a plugin that accesses data using these functions can cause unintended side effects, like infinite loops and getting the wrong values. When ACF does implement term meta I’m going to need to figure out how to hook into get_option() and divert it to get_term_meta() when the field name starts with a taxonomy name. I’m sure it can be done, I’m just not in a hurry to get there. I figure I have some time though, knowing the way ACF is built around {$taxonomy}_{$term_id} for these option names I think is one the reasons it hasn’t been implemented in ACF yet.

  • Is there any plans for ACF start using the termmeta table instead of filling up the options table?

  • That’s the $64,000 question. I have not heard anything or seen anything one way or the other. I can only assume that the developer is working on a plan to incorporate term meta. I think that making the change is probably very complicated and I’m not going to rush him, I’d personally prefer that when it is incorporated that it works bug free and would rather have something that works and is backwards compatible and having it later than having it sooner and rushed and probably buggy.

  • Thanks so much for this code guys. I was just about to start trying to query the options table in a custom way and get the taxonomy name and term_id from the option name! ugh

  • Just to add, for @infoircary-com, your code is great, but I have come across a couple of deficiencies:

    – if taxonomy is not the first location for the field, it won’t work
    – if you’re expecting an image field to return an image object, you’ll only get an attachment ID

    Those can both be worked around on a small scale but just thought I’d point it out, maybe we can work on enhancing the code to be more bulletproof if it’s not going to make it into ACF Core anytime soon 🙂

  • Yes, @tdmalone, I have actually ran into a number of cases where I have had to update my code. It is actually a complex problem to solve.

    I would really like to know the answer to: Is ACF going to support termmeta or not?

    Because right now I am just going under the assumption they will soon, so I am only investing time in solving code issues as they arise in my use cases. Rather than trying to go bulletproof.

    For the sake of ACF, I hope they support it soon, It is essential to have termmeta in the wp_termmeta table if you want to properly running a meta_query based on meta of you taxonomies.

  • For the sake of ACF, I hope they support it soon, It is essential to have termmeta in the wp_termmeta table if you want to properly running a meta_query based on meta of you taxonomies.

    ^ THIS

  • +1 on term meta. This is vital to ACF (IMO) and the evolution/power of WP core.

  • Hi guys

    Great news. I’ve just added termmeta to ACF PRO 5.5.0 and will release in the next month!

  • Hmmm, I don’t seem able to update the term meta when updating acf values any longer. Anybody running into issues here?

  • Great News Elliot!!! Thanks so much! Would it allow to update old values so to clean the options table safely?

  • Will the data be migrated automagically?

  • @elliot how is this going? When can we expect the release? Thanks.

  • @elliot Now that the update has been released and we know that the fields are copied over the wp_termmeta table, can we delete them from the wp_options table ?

  • You can, but you should backup your DB, and also confirm that they are in the term_meta table. I was able to clear our my options tables without issue, after the ACF update ran.

  • Did you manually clean up? Also, what’s the best method to clean things up since ACF adds the field_ field along with every field? That seems like a very manual process to track them all down.

  • Yeah, I wish the database update built into ACF removed the options data. I had to do it manually, but it was easy, only because I prefix all my ACF fields, so I was able to write a query for that prefix.

    You could create a PHP script to loop through your term_meta ACF fields, that is about the only other way I can think to get them in a non-manual manner.

  • If you only deleted your prefixed options, then you missed all of the ‘field_12346782568’ data as well though.

  • I guess for now it’s a good thing that ACF does NOT remove the wp_options entries. I’m pretty sure this will be an option on a future version, when everything is all tested extensively.

  • There is also a backwards compatibility thing. There are times that some might have used get_option() instead of get_field(). If ACF wipes the options table then the update could break a site that does. For anyone that’s done this, or has a site that may have been built by someone else, I would most definitely back up the database before deleting data. You may need to go into the template code and find those places using get_option() and switch them to either get_field() or get_term_meta()

    Yes, I’ve done this, why? So that sites keep working even if I need to disable ACF for some reason. I have a couple of sites that need some work 😛

Viewing 25 posts - 1 through 25 (of 28 total)

The topic ‘How to use WP Term Meta, the easy way!’ is closed to new replies.