Php 选中父类别时自动检查子类别/术语

Php 选中父类别时自动检查子类别/术语,php,wordpress,Php,Wordpress,我有这个大陆->国家类别设置为一个自定义的职位类型 - Africa - Uganda - Zambia - Zimbabwe - Asia - Afghanistan - Bahrain - Bangladesh - Bhutan 我认为这可以通过某种WordPress功能来解决,如果父类别Africa被选中,该功能将自动检查一个大陆的所有子类别国家,而不是像plugin那样的其他方式 例如,如果选中非洲,则应自动选中所有非洲国家。如果

我有这个大陆->国家类别设置为一个自定义的职位类型

- Africa
    - Uganda
    - Zambia
    - Zimbabwe
- Asia
    - Afghanistan
    - Bahrain
    - Bangladesh
    - Bhutan
我认为这可以通过某种WordPress功能来解决,如果父类别Africa被选中,该功能将自动检查一个大陆的所有子类别国家,而不是像plugin那样的其他方式

例如,如果选中非洲,则应自动选中所有非洲国家。如果仅选中津巴布韦,则不应选中父类别Africa

此外,如果未选中非洲,非洲国家也应自动取消选中。

编辑: 我有一个相对较长的答案,可以很好地检查子术语,但在父母未检查时取消检查它们就不太好了。我在这里支持这个答案:也可以在

新答案: 好吧,事实上我为这个结果感到很自豪。在做了一些研究之后,我发现了钩子,它在函数的末尾被激发,位于

在这个钩子上,它接受6个参数:$object\u id、$terms、$tt\u id、$taxonomy、$append、$old\u tt\u id。这里对于check子项和uncheck子项来说,重要的是$tt_id和$old_tt_id。这些分别是新术语ID和旧术语ID的数组

这使我们能够比较这两个阵列,并查看添加了哪些ID以及删除了哪些ID。这一点很重要,因为你可以选择非洲,然后取消选择非洲,现在选择亚洲。这里有一个方便的函数,可以让您看到这两种差异:

function array_diff_once($a1, $a2){
    foreach($a2 as $val){
        if( false !== ($pos = array_search($val, $a1)) ){
            unset($a1[$pos]);
        }
    }
    
    return array_values($a1);
}
因此,我们不必使用save_post钩子,而是可以比较set_object_terms钩子上添加/删除的术语,并为每个术语添加/删除子术语。注意,如果帖子没有发布,在不合适的时候自动保存,等等,这也会触发,所以我设置了一些中止条件

add_action( 'set_object_terms', 'so_60079535_toggle_child_terms', 10, 6 );
function so_60079535_toggle_child_terms( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ){
    // Abort if this is an autosave/backup
    if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
        return;

    // Abort if no ids are set from before or now
    if( empty($tt_ids) && empty($old_tt_ids) )
        return;

    // Only do things if this post is published (front facing)
    $post_status = get_post_status( $object_id );
    if( $post_status != 'publish' )
        return;

    // What terms where ADDED, and which were REMOVED?
    $added_terms   = array_diff_once( $tt_ids, $old_tt_ids );
    $removed_terms = array_diff_once( $old_tt_ids, $tt_ids );

    // Any terms ADDED this time?
    if( !empty($added_terms) ){
        foreach( $added_terms as $added_term ){
            // Do any of these added terms have children?
            if( $added_child_terms = get_term_children( $added_term, $taxonomy ) ){
                // Append those children
                wp_set_object_terms( $object_id, $added_child_terms, $taxonomy, true );
            }
        }
    }

    // Any terms REMOVED?
    if( !empty($removed_terms) ){
        foreach( $removed_terms as $removed_term ){
            // Do any of the removed terms have children?
            if( $removed_child_terms = get_term_children( $removed_term, $taxonomy ) ){
                // Remove them all
                wp_remove_object_terms( $object_id, $removed_child_terms, $taxonomy, true );
            }
        }
    }
}
事实上,我已经把这段代码放在了我的测试站点上,它似乎工作得完美无缺,不管孙子/大孙子术语有多深,一次添加或删除了多少。另一件巧妙的事情是,这个钩子已经传递了$taxonomy参数,因此它应该可以自动地用于任何和所有添加的分类法。如果不希望这样做,您可以随时非常轻松地为某些分类法、帖子类型等添加中止条件。

