Cách WordPress lưu dữ liệu Custom Fields – P1 – Data flow

Chúng ta đã biết custom fields là gì rồi và các tính năng mà WordPress cung cấp đi kèm với custom field. Bạn đang chuẩn bị những thứ cần thiết để ứng dụng nó vào một dự án nào đó phải không? Nhưng từ từ đã nhé. Trước khi bắt tay vào việc, mình nghĩ là bạn nên vọc thêm một chút nữa để hiểu hơn về bản chất của custom field. Câu hỏi đặt ra lúc này là: Điều gì sẽ xảy ra với custom field khi ta nhấn nút Lưu bài viết? Bài viết này và một vài viết tiếp sau đây sẽ giải thích quá trình này cho bạn nhé.

Có hai thứ sẽ xảy ra sau khi bạn nhấn nút Lưu bài viết. Đầu tiên, dữ liệu của bạn sẽ được xử lý bằng PHP bao gồm cả việc xử lý trực tiếp thông qua các hàm và gián tiếp thông qua các bộ lọc (filter). Như vậy, giá trị cuối cùng được lưu vào database có thể sẽ bị khác đi so với dữ liệu mà bạn nhập vào. Bài viết này sẽ làm rõ vấn đề này. Tiếp theo, dữ liệu của custom field sẽ được lưu vào database, và quá trình này mình sẽ nói cụ thể hơn ở phần 2 nhé.

Bây giờ, cùng xem lại code của plugin “Hello Custom Fields” mà mình đã tạo ở bài trước nhé.

function hcf_save( $post_id ) {
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }
    if ( $parent_id = wp_is_post_revision( $post_id ) ) { 
        $post_id = $parent_id;
    } 
    $field_list = [ 
        'hcf_author', 
        'hcf_published_date', 
        'hcf_price', 
    ]; 
    foreach ( $field_list as $fieldName ) { 
        if ( array_key_exists( $fieldName, $_POST ) ) { 
            update_post_meta( 
                $post_id, 
                $fieldName, 
                sanitize_text_field( $_POST[ $fieldName ] ) 
            ); 
        } 
    } 
} 
add_action( 'save_post', 'hcf_save' );

Chúng ta sẽ xem kỹ hơn phần code và kiểm tra từng hàm để hiểu về đường đi của dữ liệu khi một bài viết được lưu. Khi đó, bạn sẽ hiểu tại sao plugin của chúng ta phải chứa những dòng code này.

Mình sẽ phân chia đường đi của dữ liệu thành 2 giai đoạn, bao gồm:

  • Trước khi plugin gọi hàm update_post_meta;
  • Sau khi plugin gọi hàm update_post_meta.

Trước khi gọi hàm update_post_meta

Tải trang chỉnh sửa bài viết

Ở bước này, WordPress sẽ tạo dữ liệu nháp và khởi động hàm save_post.

Bắt đầu với trang Add New Post (wp-admin/post-new.php). Dòng ths 70 trong file này có nội dung như sau:

$post = get_default_post_to_edit( $post_type, true );

Đoạn code này là để tạo một bài viết mới dạng đối tượng là Post với các giá trị mặc định như các dữ liệu ban đầu. Tham số đầu tiên trong đoạn code trên ($post_type) chính là post type. Tham số thứ hai (true) là để xác định xem lưu bài viết vào database hay là không. Bởi vì tham số thứ hai là true, nên hàm wp_insert_post sẽ được gọi ra.

