Support

Account

Forum Replies Created

  • Update after using my workaround for a while now.
    Field names are not unique and the internal ACF store just overwrites name => key mapping when a duplicate is added. When using my workaround this makes get value potentially return the wrong field key.

    For example, using the following setup would always return the key for field group 2:
    field group 1
    – field “link”
    field group 2
    – field “link”

    I’ve expanded my workaround to make a difference between options and post fields assuming options are actually unique making options always do a DB roundtrip again.

  • You yourself said that you are attempting to get a field value before init, this in my opinion is the reason for the error/warning you are seeing.

    I don’t think I said that anywhere in this thread. I said the init is always performed and as you mentioned the documentation states you do not have to wrap the get_value in an init hook. Please open up the acf_maybe_get_field function you will see the init call.

    The notice is just suppressed when you wrap all code in acf/init hook but the return value is still incorrect, it’s different when using the key to get the value. It would return 1 instead of true in my case and this might cause unexpected side effects. This is a bug in my opinion. And if it works with a key without wrapping with an init hook it should also work with a name in my opinion, which technically is a simple fix and as a side effect improves performance for some users. Not sure why you would not want that.

  • Ok, this really confuses me, I was in contact using the form you linked.
    The reply was that this kind of “custom usage” is beyond the scope of the support desk. I was referred to the forum and was told dev support was helping me. So I was under the impression the forums should be used for this kind of support.

    Guess I will retry.

  • I’m going to mark this thread as resolved with my own workaround but I still think this is a bug in the library and should be addressed. Using the init hook does not solve the issue it only suppresses the notice and actually returns the wrong return type!

    To conclude, in my opinion:
    ACF should either drop support for get_value by name or make acf_get_reference also look in the local store to resolve name -> key mapping instead of only relying on the DB or internal cache.

  • It seems like poor design, needing an additional roundtrip per field you get by name.
    Why not resolve it from the internal storage like I’m doing right now? Or not support fetching by name if it’s inefficient.
    With the new notice, you should always have the key mapping in the internal store anyway.

    If you must get field values before WP caches the meta values for a post then calling get_post_meta($post_id) will cause WP to cache all meta values for a post and reduce queries because ACF will not cause a query for each field.

    That is interesting to know but I guess won’t work with options that are heavily used in my case.

  • @ksksks I’ve been digging into the notice issue myself and found that using the field key prevents the error. Alternatively, you could try this snippet that I created.

    
    add_filter('acf/pre_load_reference', function ($reference, $name, $postId) {
        $fields = acf_get_local_store('fields');
    
        return $fields && array_key_exists($name, $fields->aliases) ? $fields->aliases[$name] : $reference;
    }, 10, 3);
    

    This probably saves a DB roundtrip per option for you.

  • After testing my last fix my total queries on my test page went down from ~190 to 140. I think this filter might be an overall improvement in reducing queries to fetch the field keys from the DB not just in my case.

  • Yes, wrapping the get_field() call inside an acf/init action in function.php is required. You can define the field group without it but if you try to get a value you are getting a value directly inside functions.php that happens before init.

    No, it is not. Not according to documentation and also not looking at the code.

    Please follow the code.
    1) get_field
    2) acf_maybe_get_field
    3) acf_init();
    ….
    Getting the actual value happens after these steps. Always.

  • Going through all the code I realized I could do a better performing work around which is the following:

    
    add_filter('acf/pre_load_reference', function ($reference, $name, $postId) {
        $fields = acf_get_local_store('fields');
    
        return $fields && array_key_exists($name, $fields->aliases) ? $fields->aliases[$name] : $reference;
    }, 10, 3);
    
  • Hi John,

    I’ve tested again with a clean install to make sure it’s not in my codebase.
    I took the latest WP 5.9 + latest ACF 5.11.4 and added the following to the function.php to trigger the error:

    
        acf_add_local_field_group([
            "key" => "group_Feature toggles",
            "title" => "Feature Toggles",
            "fields" => [
                [
                    "type" => "true_false",
                    "name" => "anchor_tags",
                    "label" => "Anchor Tags",
                    "key" => "field_feature_toggles_anchor_tags",
                ],
                ]
            ]);
            
        get_field('anchor_tags', 'option');
    

    Yes wrapping it in acf/init suppresses the notice but as the documentation states it should not be required. acf_maybe_get_field triggers init before it gets the value. So even without wrapping it will always be initialized looking at the code.

    I’ve been digging in the ACF codebase and I think the root cause lies within the acf_maybe_get_field code:

    
    /*
    *  acf_get_object_field
    *
    *  This function will return a field for the given selector.
    *  It will also review the field_reference to ensure the correct field is returned which makes it useful for the template API
    *
    *  @type    function
    *  @date    4/08/2015
    *  @since   5.2.3
    *
    *  @param   $selector (mixed) identifyer of field. Can be an ID, key, name or post object
    *  @param   $post_id (mixed) the post_id of which the value is saved against
    *  @param   $strict (boolean) if true, return a field only when a field key is found.
    *  @return  $field (array)
    */
    function acf_maybe_get_field( $selector, $post_id = false, $strict = true ) {
    
    	// init
    	acf_init();
    
    	// Check if field key was given.
    	if ( acf_is_field_key( $selector ) ) {
    		return acf_get_field( $selector );
    	}
    
    	// Lookup field via reference.
    	$post_id = acf_get_valid_post_id( $post_id );
    	$field   = acf_get_meta_field( $selector, $post_id );
    	if ( $field ) {
    		return $field;
    	}
    
    	// Lookup field loosely via name.
    	if ( ! $strict ) {
    		return acf_get_field( $selector );
    	}
    
    	// Return no result.
    	return false;
    }
    

    When get_field is called the second thing it does is call acf_maybe_get_field.
    So what happens:
    1) init
    2) check if key (it’s not)
    3) try to get the field using the hidden metadata using acf_get_meta_field which will fail because it has not yet been saved to the database.
    4) $strict is true so it will not trigger acf_get_field
    5) now we return false.

    The acf_get_meta_field only looks at the DB so this is where I think it initially goes wrong and because this method runs in strict mode it never calls acf_get_field which would resolve the field just fine.

    Why does acf_get_meta_field only look at the DB if the local store has a mapping between field keys and names?
    Why was this strict mode introduced? Did ACF stop supporting getting a value by name? That would explain a lot.

    How I think this issue should be resolved:
    acf_get_reference which is called by acf_get_meta_field should check the local store to map a name to a key and fall back to the DB when it can’t find one although I think that situation would trigger this new notice. So I wonder if fetching this info from the DB would be needed at all.

  • I also looked at the documentation and read the same “or”.
    I tested with the init hook and had the same result I think. I will test again and set up a clean WP install with minimal code to reproduce the issue if it persists. If it does I will share a repo.

  • Hi John,

    I’m not getting an error on the hook. The hook is a workaround.
    The error occurs on normal use.

    The field group is defined in a class that is directly loaded from the functions.php. Usage is as in my example, define the fields then fetch the value after.

    As mentioned in my initial post this only occurs for new options. The error is resolved after actually saving the option once on the settings page. This is because the database also holds a key -> name mapping resolving the issue.

  • Hi John,

    
    $fields = acf_get_local_store('fields');
    $fields->aliases
    

    This is the internal array in the ACF store that holds a mapping with field names to field ids.

    Here’s a very minimalistic representation of what I’m doing:

    
            acf_add_local_field_group([
                "key" => "group_Feature toggles",
                "title" => "Feature Toggles",
                "fields" => [
                    [
                        "type" => "true_false",
                        "name" => "anchor_tags",
                        "label" => "Anchor Tags",
                        "key" => "field_feature_toggles_anchor_tags",
                    ],
                ]
            ]);
            get_field('anchor_tags', 'option');
    

    The aliases array contains:
    "anchor_tags" => "field_feature_toggles_anchor_tags"

    So my pre load value work around just fetches the field key from the internal store and uses that to fetch the value instead of the name solving the issue.

  • I did a bit more testing on my workaround and I noticed something unexpected with the return type.
    When fetching by name a toggle (true/false) field returns a string "1" but doing the same with a key returns a bool true.

    I am now using the following workaround:

    
    add_filter('acf/pre_load_value', 'getFieldByName', 10, 3);
    function getFieldByName(mixed $value, string|int $post_id, array $field): mixed
    {
        if (!$field['key']) {
            $name = $field['name'];
            $fields = acf_get_local_store('fields');
                
            if ($fields && array_key_exists($name, $fields->aliases)) {
                return get_field($fields->aliases[$name], $post_id);
            }
        }
    
        return $value;
    }
    
Viewing 14 posts - 1 through 14 (of 14 total)