วิธีการทำให้สถานะเปิด / ปิดและซ่อน / แสดง metaboxes บันทึกไว้ในแต่ละโพสต์?


9

ปัญหาที่แท้จริงของฉันค่อนข้างซับซ้อนดังนั้นฉันจะลองที่นี่เพื่อทำให้เป็นนามธรรมและทำให้มันง่าย

ฉันกำลังทำงานกับแอพที่กำหนดเองโดยใช้ WordPress ฉันลงทะเบียนประเภทโพสต์ที่กำหนดเองมาเรียกมันว่า "คน" ที่ฉันเก็บข้อมูลเกี่ยวกับ ... คน

CPT รองรับเฉพาะชื่อเรื่องการโพสต์และฟิลด์เริ่มต้นของเนื้อหาโพสต์ แต่มีเมท็อกซ์เพื่อเก็บข้อมูลบุคคล (คิดว่าแอพของฉันเป็นสมุดที่อยู่)

ดังนั้นมี metabox ในการจัดเก็บข้อมูลส่วนบุคคลหนึ่งในการจัดเก็บข้อมูลเครือข่ายสังคมอีกเพื่อจัดเก็บข้อมูลที่เกี่ยวข้องกับการทำงานคือถ้าคนนั้นคือฉันลูกค้าลูกค้าซัพพลายเออร์ถ้าเรามีเครดิตหรือเดบิต ...

ฉันทำให้มันง่ายขึ้นที่นี่ แต่มีจำนวนของ metaboxes ที่สอดคล้องกันสมมุติว่า 12

ปัญหาของฉันคือบางคนที่ฉันต้องการจัดเก็บข้อมูลเป็นเพียงการติดต่อแบบสุ่มและฉันต้องการเก็บเฉพาะข้อมูลส่วนบุคคลอื่น ๆ เป็นเพื่อนและฉันต้องการเก็บข้อมูลส่วนบุคคลและข้อมูลเครือข่ายสังคมอื่น ๆ คือลูกค้าหรือซัพพลายเออร์และฉัน ต้องการเก็บข้อมูลที่เกี่ยวข้องกับงาน

หากเมื่อแก้ไขโพสต์ฉันซ่อน (ผ่านเมนูตัวเลือกหน้าจอ ) หรือปิดเมท็อกซ์ที่ฉันไม่ต้องการเมื่อฉันเปิดโพสต์อื่นที่ฉันต้องการพวกเขาฉันต้องแสดงหรือเปิดอีกครั้ง นั่นเพราะ metaboxes ตำแหน่ง / / สถานะการสั่งซื้อจะถูกบันทึกไว้บนพื้นฐานต่อผู้ใช้เป็นข้อมูลเมตาของผู้ใช้

หากคุณนึกภาพในบางโพสต์ฉันต้องการเมท็อก 2 ตัวในบาง 10 และใน 5 คุณเข้าใจว่ามันน่ารำคาญเพราะการแสดงทั้งหมด / เปิดทำให้หน้าจอแก้ไขสามารถเข้าถึงได้ต่ำ (แถบเลื่อนดูเหมือนไม่มีที่สิ้นสุด) และบางครั้งข้อมูลที่ฉันมองหาคือ ในตอนท้ายของหน้าหลังจากกลุ่มของ metaboxes ที่ไม่มีข้อมูล ...

คำถาม:

เป็นไปได้หรือไม่ที่จะบันทึกตำแหน่ง / สถานะ / คำสั่งของ metaboxes แบบโพสต์สำหรับประเภทโพสต์เฉพาะ?


PS: ฉันรู้ว่าบาง js / jQuery สามารถแก้ปัญหาได้ แต่ถ้าเป็นไปได้ฉันจะหลีกเลี่ยงการแก้ปัญหาจาวาสคริปต์

คำตอบ:


8

ปัญหาหลัก:

ปัญหาหลักที่นี่เป็นที่ในclosing- , hiding-และordering-โทร AJAX, ไม่มี ID โพสต์ส่งไปพร้อมกับอัตรา นี่คือตัวอย่างข้อมูลแบบฟอร์มสองแบบ:

1) action:closed-postboxes
closed:formatdiv,tagsdiv-post_tag,trackbacksdiv,authordiv
hidden:slugdiv
closedpostboxesnonce:8723ee108f
page:post

