0

I'm using a custom meta field, to specify which post will appear as a featured one in the front page.

This meta field is a true/false value, declared through ACF.

I want this condition to only be applied to one post, i.e. when the user declares a post as "featured", and saves it, all the other ones (theoretically only the last one) that have this meta value checked, should be turned "off".

To achieve this, I've declared a function attached to the "save_post" action, that turns to "false" this specific post meta value, for the older featured posts.

This is my approximation, but there's something that I'm missing, because it is not working at all.

function only_one_agenda_featured( $post_id ) {

    // If this isn't a 'agenda' post, don't update it.
    if ( 'agenda' != $post->post_type ) {
        return;
    }

    // Stop when it is an autosave
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    // Prevent quick edit from clearing custom fields
    if (defined('DOING_AJAX') && DOING_AJAX) {
        return;
    }

    $post_meta_value = get_post_meta( $post_id, 'agenda_featured', TRUE );

    if ( $post_meta_value == '1' ) {
        $args = array(
            'meta_key'         => 'agenda_featured',
            'meta_value'       => '1',
            'post_type'        => 'agenda',
            'post__not_in'     => array($post_id),
            );
        $posts_to_update = get_posts( $args );
        foreach ( $posts_to_update as $post_to_update ) {
            update_post_meta( $post_to_update->ID, 'agenda_featured', '0', '1' );
        }
    }
}
add_action( 'save_post', 'only_one_agenda_featured' );

Any help will be appreciated.

2

I want this condition to only be applied to one post, i.e. when the user declares a post as "featured", and saves it, all the other ones (theoretically only the last one) that have this meta value checked, should be turned "off".

You might then try to save the one and only featured post ID as an option instead.

Then you don't need to pollute the post meta table with multiple false values.

  • As an option? Do you mean to save it as an option in the wp_options table? – Capiedge Nov 19 '15 at 16:31
  • yes, just store a single number in the wp_options table (via the options API), very simple to maintain @Capiedge – birgire Nov 19 '15 at 16:34
  • Maybe simple to mantain, but not easy to implement through the ACF control panel. Because I want to show this metabox in every single cpt while editing, not in an theme options section... – Capiedge Nov 19 '15 at 17:16
  • You wouldn't need ACF for this approach, you could check out the Codex example on how to add a metabox and then adjust it slightly with a single checkbox that stores the value in the options table. @Capiedge – birgire Nov 19 '15 at 17:46
0

The first problem is that $post is not in scope in your callback. You need to alter the code to pull that in:

function only_one_agenda_featured( $post_id, $post ) {
  // ...
}
add_action( 'save_post', 'only_one_agenda_featured' ,10, 2);

Given that change, things should work better, but the code is overly complicated. Unless you need a history of old agenda_featured posts, all you need to do is delete the old field and then set the new one.

function only_one_agenda_featured( $post_id, $post ) {

    // If this isn't a 'agenda' post, don't update it.
    if ( 'agenda' != $post->post_type ) {
        return;
    }

    // Stop when it is an autosave
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    // Prevent quick edit from clearing custom fields
    if (defined('DOING_AJAX') && DOING_AJAX) {
        return;
    }

    if (!empty($_POST['agenda_featured']) && true == $_POST['agenda_featured']) {
        // delete
        delete_metadata ( 'agenda', null, 'agenda_featured', null, true );
        // and insert
        add_post_meta( $post_id, 'agenda_featured', true);
    }

}
add_action( 'save_post', 'only_one_agenda_featured' ,10, 2);

You can further simplify that by using a post-type specific hook:

3580            do_action( "save_post_{$post->post_type}", $post_ID, $post, $update );

Something like:

