web-dev-qa-db-ja.com

投稿タイプまたは投稿IDで登録済みメタボックスを取得する

関連のない別の投稿タイプにあるときに、特定の投稿タイプ(または投稿ID)の登録済みメタボックスを取得する方法を探しています。特定のクライアントのニーズに合わせてカスタムの「編集」投稿タイプを作成しようとしており、メタボックスを「編集可能」タイプに登録しなくてもロードできるようにしたいと考えています。

説明:

私が達成しようとしているのは、別の投稿タイプからのメタボックスの実際の表示です。そのため、 "events"投稿タイプに日付、時刻、および場所を含むメタボックスがある場合、実際にメタボックスを再度登録することなく、同じボックスを別の投稿タイプに表示したいと思います。

4
Norcross

間違った仮定

@ Rarstまたは@ Dan-Cameronanswerが機能する可能性がありますが、投稿タイプ編集ページを表示しているときは、他の投稿タイプのメタボックスは次のようになります。すべて登録済み。

それが起こらない異なる場合があります。

  • メタボックスが、より一般的で一般的な"add_meta_boxes_{$post_type}"ではなく "add_meta_boxes" hookを使用して登録されている場合、 stealer postタイプの管理ページでは利用できません。
  • 一般的なpostフック "add_meta_boxes" をフックバックコールバックに渡すことで、現在の投稿タイプと現在の投稿オブジェクトを引数として渡します。それは、コールバックを登録することがいくつかのif条件でそれらの引数を使用するかもしれないことを意味します、それで我々は前のポイントと同じ状況にあることは可能です

つまり、投稿タイプ _ a _ のメタボックスをどのように登録するかを管理していないと、投稿タイプ _ bの投稿編集ページでそれらが有効になるかどうかわかりません。 _

あなたがそのコントロールを持っている場合には、それから @Rarst answer がうまくいくでしょう。

あなたがそのコントロールを持っていない場合、唯一の方法は少しトリッキーでしょう。

その場合のIMOあなたの唯一のチャンスは、あなたが箱を盗むことを望んでいるCPTのポスト編集ページにHTTPリクエスト(AJAXまたはWP HTTP API経由)を送ることです。ページにbox配列を返すようにするリクエスト変数を追加する必要があります(おそらくシリアライズされたJSONエンコーディング)。

その後、返されたボックスを現在の投稿のボックスとマージすることができます。これで完了です。

ポイントはそのようなコードです

  • 遅い(追加のリクエストがあるため)
  • 些細ではない

ですから、特定の投稿タイプの投稿用に登録されているすべてのボックスがどれであるかを制御できる場合は、 stealer postタイプ用にそれらのボックスを再度登録するだけで済みます。

あなたがどれがその投稿タイプのすべてのボックスであるか確信が持てない場合は、追加のHTTPリクエストがあなたの唯一のチャンスです。

そのワークフローを実装するクラスをここに投稿します( ここ は読みやすくするためにGistとして):

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()はRarstの回答から派生したものです。
  • 私はWP HTTP APIを使用して、すべてを1か所にまとめるための追加の要求を送信しましたが、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からのメタボックスは、たとえ表示されていても保存されず、オリジナルのものにアクセスできない場合は、おそらく別の保存ルーチンを作成する必要があります。

3
gmazzap

この投稿から '編集'投稿タイプを作成するオプションを提供する追加のメタボックス(チェックボックス、私は考えています)を作成します。このコードのために、それを 'transfer_data'と呼びます。

$ post_typeの値を、データを取得する元の投稿の投稿タイプに変更します。

元の投稿IDを「編集」投稿タイプのメタ値に保存する機能を追加しました。 'edit'メタボックスを元の投稿に保存するには、その値とこの関数(もちろん代わりに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' );
0
Mickey

私はこれが信頼できるものではないと主張するつもりはないが、これは私がどこまで得たかについてである:

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は操作対象の投稿タイプです。

0
Rarst

これはメタボックスが行くべき場所に十分近い場所に手動でメタボックスを挿入します。問題は、それらがその投稿タイプの標準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' );

もちろん、screen-> idをチェックして、mboxを正しいスクリーンに追加していることを確認する必要があります。これもメタボックスを同じように登録しないので、スクリーンオプションからそれらを削除するようなことはうまくいきません。


更新:これについて少し考えた後、これは私が上で説明したほど容易ではないように見えます。元の保存アクションでpost_typeをチェックする必要がある場合は手動でメタボックスを保存し、メタボックスに互換性がないという問題があるとします。

私が最初にグローバル変数を変更することで述べたように、これを回避する方法があるはずです。 ????

0
Dan Cameron