2) action:meta-box-order
_ajax_nonce:b6b48d2d16
page_columns:2
page:post
order[side]:submitdiv,formatdiv,categorydiv,tagsdiv-post_tag,postimagediv
order[normal]:postexcerpt,postcustom,trackbacksdiv,commentsdiv,authordiv
order[advanced]:

เราสามารถแก้ไขได้โดยใช้การโทร ajax แบบกำหนดเองอื่น

แน่นอนว่าเราสามารถเบ็ดเข้ากับsave_posthook และแก้ไขข้อมูลทุกครั้งที่บันทึกโพสต์ แต่นั่นไม่ใช่ประสบการณ์ UI ปกติดังนั้นจึงไม่พิจารณาที่นี่

มีอีกโซลูชันที่ไม่หรูหราสำหรับ PHP ที่อธิบายไว้ด้านล่าง:

โซลูชันที่ไม่ใช้ Javascript:

คำถามคือที่เก็บข้อมูล? ในฐานะที่เป็นข้อมูลเมตาของผู้ใช้โพสต์ข้อมูลเมตาหรืออาจอยู่ในตารางที่กำหนดเอง

ที่นี่เราเก็บไว้เป็นข้อมูลเมตาของผู้ใช้และใช้เวลาปิดการโพสต์กล่อง meta เป็นตัวอย่าง

เมื่อclosedpostboxes_postอัปเดตค่าเมตาเราจะบันทึกลงในclosedpostboxes_post_{post_id}ค่าเมตาเช่นกัน

จากนั้นเราจะทำการดึงข้อมูลclosedpostboxes_postเพื่อแทนที่ด้วยค่าเมตาที่สอดคล้องกันโดยยึดตาม ID ผู้ใช้และรหัสโพสต์

a) การอัพเดทในระหว่างการclosed-postboxesกระทำ ajax:

เราสามารถดึงรหัสโพสต์ผ่านทาง wp_get_referer()และจากนั้นใช้url_to_postid()ฟังก์ชั่นที่ใช้งานง่าย ครั้งแรกที่ผมรู้เกี่ยวกับเรื่องนี้ฟังก์ชั่น "ตลก" หลังจากที่ได้อ่านคำตอบจาก @s_ha_dumไม่กี่เดือนที่ผ่านมา ;-) แต่น่าเสียดายที่ฟังก์ชั่นไม่รู้จัก ?post=123ตัวแปร GET แต่เราสามารถทำเคล็ดลับเล็ก ๆ น้อย ๆ โดยเพียงแค่เปลี่ยนไปp=123จะได้รับรอบมัน

เราสามารถเชื่อมupdated_user_metaต่อclosedpostboxes_postได้ซึ่งจะมีการอัปเดตหลังจากที่ข้อมูลเมตาของผู้ใช้สำหรับได้รับการอัปเดตแล้ว:

add_action( 'updated_user_meta',                           
    function ( $meta_id, $object_id, $meta_key, $_meta_value )
    {
        $post_id = url_to_postid( str_replace( 'post=', 'p=', wp_get_referer() ) );
        if( 'closedpostboxes_post' === $meta_key && $post_id > 0 )
            update_user_meta( 
                $object_id, 
                'closedpostboxes_post_' . $post_id, 
                $_meta_value 
            );
    }
, 10, 4 );

b) การดึงข้อมูล:

เราสามารถขอลงในget_user_option_closedpostboxes_postเบ็ดเพื่อแก้ไขข้อมูลที่ดึงมาจากclosedpostboxes_postเมตาผู้ใช้:

add_filter( 'get_user_option_closedpostboxes_post',
    function ( $result, $option, $user )
    {
        $post_id = filter_input( INPUT_GET, 'post', FILTER_SANITIZE_NUMBER_INT );
        $newresult = get_user_option( 'closedpostboxes_post_'. $post_id , $user->ID );
        return ( $newresult ) ? $newresult : $result;
    }
, 10, 3 );

เราอาจต้องการพิจารณากรณีที่ไม่มีโพสต์closedpostboxes_post_{post_id}ให้บริการ closedpostboxes_postดังนั้นมันจะใช้การตั้งค่าที่บันทึกไว้ล่าสุดจาก บางทีคุณอาจต้องการให้เปิดทั้งหมดหรือปิดทั้งหมดในกรณีเริ่มต้น มันจะง่ายต่อการแก้ไขพฤติกรรมนี้

สำหรับประเภทโพสต์ที่กำหนดเองอื่น ๆ เราสามารถใช้closedpostboxes_{post_type}เบ็ดที่เกี่ยวข้อง

