是否可以使用已注册的REST字段来过滤REST API端点?

时间:2021-06-25 作者:Daniel Jonguitud

我在自定义post类型端点中添加了一个rest字段,如下所示:

function artists_per_building() {
   register_rest_field( \'building\',
  \'artists\',
array(
    \'get_callback\'  => \'rest_get_users_per_building\',
    \'update_callback\'   => null,
    \'schema\'            => null,
  )
 );
 }
 add_action( \'rest_api_init\', \'artists_per_building\' );

function rest_get_users_per_building( $request ) {

$user_query = new WP_User_Query( 
array(
    \'role\'          => \'artists_role\',
    \'meta_query\'    => array(
        \'relation\'  => \'AND\',
        array( 
            \'key\'     => \'building\',
            \'value\'   => $request["id"],
          )
      )
   ) 
);
   return count($user_query->get_results());
}
现在我想按艺术家排序,这可能吗?

1 个回复
最合适的回答,由SO网友:bosco 整理而成

It is possible, yes, but for this specific use-case I think you\'ll need to do a little more legwork than usual.

Add artist_count meta to building posts

In principal we\'d be trying to tell the WP_Query responsible for retrieving buildings to "orderby number of artists," but since many artists are associated with a single building by way of an artist\'s user meta only, there\'s no direct way to perform that ordering through a WP_Query (short of modifying the actual SQL).

I think the easiest way to mitigate this situation would be to add the artist count as post meta to each building and update that count as necessary. We can patch that in to your existing functionality regardless of how you\'re currently setting roles and assigning buildings by hooking in to role and user meta changes:

/**
 * Update buildings\' artist_count meta and optionally a user\'s building meta.
 *
 * @param int      $user_id The ID of the user who\'s being assigned a new building.
 * @param int|null $new_building The ID of the user\'s new building, or `null` if the assignment is being removed.
 * @param int|null $old_building The ID of the user\'s previous building, or `null` if there wasn\'t one.
 * @param boolean  $update_user Whether or not to update the user\'s `building` meta.
 **/