function get_default_post_to_edit( $post_type = 'post', $create_in_db = false ) { 
    … 
    if ( $create_in_db ) { 
        $post_id = wp_insert_post( array( 
      'post_title' => __( 'Auto Draft' ), 
      'post_type' => $post_type, 
      'post_status' => 'auto-draft' )); 
    …

Nếu bạn thoát trang Add New Post và quay trở lại trang All Posts page trong vòng dưới 60 giây, bạn sẽ không thấy bất kỳ bài viết nào mới cả. Tuy nhiên, vẫn tồn tại một bài viết mới đang ở trạng thái “bản nháp tự động” trong database đó. Nếu bạn cứ làm như vậy thì bạn sẽ có thêm rất nhiều các bản lưu nháp tự động (auto-draft) như này trong database. Nhưng đừng lo, chúng sẽ tự động bị xóa đi trong vòng 7 ngày thôi.

Hook save_post sẽ được khởi động khi hàm wp_insert_post chạy xong.

function wp_insert_post( $postarr, $wp_error = false ) { 
    … 
    do_action( 'save_post', $post_ID, $post, $update ); 
    … 
    return $post_ID; 
}

Đồng thời, hàm hcf_save trong plugin của chúng ta cũng sẽ được gọi ra (bởi vì chúng ta đã đính kèm nó vào hàm save_post). Nhưng vì hằng số DOING_AUTOSAVE chưa được xác định và thừa vì người dùng chưa nhập dữ liệu nào vào custom field cả, nên hàm này sẽ không thể chạy tiếp được. Hơn nữa, tuy những bài viết lưu nháp tự động sẽ bị xóa trong vòng 7 ngày, nhưng trong metadata thì lại không. Việc này sẽ dẫn đến việc bạn có một lượng dữ liệu rác rất lớn trong database.

Chúng ta có thể cải thiện plugin bằng cách bỏ qua các bài viết ở trạng thái lưu nháp tự động.

function hcf_save( $post_id ) { 
    … 
    if (get_post_status($post_id) === 'auto-draft') { 
        return; 
    } 
    … 
}

Lưu ý: Plugin Meta Box có một lựa chọn giúp bạn lưu các custom field bằng tính năng lưu tự động của Meta Box. Bạn có thể xem ở phần thiết lập cài đặt autosave.

Revision và heartbeat

Nếu bạn vẫn chưa rõ heartbeat là gì, thì bạn có thể đọc thêm thông tin về nó ở đây: Heartbeat API.

Theo mặc định thì cứ sau 60 giây, tính năng heartbeat sẽ gửi một yêu cầu đến server. Ở trong phần đăng tải bài viết, nếu bạn thay đổi các giá trị của các field cơ bản trong bài viết, heartbeat sẽ đính kèm dữ liệu của nó vào bài viết. Trên server, WordPress chạy kèm hàm heartbeat_autosave vào action heartbeat_received. Sau đó hàm heartbeat_autosave sẽ gọi hàm wp_autosave để cập nhật hoặc tạo các bản lưu (revision) cho post.

Hàm wp_autosave thường sẽ có dạng như sau:

function wp_autosave( $post_data ) { 
    // Back-compat 
    if ( ! defined( 'DOING_AUTOSAVE' ) ) 
        define( 'DOING_AUTOSAVE', true ); 
    … 
    if ( ! wp_check_post_lock( $post->ID ) 
      && get_current_user_id() == $post->post_author 
      && ( 'auto-draft' == $post->post_status 
       || 'draft' == $post->post_status ) ) { 
// Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked 
        return edit_post( wp_slash( $post_data ) ); 
    } else { 
// Non drafts or other users drafts are not overwritten. The autosave is stored in a special post revsion. 
        return wp_create_post_autosave( wp_slash( $post_data ) ); 
    } 
}

Nếu một bài viết mà không có cả bản nháp và bản nháp lưu tự động thì bạn không thể chỉnh sửa bài viết đó được. Thay vào đó, một bản revision sẽ được tạo dựa trên bài viết đó.

Hai hàm edit_postwp-creat_post_autosave sẽ đều gọi hàm wp_update_post, và hàm wp_update_post gọi hook save_post. Tuy nhiên, heartbeat sẽ không gửi dữ liệu của custom field mà nó chỉ chứa các dữ liệu cơ bản mặc định của bài viết mà thôi. Vì vậy hàm hcf_save sẽ không cập nhật custom field nếu như hằng số DOING_AUTOSAVE bị từ chối.

Lưu ý: Nếu bạn muốn giá trị của custom field được lưu trong các revision, bạn có thể lưu dấu các thay đổi của custom field bằng cách sử dụng plugin MB Revision của Meta Box.

Đăng tải bài viết

Khi trang Add New Post tải thành công, dữ liệu sẽ được gửi đến file /wp-admin/post.php.

Data flow của việc đăng tải bài viết trong WordPress

Khi bạn nhấn vào nút Publish, đoạn code dưới đây nằm trong file /wp-admin/post.php sẽ chạy:

check_admin_referer('update-post_' . $post_id);

$post_id = edit_post();

// Session cookie flag that the post was saved 
if ( isset( $_COOKIE['wp-saving-post'] ) && $_COOKIE['wp-saving-post'] === $post_id . '-check' ) { 
    setcookie( 'wp-saving-post', $post_id . '-saved', time() + DAY_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, is_ssl() ); }

redirect_post($post_id); // Send user on their way while we keep working exit();

Hàm edit_post sẽ chạy và hook save_post được gọi ra.

function edit_post( $post_data = null ) { 
    … 
    update_post_meta( $post_ID, '_edit_last', get_current_user_id() ); 

    $success = wp_update_post( $post_data ); 
    … 
    return $post_ID; 
}

Tuy nhiên, lúc này, nó sẽ chứa các dữ liệu của custom field trong biến $_POST, vì vậy hàm hcf_save sẽ cập nhật các giá trị của custom field bằng cách gọi hàm update_post_meta ra.

Sau khi gọi hàm update_post_meta

Hàm update_post_meta như sau:

function update_post_meta( $post_id, $meta_key, $meta_value, $prev_value = '' ) { 
    // Make sure meta is added to the post, not a revision. 
    if ( $the_post = wp_is_post_revision($post_id) )
        $post_id = $the_post; 
    $updated = update_metadata( 'post', $post_id, $meta_key, $meta_value, $prev_value ); 
    if ( $updated ) { 
        wp_cache_set( 'last_changed', microtime(), 'posts' ); 
    } 
    return $updated; 
}

Như vậy, hàm này sẽ giúp chúng ta kiểm tra các bản revision. Bây giờ, chúng ta có thể xóa đoạn code sau đây trong hàm hcf_save.

function hcf_save( $post_id ) { 
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { 
        return; 
    } 
    if (get_post_status($post_id) === 'auto-draft') { 
        return; 
    } 
    $field_list = [ 
        'hcf_author', 
        'hcf_published_date', 
        'hcf_price', 
    ]; 
    foreach ( $field_list as $fieldName ) { 
        if ( array_key_exists( $fieldName, $_POST ) ) { 
            update_post_meta( 
                $post_id, 
                $fieldName, 
                sanitize_text_field( $_POST[ $fieldName ] ) 
            ); 
        } 
    } 
} 
add_action( 'save_post', 'hcf_save' );

Hàm update_post_meta không có quá nhiều logic cho lắm. Hàm update_metadata thú vị hơn hàm này khá nhiều. WordPress hỗ trợ metadata cho các loại đối tượng khác nhau như post, comment, user mà mong muốn tiêu chuẩn khóa các API làm việc với metadata vào một nhóm các hàm được gọi là Metadata API. Tóm lại, Metadata API có thể được sử dụng để tạo metadata cho bất kỳ bảng nào miễn là bạn tạo một bảng có cấu trúc khớp với các bảng wp_commentmeta, wp_postmeta, wp_usermeta.

Quay trở lại với đường đi của dữ liệu, hàm update_metadata gọi rất nhiều hàm khác để lọc và tiêu chuẩn hóa các dữ liệu được nhập vào bởi người dùng. Rất nhiều các filter được sử dụng ở mỗi hàm này. Nên cách xử lý với các dữ liệu của bạn cũng sẽ khác nhau. Trong trường hợp gặp lỗi, bạn nên kiểm tra lại từ bước đầu tiên nhé.

Sau khi có dữ liệu sạch, WordPress sẽ cập nhật database. Để ưu tiên cho việc này, hàm update_metadata sẽ gọi một filter là update_{$meta_type}_metadata. Nếu kết quả trả về là true, hàm này sẽ không cập nhật dữ liệu nữa. Ngược lại, thì kết quả trả về sẽ là một giá trị của filter. Chắc là WordPress có ý định cho phép một số loại plugin làm khác đi so với logic về việc cập nhật dữ liệu, đặc biệt là khi khắc phục các lỗi liên quan đến việc lưu dữ liệu custom field.

Lời kết

Bài viết này dẫn chúng ta đi qua các nội dung về đường đi của dữ liệu của custom field để hiểu rõ hơn về việc điều sẽ xảy ra với chúng kể từ khi chúng còn đang ở form được người dùng điền vào trên trình duyệt web cho đến khi được lưu vào database. Thông qua đó, chúng ta cũng có thể hiểu thêm về ý nghĩa của từng đoạn code mà chúng ta sử dụng trong plugin và biết cách để cải thiện chúng. Ngoài ra, chúng ta cũng biết thêm được rằng các hook có thể thay đổi dữ liệu của custom fields nên chúng ta có thể dễ dàng khắc phục lỗi hơn.

Trong bài viết tới, mình sẽ nói kỹ hơn về cấu trúc dữ liệu trong metadata, nền tảng, điểm mạnh, điểm yếu và các giải pháp cho chúng nhé.

Để lại bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *