按帖子类型或帖子ID获取已注册的元变量

时间:2015-06-18 作者:Norcross

我正在寻找一种方法,当在一个单独的、不相关的帖子类型上时,可以获取特定帖子类型(或帖子ID)的注册元盒。我正在为特定的客户需求制作一个自定义的“编辑”帖子类型,并希望能够加载元数据库,而无需将其注册为“可编辑”类型。

Clarification:

我试图完成的是另一个帖子类型的元盒的实际显示。因此,如果我的“事件”帖子类型有一个包含日期、时间和位置的元框,我希望在单独的帖子类型上显示这些相同的框,而不需要再次实际注册元框。

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

错误的假设

@Rarst@Dan-Cameron 答案可能有效,但假设在查看帖子类型编辑页面时,其他帖子类型的元数据库都已注册。

有不同的情况不会发生这种情况:

如果使用"add_meta_boxes_{$post_type}" 钩子,而不是更常见的泛型"add_meta_boxes" 它们在管理员页面中不适用于窃取者的帖子类型,即使是通用的帖子挂钩"add_meta_boxes" 将当前post类型和当前post对象作为参数传递给挂钩回调。这意味着注册回调可能会在某些情况下使用这些参数if 简而言之,我们可能处于与前一点相同的情况,除非您能够控制某些帖子类型A的元盒如何注册,否则您无法确定它们是否能够在某些帖子类型B的后期编辑页面中注册。

如果你有控制权,那么@Rarst answer 会成功的。

如果你没有控制权,唯一的办法就是有点棘手。

在这种情况下,我认为您唯一的机会是向要从中窃取方框的CPT的编辑后页面发送HTTP请求(AJAX或通过WP HTTP API)。您必须添加一些请求变量,使页面返回box数组(可能是序列化的或JSON编码的)。

之后,您可以将返回的框与当前帖子的框合并,就完成了。

关键是这样的代码

  • 很慢(因为额外的请求)
  • 不是微不足道的,因此,如果您可以控制哪些是为某个post类型注册的所有框,那么只需再次为“窃取者”post类型注册这些框,就可以更简单更快。

    如果您不能确定该post类型的所有框,那么额外的HTTP请求是您唯一的机会。

    我将在这里发布一个实现该工作流的类(here 作为提高可读性的依据):

    class MetaboxStealer
    {
        private static $stealing;
    
        private $post_type;
        private $boxes = [];
    
        /**
         * When the request contain special variable, this function will make it
         * return a serialized version of $wp_meta_boxes array and die.
         */
        public static function init()
        {
            add_filter(\'post_updated_messages\', function ($messages) {
                if (MetaboxStealer::stealing()) {
                    ob_start();
                    return [];
                }
                return $messages;
            });
    
            add_action(\'do_meta_boxes\', function () {
                if (MetaboxStealer::stealing()) {
                    ob_end_clean();
                    global $wp_meta_boxes;
                    echo serialize($wp_meta_boxes);
                    die();
                }
            });
        }
    
        /**
         * Checks that the request contain a special variable that will make request
         * return a serialized version of $wp_meta_boxes array and die.
         *
         * @return bool
         */
        public static function stealing()
        {
            if (is_null(self::$stealing)) {
                $screen = function_exists(\'get_current_screen\') ? get_current_screen() : null;
                $stealing = filter_input(INPUT_GET, \'stealing-boxes\', FILTER_SANITIZE_STRING);
                self::$stealing =
                    $screen instanceof \\WP_Screen
                    && $stealing
                    && wp_verify_nonce($stealing, $screen->post_type);
            }
            return self::$stealing;
        }
    
        /**
         * @param string $post_type Current post type
         */
        public function __construct($post_type)
        {
            $this->post_type = $post_type;
        }
    
        /**
         * Send a HTTP request to post edit page of a given CPT setting a special
         * variable that will make that page return serialized $wp_meta_boxes array.
         * After that, so obtained boxes are merged with the boxes for current post type.
         *
         * @param string $cpt CPT to steal metaboxes from
         */
        public function steal($cpt)
        {
            $vars = [
                \'post_type\'      => $cpt,
                \'stealing-boxes\' => wp_create_nonce($cpt),
            ];
            $url = add_query_arg($vars, admin_url(\'/post-new.php\'));
            $cookies = [];
            foreach ($_COOKIE as $name => $value) {
                if (\'PHPSESSID\' !== strtoupper($name)) {
                    $cookies[] = new \\WP_Http_Cookie([
                        \'name\'  => $name,
                        \'value\' => $value,
                    ]);
                }
            }
            $response = wp_remote_get($url, [\'cookies\' => $cookies]);
            if (! is_wp_error($response)) {
                $body = wp_remote_retrieve_body($response);
                if (is_serialized($body)) {
                    $boxes = unserialize($body);
                    $this->boxes = isset($boxes[$cpt]) ? $boxes[$cpt] : [];
                    empty($this->boxes) or $this->merge();
                }
            }
        }
    
        /**
         * Merge metaboxes for current post type with boxes obtained with 
         * a HTTP request to another CPT post edit page.
         */
        private function merge()
        {
            global $wp_meta_boxes;
            isset($wp_meta_boxes[$this->post_type]) or $wp_meta_boxes[$this->post_type] = [];
            foreach ($this->boxes as $context => $priorities) {
                foreach ($priorities as $priority => $boxes) {
                    if (! isset($wp_meta_boxes[$this->post_type][$context])) {
                        $wp_meta_boxes[$this->post_type][$context] = [];
                    }
                    if (! isset($wp_meta_boxes[$this->post_type][$context][$priority])) {
                        $wp_meta_boxes[$this->post_type][$context][$priority] = [];
                    }
                    $wp_meta_boxes[$this->post_type][$context][$priority] = array_merge(
                        $wp_meta_boxes[$this->post_type][$context][$priority],
                        $boxes
                    );
                }
            }
        }
    }
    
    备注:

    方法merge() 我已经使用WP HTTP API发送了额外的请求,将所有内容保存在一个地方,但是AJAX实现会更好,如何使用也很简单。

    // init the class
    add_action(\'admin_init\', [\'MetaboxStealer\', \'init\']);
    
    // use the class to merge boxes for current CPT with boxes for another CPT
    add_action(\'edit_form_after_editor\', function ($post) {
        $stealer_cpt = \'stealer-cpt\';
        $steal_from = \'events\';
        if ($post->post_type === $stealer_cpt) {
            $stealer = new MetaboxStealer($post->post_type);
            $stealer->steal($steal_from);
            // note that you can steal from different CPTs
            // $stealer->steal($another_steal_from);
        }
    });
    
    保存问题无论您如何在另一个CPT的后期编辑页面中显示来自一个CPT的框,保存例程都可能在保存后期元数据之前检查后期类型。

    在这种情况下,来自另一个CPT的元盒(即使显示)将不会保存,如果您无法访问原始的保存例程,则可能需要编写另一个保存例程。

SO网友:Mickey

创建一个额外的元框(我想是复选框),它提供了从该帖子创建“编辑”帖子类型的选项。对于这段代码,我们将其称为“transfer\\u data”。

将$post\\U type的值更改为我们将从中检索数据的原始帖子的帖子类型。

我添加了将原始帖子ID保存为“编辑”帖子类型的元值的功能。为了将“编辑”元数据库保存到原始帖子,您应该能够使用该值和该函数(使用update_post_meta() 相反,当然)对另一个要挂接的函数进行反向工程。

function create_edit_page($data){
    $post_type = \'the_post_type_to_work_with\';
    // Grab this post\'s ID
    $orig_id = $_POST[\'post_ID\'];
    // Grab the value of the \'transfer_data\' field
    $is_transfer_val = get_post_meta( $orig_id, \'transfer_data\');
    if($data[\'post_type\'] == $post_type && $is_transfer_val == TRUE && 
            isset($data[\'guid\']) && strlen($data[\'guid\'])>0 ){

        $post_id = wp_insert_post(
          array(
            \'comment_status\'  => \'closed\',
            \'ping_status\'   => \'closed\',
            \'post_author\'   => $data[\'post_author\'],
            \'post_name\'   => $slug,
                \'post_content\'  =>  $data[\'post_content\'],
            \'post_title\'    => $data[\'post_title\'],
            \'post_status\'   => \'publish\',
                // The custom post type \'editing\'
            \'post_type\'   => \'editing\'
          )
        );

        // create the meta fields
        $all_meta_boxes = get_post_meta( $orig_id );
        if(isset( $all_meta_boxes ) && is_array( $all_meta_boxes )){
            foreach($all_meta_boxes as $metakey => $metavalue){
                add_post_meta($post_id, $metakey, $metavalue);
            }
        }
        // add a meta field that points to original post (for editing purposes, etc.)
        add_post_meta($post_id, \'original_post_id\', $orig_id);

        // If you want to redirect the user after saving use the filter below
        // add_filter(\'redirect_post_location\', \'my_post_redirect_filter\', \'99\');

        return $data;
    }
    return $data;
}
add_action( \'wp_insert_post\', \'create_edit_page\', \'99\' );
// Or, call BEFORE updating the database with below action
//add_action( \'wp_insert_post_data\', \'create_edit_page\', \'99\' );

SO网友:Rarst

我不会声称这是可靠的,但这是关于我取得了多大进展:

add_action( \'add_meta_boxes\', function () {
    global $wp_meta_boxes;

    foreach ( $wp_meta_boxes[\'steal-from\'] as $context => $priorities ) {

        foreach ( $priorities as $priority => $boxes ) {

            if ( ! isset( $wp_meta_boxes[\'metabox-stealer\'][ $context ] ) ) {
                $wp_meta_boxes[\'metabox-stealer\'][ $context ] = [ ];
            }

            if ( ! isset( $wp_meta_boxes[\'metabox-stealer\'][ $context ][ $priority ] ) ) {
                $wp_meta_boxes[\'metabox-stealer\'][ $context ][ $priority ] = [ ];
            }

            $wp_meta_boxes[\'metabox-stealer\'][ $context ][ $priority ] = array_merge(
                $wp_meta_boxes[\'metabox-stealer\'][ $context ][ $priority ],
                $boxes
            );
        }
    }
}, 11 );
在哪里steal-frommetabox-stealer 是要操作的岗位类型。

SO网友:Dan Cameron

这将手动将元盒插入到元盒应该放置的足够近的位置。问题是,它们将被插入到该帖子类型的标准MBOX之前。

我相信如果必要的话,你可以更多地使用它,但这至少可以得到你需要的基本信息。

function monkey_advanced_meta_boxes() {
    $post = get_post( 2457 );
    do_meta_boxes( \'post_type_to_take_mboxes_from\', \'normal\', $post );
    do_meta_boxes( \'post_type_to_take_mboxes_from\', \'advanced\', $post );
}
add_action( \'edit_form_advanced\', \'monkey_advanced_meta_boxes\' );

function monkey_sidebar_meta_boxes() {
    $post = get_post( 2457 );
    do_meta_boxes( \'post_type_to_take_mboxes_from\', \'side\', $post );
}
add_action( \'submitpost_box\', \'monkey_sidebar_meta_boxes\' );
当然,您需要检查屏幕->id,并确保您正在将MBoxe添加到正确的屏幕。这也不会注册相同的元框,因此从屏幕选项中删除它们之类的操作将不起作用。

更新:想了想这一点之后,看起来这不像我上面描述的那么容易。我假设您想让元框保存元,如果原始保存操作正在检查post\\u类型(它应该这样做),那么您可能需要手动执行此操作,然后出现元框不兼容的问题。

我想一定有办法解决这个问题,就像我最初提到的修改全局变量一样。

结束

相关推荐

一个帖子影响另一个帖子设置的Metabox

This great answer David Gard让我有一个代谢箱sticky (复选框)用于一次只能在循环中的一个帖子上设置(选中)的帖子。如果帖子A选中了那个元框,那么B,C。。。Z不能检查它。为了能够选中其他帖子中的框,用户必须首先取消选中帖子A中的框,然后才能选中其他框中的框。这本身就很有效。然而,我在使用其他元框(文本输入字段)时遇到了问题。每个帖子都有三个元数据库:sticky 和两个输入文本字段review 和source (其他可能稍后添加)。假设帖子Asticky 选中,值为rev