function wpse391043_assign_artist_building( $user_id, $new_building = null, $old_building = null, $update_user = false ) {
  if( empty( $new_building ) && empty( $old_building ) )
    return;
  
  // Subtract one from the previous building\'s artist count, if applicable.
  if( ! empty( $old_building ) ) {
    $building_count = get_post_meta( $old_building, \'artist_count\', true );

    update_post_meta(
      $old_building,
      \'artist_count\',
      empty( $building_count ) ? 0 : $building_count - 1
    );
  }

  // Add one to the new building\'s artist count, and set the user\'s new building meta if applicable.
  if( ! empty( $new_building ) ) {
    $building_count = get_post_meta( $new_building, \'artist_count\', true );
  
    update_post_meta(
      $new_building,
      \'artist_count\',
      empty( $building_count ) ? 1 : $building_count + 1
    );

    if( $update_user )
      update_user_meta( $user_id, \'building\', $new_building );
  }
  else if( $update_user ) {
    // If there\'s no new building ID, remove the user meta.
    delete_user_meta( $user_id, \'building\' );
  }
}

/**
 * When a user has building meta set with no previous value, increment the building\'s
 * artist_count meta.
 **/
function wpse391043_add_user_building_meta( $user_id, $meta_key, $building_id ) {
  if( $meta_key !== \'building\' )
    return;

  wpse391043_assign_artist_building( $user_id, $building_id );
}
add_action( \'add_user_meta\', \'wpse391043_add_user_building_meta\', 10, 3 );

/**
 * When a user\'s building meta changes, update both building\'s artist_count meta.
 **/
function wpse391043_update_user_building_meta( $meta_id, $user_id, $meta_key, $building_id ) {
  if( $meta_key !== \'building\' )
    return;
  
  $old_building = get_user_meta( $meta_id, \'building\', true );
  
  wpse391043_assign_artist_building( $user_id, $building_id, $old_building );
}
add_action( \'update_user_meta\', \'wpse391043_update_user_building_meta\', 10, 4 );

/**
 * Remove a user\'s building meta and update the building\'s artist_count meta if a user
 * looses the artists_role role.
 */
function wpse391043_update_user_role( $user_id, $role, $old_roles ) {
  if( $role === \'artists_role\' || current( $old_roles ) !== \'artists_role\' )
    return;
  
  $old_building = get_user_meta( $user_id, \'building\', true );
  
  wpse391043_assign_artist_building( $user_id, null, $old_building, true );
}
add_action( \'set_user_role\', \'wpse391043_update_user_role\', 10, 3 );

With that in place, buildings\' artist_count meta should now stay up-to-date with whatever changes to users\' building meta are taking place; and rest_get_users_per_building() gets a nice performance boost in that now it only need look up one piece of post meta:

function rest_get_users_per_building( $building ) {
  $artist_count = get_post_meta( $building[\'id\'], \'artist_count\', true );

  return empty( $artist_count ) ? 0 : $artist_count;
}

This will also benefit the actual orderby operation on the whole, as it does not need to calculate artist counts for every building each time such a request is made.

Register the artists orderby REST parameter

Now we can use a rest_{post type}_collection_params filter in order to expose a new artists orderby param:

/**
 * Add `artists` as an orderby parameter for the building REST controller.
 **/
function wpse391043_rest_building_params( $params ) {
  $params[\'orderby\'][\'enum\'][] = \'artists\';
  return $params;
}
add_filter( \'rest_building_collection_params\', \'wpse391043_rest_building_params\' );

Modify the REST WP_Query arguments to account for orderby=artists

And a rest_{post type}_query filter in order to factor that parameter in to the building post type query:

/**
 * If `orderby` is `artists`, modify the query to order by `artist_count` meta.
 **/
function wpse391043_rest_building_query( $args, $request ) {
  $orderby = $request->get_param( \'orderby\' );

  if( empty( $orderby ) || $orderby !== \'artists\' )
    return $args;

  $args[\'orderby\']  = \'meta_value_num\';
  $args[\'meta_key\'] = \'artist_count\';

  return $args;
}
add_filter( \'rest_building_collection_query\', \'wpse391043_rest_building_query\', 10, 2 );

After which, you should be able to order buildings by their artist_count meta using the new parameter:

/wp-json/wp/v2/buildings?orderby=artists

Synchronize current artist counts to the new meta

You might need to get the new artist_count meta set up on each building to reflect the current assignments, which could be done with a little loop which can you run once:

function wpse391043_sync_building_artist_counts() {
  $building_counts     = []; // A mapping of building IDs to artist counts.
  $assigned_artist_ids = get_users(
    [
      \'role\'         => \'artists_role\',
      \'meta_key\'     => \'building\',
      \'meta_compare\' => \'EXISTS\',
      \'number\'       => -1,
      \'fields \'      => \'ID\',
    ]
  );

  foreach( $assigned_artist_ids as $user_id ) {
    $building_id = get_user_meta( $user_id, \'building\' );

    if( empty( $building_id ) )
      continue;
    
    if( ! isset( $building_counts[ $building_id ] ) )
      $building_counts[ $building_id ] = 0;

    $building_counts[ $building_id ]++;
  }
    
  foreach( $building_counts as $building_id => $artist_count )
    update_post_meta( $building_id, \'artist_count\', $artist_count );
}

相关推荐

我可以在纯固定链接格式上使用REST-API吗?

最近,我所有的REST-API请求都突然返回404错误,每个请求(无论是自定义端点还是内置的)。然后我想这是因为permalink的结构。/wp-json/ 在普通permalink下无法访问,因为目前根本没有可用的重定向规则。在这种情况下是否可以使用REST端点?自定义和内置。