wpmetabox / meta-box

The best plugin for WordPress custom fields and custom meta boxes
https://metabox.io
1.19k stars 423 forks source link

changing query_args according to post meta #1222

Closed btsimonh closed 6 years ago

btsimonh commented 6 years ago

Hi, I recently had a requirement to filter the users in a type=>'user' metabox based on the post meta.

Basically, it was a 'user access' entry, and I only wanted to list users who belonged to the same company as who 'owned' the post being edited.

Metabox defn:

                array(
                    'name'        => __( 'User Access', 'me' ),
                    'id'          => 'user_access',
                    'type'        => 'user',
                    'multiple'    => true,
                    'field_type'  => 'select_advanced',
                    'placeholder' => __( 'Start typing...', 'me' ),
                    'query_args'  => array(),
                ),

I could not identify a filter which was run in the Loop, and which could change the query_args in the metabox. Did I miss something? The action rwmb_before was attractive, until I realised that the metaboxes are passed by Value, so modifying them is not possible.

In the end, I added a filter into user.php:

    public static function get_options( $field ) {
        **$field = apply_filters('YU_rwmb_user_query', $field);**
        $query = new WP_User_Query( $field['query_args'] );
        error_log(print_r($query->request,1));
        return $query->get_results();
    }

which works fine.

and the filter code:

    add_filter( 'YU_rwmb_user_query' , 'deal_with_field', 10, 1 );
    function deal_with_field($field){
      if ($field['id'] == 'user_access'){
        global $post;
        if ($post){
          //error_log('normalize_field'.print_r($field,1));
          $company = get_post_meta($post->ID, 'company', true);
          $field['query_args']['meta_key'] = 'company';
          $field['query_args']['meta_value'] = $company;
          $field['query_args']['meta_compare'] = '=';
        }
      }
      return $field;
    }

Questions: Is there a filter which is already in place which would do this (enable dynamic changes to the field according to post data)? Am I just trying to do it in the wrong way?

If not, it might be something useful to consider S

rilwis commented 6 years ago

Hi,

Why don't you just setup the query args before setting up the field, like this:

$query_args = ...; // Your conditions go here

$field = [
    ... // other settings
    'query_args' => $query_args,
];
btsimonh commented 6 years ago

hi rilwis, at the point where rwmb_meta_boxes is called, the global $post is not yet set :(. i.e.:

    add_filter( 'rwmb_meta_boxes', 'devices_meta_boxes' );
    function devices_meta_boxes( $meta_boxes ) {
        global $post;
        error_log(print_r($post,1));

always prints empty, so the query args can't be made dependent upon the post. - unless there is some special php way of the $query_args being evaluated at the time of use, rather than being a constant string (I did try setting query_args['meta_value'] = '{$post->company}', but printing the SQL statement revealed it just took the string with curlies all the way through.... i'm not sure where/how curly brackets assessment is done in php, but maybe this would be a nice solution for metabox fields if not too inefficient?). I guess that I could get the postID direct from the query string or URL, or in some other WP way; but one might anticipate that this kind of thing may be wanted in a Loop where multiple posts are present?

Another thought came to me after implementation. My 'Company' field is actually another metabox field, so My implementation is sub-standard, since I can change the company, and the userlist presented no longer matches it. So a better solution for my current requirement would be some custom javascript which based the userlist presented on the other field (i.e. the php would need to give all users and their companies), so when I get time I'll look at the Html filter callbacks and the select list callbacks, and see if I can make them suit. However, this does not negate the possible usefulness of the stuff discussed above .... p.s. thanks for a good solid project :).

rilwis commented 6 years ago

Hi,

The rwmb_meta_boxes runs quite late, at init with priority 20. So, you can able to get $post object if you hook to init, like this:

add_action( 'init', function() {
    add_filter( 'rwmb_meta_boxes', function( $meta_boxes ) {
        // You can access $post here
    } );
} );

But also note that, as you just want to change the arguments for the admin area only, you might need to check to see if the variable is set. Otherwise, it might cause some errors in the frontend if you use rwmb_meta helper function. The helper function will get the field settings from rwmb_meta_boxes filter, FYI.