เช่นเดียวกันควรเป็นไปได้สำหรับการสั่งซื้อและการซ่อนเมตาโบอกซ์ด้วยmetaboxhidden_{post_type}และmeta-box-order_{post_data}เมตาของผู้ใช้

ป.ล. : ขออภัยสำหรับคำตอบสุดสัปดาห์ที่ยาวเกินไปเนื่องจากควรสั้น & jolly ;-)


ยอดเยี่ยม +1 N / P สำหรับคำตอบที่ยาวฉันจะไม่คาดหวังคำสั้น ๆ พูดตามตรงฉันไม่ได้คาดหวังอะไรเลยในวันหยุดสุดสัปดาห์ :) สองสิ่งที่ฉันชอบมาก: ลำดับที่ 1 แนวคิดในการเก็บข้อมูลตามผู้ใช้และต่อโพสต์: ความคิดของฉันคือเก็บในโพสต์เมตา แต่ด้วยวิธีนี้ทั้งหมด ผู้ใช้จะมีสถานะเดียวกัน แนวคิดที่จะใช้'get_user_option_*_post'เพื่อทำให้ WP รู้จักข้อมูลที่กำหนดเอง เพียงคิดว่าฉันไม่ชอบมากเกินไปคือการใช้งานwp_get_refererใน$_SERVERvar ที่ไม่น่าเชื่อถือจริงๆแต่ฉันคิดว่าฉันมีความคิดที่จะเอาชนะ "ปัญหาหลัก";)
gmazzap

ขอบคุณฉันคิดว่ามันขึ้นอยู่กับจำนวนผู้ใช้และโพสต์ที่จะเก็บข้อมูลได้ดีที่สุด ข้อมูลนี้ควรมี TTL และถูกลบเช่นเดือนละครั้งหรือไม่ ใช่ฉันเห็นด้วยกับคุณเกี่ยวกับwp_get_referer()วิธีการนั่นเป็นสาเหตุที่ฉันเรียกมันว่าโซลูชัน PHP ที่ไม่หรูหรา ;-) ฉันแรกคิดเกี่ยวกับการจัดเก็บรหัสโพสต์ปัจจุบันสำหรับผู้ใช้แต่ละคน แต่นั่นไม่ได้ผลหากผู้ใช้กำลังแก้ไขสองคน โพสต์ในเบราว์เซอร์ รอคอยที่จะรับฟังความคิดของคุณเกี่ยวกับ "ปัญหาหลัก" เพลิดเพลินไปกับวันหยุดสุดสัปดาห์ ;-)
birgire

หลังจาก 43 วันการ upvote จำฉันไว้ให้ตอบคำถามนี้ ขอบคุณอีกครั้งสำหรับคำตอบของคุณ
gmazzap

6

ตามที่ระบุโดยbirgireในคำตอบของเขา WordPress ใช้ AJAX เพื่ออัปเดตสถานะ metaboxes และข้อมูลที่ส่งผ่านในคำขอ AJAX ไม่รวมรหัสโพสต์และทำให้การอัปเดตสถานะของกล่องเป็นเรื่องยากสำหรับแต่ละโพสต์

เมื่อฉันพบการกระทำ AJAX ที่ใช้โดย WordPress 'closed-postboxes'ฉันค้นหาสตริงนี้ในโฟลเดอร์ admin js เพื่อค้นหาว่า WordPress ทำให้การร้องขอ AJAX เป็นอย่างไร

ผมพบว่ามันเกิดขึ้นในpostbox.jsที่บรรทัด #

ดูเหมือนว่า:

save_state : function(page) {
  var closed = $('.postbox').filter('.closed').map(function() {
      return this.id;
    }).get().join(',');
  var hidden = $('.postbox').filter(':hidden').map(function() {
      return this.id;
    }).get().join(',');
  $.post(ajaxurl, {
    action: 'closed-postboxes',
    closed: closed,
    hidden: hidden,
    closedpostboxesnonce: jQuery('#closedpostboxesnonce').val(),
    page: page
  });
}

โดยพื้นฐานแล้ว WordPress จะมองรายการ DOM ด้วยคลาส 'ตู้ไปรษณีย์' และคลาส 'ปิด' และสร้างรายการที่คั่นด้วยจุลภาคของ ID ของพวกเขา เช่นเดียวกับรายการ DOM ที่ซ่อนอยู่ซึ่งมีคลาส 'ตู้ไปรษณีย์'

