Tạo MetaBox đính nhiều hình ảnh trong WordPress

Tạo MetaBox đính nhiều hình ảnh trong WordPress

Tiêu chí của mình khi lập ra mục WordPress là cố gắn hướng dẫn bạn cách tạo theme WordPress hạn chế sử dụng plugin bên ngoài đến mức tối đa có thể, dĩ nhiên cũng sẽ không thể tránh khỏi việc sử dụng Plugin nhưng nếu bạn hạn chế sử dụng plugin miễn phí thì sẽ tránh được nhiều rủ ro cho website của bạn. Hơn nữa việc cần một chức năng cụ thể và viết ra chức năng đó sẽ tối ưu hơn rất nhiều so với việc bê nguyên một plugin có nhiều tính năng dư thừa vào. Bài viết này mình sẽ chia sẻ với bạn cách tạo ra một metabox đích kèm được nhiều hình ảnh trong bài viết, việc này giúp bạn có thể tùy biến được theme theo sở thích như có thể tạo ra các slide, hay hình ảnh công ty,….

Tạo jQuery và CSS cho metabox

Để tạo ra tính năng upload nhiều hình ảnh bạn cần phải sử dụng jQuery, jQuery sẽ đưa ra những sự kiện click để lấy tạo ra các nút thêm hình ảnh, thay đổi hình ảnh, xóa hình ảnh, săp xếp hình ảnh,….

Bây giờ mình tạo ra một tệp file jQuery và đặt tên là gallery-metabox.js sau đó thêm lần lượt các đoạn sau:

Tạo hàm jQuery

Hàm này sẽ chứa tất cả các sự kiện bên dưới.

jQuery(function($) {
   // viết tất cả sự kiện vào đây
}

Tạo sự kiện thêm hình ảnh

Mình sẽ tạo một sự kiện cho nút thêm hình ảnh. gallery-metabox là id của hộp metabox, a.gallery-add là nút thêm hình ảnh.

var file_frame;
$(document).on('click', '#gallery-metabox a.gallery-add', function(e) {
   e.preventDefault();
   if (file_frame) file_frame.close();
   file_frame = wp.media.frames.file_frame = wp.media({
      title: $(this).data('uploader-title'),
      button: {
      text: $(this).data('uploader-button-text'),
   },
   multiple: true
 });

file_frame.on('select', function() {
   var listIndex = $('#gallery-metabox-list li').index($('#gallery-metabox-list li:last')),
   selection = file_frame.state().get('selection');

selection.map(function(attachment, i) {
   attachment = attachment.toJSON(),
   index = listIndex + (i + 1);
$('#gallery-metabox-list').append('<li><input type="hidden" name="tdc_gallery_id[' + index + ']" value="' + attachment.id + '"><img class="image-preview" src="' + attachment.sizes.thumbnail.url + '"><a class="change-image button button-small" href="#" data-uploader-title="Change image" data-uploader-button-text="Change image">Đổi hình</a><br><small><a class="remove-image" href="#">Remove image</a></small></li>');
 });
 });
 fnSortable();
 file_frame.open();

});

Đoạn trên nếu giải thích chi tiết thì rất dài dòng nên mình sẽ bỏ qua, Có hàm fnSortable() là hàm để sắp xếp lại thứ tự của các hình ảnh khi có sự thay đổi. Mình sẽ đưa ra hàm này ở cuối cùng, nên tạm thời bạn có thể không cần quan tâm.

Tạo tự kiện thay đổi hình ảnh

Sự kiện này dành cho nút thay đổi hình ảnh, thao tác này giúp bạn thay đổi hình ảnh mà không cần phải xóa đi, thêm lại và kéo về vị trí cũ. Bạn thêm đoạn này vào trong hàm jQuery.

$(document).on('click', '#gallery-metabox a.change-image', function(e) {
  e.preventDefault();
  var that = $(this);
  if (file_frame) file_frame.close();
  file_frame = wp.media.frames.file_frame = wp.media({
     title: $(this).data('uploader-title'),
     button: {
     text: $(this).data('uploader-button-text'),
  },
  multiple: false
  });

  file_frame.on( 'select', function() {
     attachment = file_frame.state().get('selection').first().toJSON();
     that.parent().find('input:hidden').attr('value', attachment.id);
     that.parent().find('img.image-preview').attr('src', attachment.sizes.thumbnail.url);
  });
  file_frame.open();
});

Tương tự như sự kiện trên, nhưng ở đây bạn không cần gọi lại hàm sắp xếp vì thứ tự là không thay đổi.

Tạo sự kiện xóa hình ảnh

Sự kiện này dành cho nút xóa hình ảnh. Trong sự kiện này có gọi đến hàm resetIndex() đây là một hàm thiết đặt lại số chỉ số Index mà trong hàm fnSortable() cũng sử dụng.

$(document).on('click', '#gallery-metabox a.remove-image', function(e) {
   e.preventDefault();
   $(this).parents('li').animate({ opacity: 0 }, 200, function() {
     $(this).remove();
      resetIndex();
    });
 });

Tạo hàm sắp xếp lại bảng và chỉ số index

Đây là hai hàm đã sử dụng nhiều ở trên, chúng có nhiệm vụ là sắp xếp lại các bảng khi có sự thay đổi như xóa hoặc thêm, đồng thời nó cũng thiết đặt lại chỉ số index.

Hàm thiết đặt lại chỉ số index

function resetIndex() {
    $('#gallery-metabox-list li').each(function(i) {
       $(this).find('input:hidden').attr('name', 'tdc_gallery_id[' + i + ']');
    });
}

Hàm sắp xếp lại bảng

function fnSortable() {
    $('#gallery-metabox-list').sortable({
       opacity: 0.6,
       stop: function() {
          resetIndex();
       }
    });
}

Hai hàm trên mình cũng sẽ không giải thích gì thêm nhé, nó chỉ làm việc sắp sếp và reset lại chỉ số index mà thôi.

Thêm css cho metabox

Mình sẽ tạo thêm một file css, đặt tên là gallery-metabox.css và thêm một đoạn css nhỏ như sau:

#gallery-metabox-list li {
   float: left;
   width: 150px;
   text-align: center;
   margin: 10px 10px 10px 0;
   cursor: move;
}

Đoạn này chỉ làm đẹp hơn một tí, đưa các hình ảnh nằm trên hàng thay vì mỗi hình một hàng, và thay đổi con chuột để tạo biểu tượng di chuyển.

Như vậy là mình đã có 2 file, bây giờ bạn hãy tải 2 tệp này vào theme của bạn, mình sẽ để file js trong mục js và css trong mục css nhé.

Tạo metabox nhiều hình ảnh trong WordPress

Bây giờ mình sẽ tiến hành tạo metabox sử dụng file jQuery và CSS đã tạo. Thông thường người ta sẽ viết Plugin nhưng mình thường viết thẳng trong file functions.php. Bây giờ hãy mở file functuons và làm theo các bước sau.

Trước tiên mình sẽ tạo ra một hàm để móc hai file này vào hook admin_enqueue_scripts

 function gallery_enqueue_hook($hook) {
   if ( 'post.php' == $hook || 'post-new.php' == $hook ) {
     wp_enqueue_script('gallery-metabox', get_template_directory_uri() . '/js/gallery-metabox.js', array('jquery', 'jquery-ui-sortable'));
     wp_enqueue_style('gallery-metabox', get_template_directory_uri() . '/css/gallery-metabox.css');
   }
 }
 add_action('admin_enqueue_scripts', 'gallery_enqueue_hook');

Tiếp theo là tạo hàm tạo metabox. Mình chỉ tạo metabox cho loại post, bạn muốn tạo cho page hoặc custom post type thì thêm vào nhé.

function add_gallery_metabox($post_type) {
  $types = array('post');
  if (in_array($post_type, $types)) {
  add_meta_box(
     'gallery-metabox',
     'Thêm nhiều hình ảnh',
     'gallery_meta_callback',
     $post_type,
     'normal',
     'high'
     );
   }  
 }
 add_action('add_meta_boxes', 'add_gallery_metabox');

Hàm này mình cũng sẽ không nói nhiều nữa, tất cả đã có trong bài Hướng dẫn tạo Meta Box trong WordPress rồi, bạn chịu khó xem lại nhé.

Với hàm tạo metabox thì mình còn phải tạo một hàm nữa có tên gallery_meta_callback(), hàm này sẽ tạo ra form ở trên metabox, bạn chú ý hàm này kỹ nhé, mình sẽ giải thích kỹ ở hàm này.

function gallery_meta_callback($post) {
   wp_nonce_field( basename(__FILE__), 'gallery_meta_nonce' );
   $ids = get_post_meta($post->ID, 'tdc_gallery_id', true);
?>
 <table class="form-table">
   <tr><td>
      <a class="gallery-add button" href="#" data-uploader-title="Thêm hình ảnh" data-uploader-button-text="Thêm nhiều hình ảnh">Thêm nhiều hình ảnh</a>
      <ul id="gallery-metabox-list">
        <?php if ($ids) : foreach ($ids as $key => $value) : $image = wp_get_attachment_image_src($value); ?>
        <li>
           <input type="hidden" name="tdc_gallery_id[<?php echo $key; ?>]" value="<?php echo $value; ?>">
           <img class="image-preview" src="<?php echo $image[0]; ?>">
           <a class="change-image button button-small" href="#" data-uploader-title="Đổi hình khác" data-uploader-button-text="Đổi hình khác">Đổi hình khác</a><br>
           <small><a class="remove-image" href="#">Xóa hình</a></small>
        </li>
        <?php endforeach; endif; ?>
     </ul>
  </td></tr>
 </table>
 <?php }

Đầu tiên bạn cần gọi làm wp_nonce_field( basename(__FILE__), 'gallery_meta_nonce' ); để đưa vào những nonce bảo mật. Mình sẽ tạo ra một biến tên $ids để lấy tất cả id của metabox của bài viết cụ thể, mục đích là để tải lên dữ liệu metabox có sẵn và hiển thị lên lại các control. Tiếp theo là mình sẽ design ra một table chứa toàn bộ control của metabox. Do các control trong form phụ thuộc nhiều vào JQuery nên chú ý đến các class và id của chúng nhé. Bên trong mình sẽ sử dụng hàm lặp để lấy ra các item được lưu, kèm theo đõ là hiển thị các nút như thay đổi, xóa, ….

Đến đây thì cơ bản bạn đã hoàn thành, nhưng còn một hàm rất quan trọng nữa đó là hàm lưu dữ liệu.

function gallery_meta_save($post_id) {
  if (!isset($_POST['gallery_meta_nonce']) || !wp_verify_nonce($_POST['gallery_meta_nonce'], basename(__FILE__))) return;
  if (!current_user_can('edit_post', $post_id)) return;
  if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;

  if(isset($_POST['tdc_gallery_id'])) {
    update_post_meta($post_id, 'tdc_gallery_id', $_POST['tdc_gallery_id']);
  } else {
    delete_post_meta($post_id, 'tdc_gallery_id');
  }
 }
add_action('save_post', 'gallery_meta_save');

Trong hàm này mình có kiểm tra các nonce và quyền hạn user trước khi cho phép lưu dữ liệu. để lưu dữ liệu mình sử dụng hàm update_post_meta() và xóa khi không có hình nào được gửi là delete_post_meta().

Đây là kết quả sau khi hoàn thành.

Kết quả tạo MetaBox đính nhiều hình ảnh trong WordPress

Kết quả tạo MetaBox đính nhiều hình ảnh trong WordPress

Hiển thị hình ảnh metabox lên post

Cách lấy dữ liệu và hiển thị thì mình đã có nói đến trong bài tạo metabox rồi. Tuy nhiên ở đây mình sẽ nói lại một tí vì đối với loại meta này thì mình sẽ cần dùng hàm lặp để lấy ra.

Đầu tiên là lấy tất cả các meta theo id: $images = get_post_meta($post->ID, 'tdc_gallery_id', true);

Sau đó là sử dụng hàm lặp để hiển thị từng hình.

foreach ($images as $image) {
  echo wp_get_attachment_url($image, 'large');
  echo wp_get_attachment_image($image, 'large');
}

Sử dụng wp_get_attachment_url() để lấy url hình ảnh. và wp_get_attachment_image() để lấy ra thẻ hình ảnh kèm theo link hình.

Như vậy là mình đã hoàn thành được một metabox cho phép đăng nhiều hình ảnh.

Chúc bạn thành công !

Các bài liên quan

Tuấn ĐC

Tuấn ĐC

Tôi thích tự do, làm những gì mình thích, thích đi nhiều nơi và học được nhiều thứ.

Liên hết bài viết: ""