function only_one_agenda_featured( $post_id, $post ) {

    // Stop when it is an autosave
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    // Prevent quick edit from clearing custom fields
    if (defined('DOING_AJAX') && DOING_AJAX) {
        return;
    }

    if (!empty($_POST['agenda_featured']) && true == $_POST['agenda_featured']) {
        // delete
        delete_metadata ( 'agenda', null, 'agenda_featured', null, true );
        // and insert
        add_post_meta( $post_id, 'agenda_featured', true);
    }

}
add_action( 'save_post_agenda', 'only_one_agenda_featured' ,10, 2);

You could make the delete/save a bit more efficient with straight SQL but I stuck to Core functions like a good boy ;)

  • OK @s_ha_dum, understood! but... before test it, let me ask you: where are you checking if this particular post has the meta value set as "true" by the user? – Capiedge Nov 19 '15 at 14:55
  • oops, sorry. I didn't. Too much hurry. Of course, you aren't checking either :) – s_ha_dum Nov 19 '15 at 14:57
  • @Capiedge : see the edit – s_ha_dum Nov 19 '15 at 15:03
  • seems to not work ... does not affect any of the posts declared as "featured". I checked the DB and still have the same values. Maybe "delete_metadata" is not being executed? – Capiedge Nov 19 '15 at 15:22
  • 1
    As I said, I'm declaring the custom field through ACF, so the $_POST value for this particular case is $_POST['acf']['field_562100c6cd7ad']. But still not working... – Capiedge Nov 19 '15 at 16:16
0

I apologize for resuming this old post, but I had a very similar need and investigated a bit on the answers posted here. Since I found a working way, let me share it.

My need was that only one post of type question had the first_question ACF flag checked. An answer suggests bringing it to options, but I needed that flag to control the visibility of other ACF fields.

The code I came up with for my solution is the following:

function update_first_question( $post_id ) {

    // If it's not the correct post type
    $post = get_post( $post_id );
    if ( $post->post_type != 'question' ) {
        return;
    }
    // Stop when it is an autosave
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }
    // Prevent quick edit from clearing custom fields
    if (defined('DOING_AJAX') && DOING_AJAX) {
        return;
    }

    // Check if the post being saved has first_question checked
    if (!empty($_POST['acf']['field_5d19b7f96cb7f']) && $_POST['acf']['field_5d19b7f96cb7f'] == TRUE) {

        // Delete 'first_question' meta from all posts
        delete_metadata ( 'post', NULL, 'first_question', NULL, TRUE );

        // Set 'first_question' meta for the post bring saved
        add_post_meta( $post_id, 'first_question', TRUE);

    }

}
add_action( 'acf/save_post', 'update_first_question', 5, 2);

What changes from the answers posted?

  • delete_metadata does not accept a custom post type, so it must be called with 'post'
  • When checking the $_POST array, you have to check the acf array with the field key in order to check its value
  • Since we're working with ACF fields it's better to hook it to acf/save_post in order to have the ACF values in $_POST. The hooked function should have a priority less than 10, to work with the value BEFORE being saved.
  • The above hook does not provide $post, so it must be retrieved by $post_id

So to answer the question, let me elaborate from s_ha_dum's answer. What you need is the following:

function only_one_agenda_featured( $post_id ) {

    // If it's not the correct post type
    $post = get_post( $post_id );
    if ( $post->post_type != 'agenda' ) {
        return;
    }
    // Stop when it is an autosave
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }
    // Prevent quick edit from clearing custom fields
    if (defined('DOING_AJAX') && DOING_AJAX) {
        return;
    }

    // Check if agenda_featured is being checked 
    if (!empty($_POST['acf']['field_562100c6cd7ad']) && $_POST['acf']['field_562100c6cd7ad'] == TRUE) {
        // delete
        delete_metadata ( 'post', NULL, 'agenda_featured', NULL, TRUE );
        // and insert
        add_post_meta( $post_id, 'agenda_featured', TRUE);
    }

}
add_action( 'acf/save_post', 'only_one_agenda_featured' , 5, 2); 

It probably won't help the OP 3 years after, but I hope it will help someone! Have a nice day!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.