编辑: 我有一个相对较长的答案,可以很好地检查子术语,但在父母未检查时取消检查它们就不太好了。我在这里支持这个答案:也可以在

新答案: 好吧,事实上我为这个结果感到很自豪。在做了一些研究之后,我发现了钩子,它在函数的末尾被激发,位于

在这个钩子上,它接受6个参数:$object\u id、$terms、$tt\u id、$taxonomy、$append、$old\u tt\u id。这里对于check子项和uncheck子项来说,重要的是$tt_id和$old_tt_id。这些分别是新术语ID和旧术语ID的数组

这使我们能够比较这两个阵列,并查看添加了哪些ID以及删除了哪些ID。这一点很重要,因为你可以选择非洲,然后取消选择非洲,现在选择亚洲。这里有一个方便的函数,可以让您看到这两种差异:

function array_diff_once($a1, $a2){
    foreach($a2 as $val){
        if( false !== ($pos = array_search($val, $a1)) ){
            unset($a1[$pos]);
        }
    }
    
    return array_values($a1);
}
因此,我们不必使用save_post钩子,而是可以比较set_object_terms钩子上添加/删除的术语,并为每个术语添加/删除子术语。注意,如果帖子没有发布,在不合适的时候自动保存,等等,这也会触发,所以我设置了一些中止条件

add_action( 'set_object_terms', 'so_60079535_toggle_child_terms', 10, 6 );
function so_60079535_toggle_child_terms( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ){
    // Abort if this is an autosave/backup
    if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
        return;

    // Abort if no ids are set from before or now
    if( empty($tt_ids) && empty($old_tt_ids) )
        return;

    // Only do things if this post is published (front facing)
    $post_status = get_post_status( $object_id );
    if( $post_status != 'publish' )
        return;

    // What terms where ADDED, and which were REMOVED?
    $added_terms   = array_diff_once( $tt_ids, $old_tt_ids );
    $removed_terms = array_diff_once( $old_tt_ids, $tt_ids );

    // Any terms ADDED this time?
    if( !empty($added_terms) ){
        foreach( $added_terms as $added_term ){
            // Do any of these added terms have children?
            if( $added_child_terms = get_term_children( $added_term, $taxonomy ) ){
                // Append those children
                wp_set_object_terms( $object_id, $added_child_terms, $taxonomy, true );
            }
        }
    }

    // Any terms REMOVED?
    if( !empty($removed_terms) ){
        foreach( $removed_terms as $removed_term ){
            // Do any of the removed terms have children?
            if( $removed_child_terms = get_term_children( $removed_term, $taxonomy ) ){
                // Remove them all
                wp_remove_object_terms( $object_id, $removed_child_terms, $taxonomy, true );
            }
        }
    }
}

事实上,我已经把这段代码放在了我的测试站点上,它似乎工作得完美无缺,不管孙子/大孙子术语有多深,一次添加或删除了多少。另一件巧妙的事情是,这个钩子已经传递了$taxonomy参数,因此它应该可以自动地用于任何和所有添加的分类法。如果不希望这样做,您可以随时为某些分类法、帖子类型等添加中止条件,非常容易。

找到自定义帖子分类法名称,将其替换为“category”。当选择非洲时,所有非洲国家都被选中-完美!唯一的问题是当取消选择非洲时,非洲国家仍然被选中,该功能如何恢复?很抱歉,这比我想象的要大,所以我不得不在午餐休息时处理它-但我想我找到了一个解决方案,它将完全按照您所描述的方式工作!我已经更新了答案并备份了旧的答案-看一看,让我知道!我添加了$taxonomy='custom_post_type';开始时限制自定义帖子类型。你做得很好,非常感谢!找到自定义帖子分类名称,将其替换为“category”。当选择非洲时,所有非洲国家都被选中-完美!唯一的问题是当取消选择非洲时,非洲国家仍然被选中,如何恢复此功能?抱歉,这比我想象的要大,所以我没有 o在我午餐休息的时候做这件事-但我想我找到了一个解决方案,它将完全按照你所描述的方式工作!我已经更新了答案并备份了旧的答案-看一看,让我知道!我添加了$taxonomy='custom_post_type';开始时限制自定义帖子类型。你做得很好,非常感谢!