ดังนั้นความคิดของฉันคือ: ฉันสามารถสร้างเมท็อกซ์ปลอมที่มีคลาสที่ถูกต้องและถูกซ่อนไว้ตั้งค่ารหัสเพื่อให้มี ID โพสต์และด้วยวิธีนี้ฉันสามารถดึงข้อมูลได้ในคำขอ AJAX

นี่คือสิ่งที่ฉันทำ:

add_action( 'dbx_post_sidebar', function() {
    global $post;
    if ( $post->post_type === 'mycpt' ) {
        $id = $post->ID;
        $f = '<span id="fakebox_pid_%d" class="postbox closed" style="display:none;"></span>';
        printf( $f, $id );
    }
});

ด้วยวิธีนี้ฉันสร้าง metabox ที่ถูกปิดและซ่อนอยู่เสมอดังนั้น WordPress จะส่ง ID ในรูปแบบ$_POSTvar ในคำขอ AJAX และเมื่อ ID กล่องปลอมมี ID โพสต์ในวิธีที่คาดเดาได้ฉันสามารถจดจำโพสต์ได้

หลังจากนั้นฉันดูว่า WordPress ทำงานอย่างไรกับ AJAX

ในadmin-ajax.phpบรรทัดที่ 72 WordPress 'wp_ajax_closed-postboxes'มีความสำคัญ 1

ดังนั้นในการดำเนินการก่อน WordPress ฉันสามารถขอให้ดำเนินการเดียวกันกับลำดับความสำคัญ 0

add_action( 'wp_ajax_closed-postboxes', function() {

    // check if we are in right post type: WordPress passes it in 'page' post var
    $page = filter_input( INPUT_POST, 'page', FILTER_SANITIZE_STRING );
    if ( $page !== 'mycpt' ) return;

    // get post data
    $data = filter_input_array( INPUT_POST, array(
        'closed' => array( 'filter' => FILTER_SANITIZE_STRING ),
        'hidden' => array( 'filter' => FILTER_SANITIZE_STRING )
    ) );

    // search among closed boxes for the "fake" one, and return if not found
    $look_for_fake = array_filter( explode( ',', $data[ 'closed' ] ), function( $id ) {
         return strpos( $id, 'fakebox_pid_' ) === 0;
    } );
    if ( empty( $look_for_fake ) ) return;

    $post_id = str_replace( 'fakebox_pid_', '', $look_for_fake[0] );
    $user_id = get_current_user_id();

    // remove fake id from values
    $closed = implode(',', array_diff( explode(',', $data['closed'] ), $look_for_fake ) );
    $hidden = implode(',', array_diff( explode(',', $data['hidden'] ), $look_for_fake ) );

    // save metabox status on a per-post and per-user basis in a post meta
    update_post_meta( $post_id, "_mycpt_closed_boxes_{user_id}", $closed );
    update_post_meta( $post_id, "_mycpt_hidden_boxes_{user_id}", $hidden );

}, 0 );

การมีข้อมูลที่บันทึกในโพสต์เมตาทำให้สามารถกรองget_user_option_closedpostboxes_mycptและget_user_option_metaboxhidden_mycpt(ทั้งสองรูปแบบของget_user_option_{$option}ตัวกรอง) เพื่อบังคับใช้ตัวเลือกการโหลด WordPress จากโพสต์เมตา:

add_filter( 'get_user_option_closedpostboxes_mycpt', function ( $result, $key, $user ) {
    global $post;
    $meta = get_post_meta( $post->ID, "_mycpt_closed_boxes_{$user->ID}", TRUE );
    if ( ! empty( $meta ) ) {
        $result = $meta;
    }
    return $result;
}, 10, 3 );

และ

add_filter( 'get_user_option_metaboxhidden_mycpt', function ( $result, $key, $user ) {
    global $post;
    $meta = get_post_meta( $post->ID, "_mycpt_hidden_boxes_{$user->ID}", TRUE );
    if ( ! empty( $meta ) ) {
        $result = $meta;
    }
    return $result;
}, 10, 3 );

ช่างเป็นความคิดที่ดีหากใช้ metabox ที่ซ่อนอยู่กับข้อมูลที่เกี่ยวข้อง +1
birgire

ขอบคุณ @ Birgire และขอขอบคุณอีกครั้งสำหรับ A ของคุณความคิดในการบันทึกข้อมูลทั้งต่อผู้ใช้และต่อโพสต์นั้นเป็นของคุณ :)
gmazzap
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.