Hướng dẫn dùng class-wp-list-table.php trong PHP

WP_List_Table là class cho phép bạn tạo bất kỳ một bảng quản lý các thành phần nào trong admin. Như bạn thấy danh sách Posts, Pages hoặc Users trong WordPress đều kế thừa từ class WP_List_Table. Nếu trong giao diện hoặc plugin của bạn có các dữ liệu theo kiểu danh sách, và bạn muốn tạo trang quản lý danh sách này trong phần options thì bài viết này có thể sẽ giúp ích được cho bạn.

Tạo class kế thừa WP_List_Table

Để hiển thị bất kỳ danh sách nào thì bạn tạo 1 class kế thừa WP_List_Table.

<?php
if ( ! class_exists( 'WP_List_Table' ) ) {
	require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

class HPDL_API_Key_List_Table extends WP_List_Table {

	public $delete_posts;
	public $date_format;
	public $time_format;

	function __construct() {
		global $status, $page;
		$this->date_format = get_option( 'date_format' );
		$this->time_format = get_option( 'time_format' );
		parent::__construct( array(
			'singular' => __( 'api_key', 'hocwp-pdl' ),
			'plural'   => __( 'api_keys', 'hocwp-pdl' ),
			'ajax'     => false
		) );
		add_action( 'admin_head', array( &$this, 'admin_header' ) );
	}

	function admin_header() {
		global $plugin_page;
		if ( 'hocwp_pdl' != $plugin_page ) {
			return;
		}
	}

	function no_items() {
		_e( 'No API Key found, dude.', 'hocwp-pdl' );
	}

	function column_default( $item, $column_name ) {
		switch ( $column_name ) {
			case 'id':
				return $item->ID;
			case 'date':
				$date = date( "$this->date_format $this->time_format", strtotime( $item->post_date ) );

				return $date;
			case 'domain':
			case 'key':
				return get_post_meta( $item->ID, $column_name, true );
			default:
				return print_r( $item, true );
		}
	}

	function get_sortable_columns() {
		$sortable_columns = array(
			'id'     => array( 'id', false ),
			'domain' => array( 'domain', false ),
			'key'    => array( 'key', false ),
			'date'   => array( 'date', false )
		);

		return $sortable_columns;
	}

	function get_columns() {
		$columns = array(
			'cb'     => '<input type="checkbox" />',
			'id'     => __( 'ID', 'hocwp-pdl' ),
			'domain' => __( 'Domain', 'hocwp-pdl' ),
			'key'    => __( 'Key', 'hocwp-pdl' ),
			'date'   => __( 'Date', 'hocwp-pdl' )
		);

		return $columns;
	}

	function column_id( $item ) {
		global $plugin_page;
		$tab     = 'api_keys';
		$edit    = admin_url( 'post.php?post=' . $item->ID . '&action=edit' );
		$actions = array(
			'edit'   => sprintf( '<a href="%s">Edit</a>', $edit ),
			'delete' => sprintf( '<a href="?page=%s&action=%s&api_key=%s&tab=%s">Delete</a>', $plugin_page, 'delete', $item->ID, $tab ),
		);

		return sprintf( '%1$s %2$s', $item->ID, $this->row_actions( $actions ) );
	}

	function get_bulk_actions() {
		$actions = array(
			'delete' => __( 'Delete', 'hocwp-pdl' )
		);

		return $actions;
	}

	function column_cb( $item ) {
		return sprintf(
			'<input type="checkbox" name="api_keys[]" value="%s" />', $item->ID
		);
	}

	function prepare_items() {
		$columns               = $this->get_columns();
		$hidden                = array();
		$sortable              = $this->get_sortable_columns();
		$this->_column_headers = array( $columns, $hidden, $sortable );
		$per_page              = get_option( 'posts_per_page' );
		if ( ! is_numeric( $per_page ) ) {
			$per_page = 10;
		}
		$current_page = $this->get_pagenum();
		$by           = ( ! empty( $_GET['orderby'] ) ) ? $_GET['orderby'] : 'ID';
		if ( 'id' == $by ) {
			$orderby = 'ID';
		} elseif ( 'date' == $by ) {
			$orderby = 'date';
		} else {
			$orderby = 'meta_value';
		}
		$order = ( ! empty( $_GET['order'] ) ) ? $_GET['order'] : 'asc';
		$args  = array(
			'post_type'      => 'hpdl_api_key',
			'posts_per_page' => $per_page,
			'paged'          => $current_page,
			'orderby'        => $orderby,
			'order'          => $order
		);
		$s     = isset( $_POST['s'] ) ? $_POST['s'] : '';
		if ( ! empty( $s ) ) {
			$args['meta_query'] = array(
				'relation' => 'or',
				array(
					'key'     => 'domain',
					'value'   => $s,
					'compare' => 'like'
				),
				array(
					'key'     => 'key',
					'value'   => $s,
					'compare' => 'like'
				)
			);
		}
		switch ( $by ) {
			case 'key':
			case 'domain':
				$args['meta_key'] = $by;
				break;
		}
		$query = new WP_Query( $args );
		if ( ! $query->have_posts() && is_numeric( $s ) ) {
			$args['post__in'] = array( $s );
			unset( $args['meta_query'] );
			$query = new WP_Query( $args );
		}
		$total_items = $query->found_posts;
		$items       = $query->posts;
		$this->set_pagination_args( array(
			'total_items' => $total_items,
			'per_page'    => $per_page
		) );
		$this->found_data = $items;
		$this->items      = $items;
	}

	public function process_bulk_action() {
		if ( isset( $_POST['_wpnonce'] ) && ! empty( $_POST['_wpnonce'] ) ) {
			$nonce  = filter_input( INPUT_POST, '_wpnonce', FILTER_SANITIZE_STRING );
			$action = 'bulk-' . $this->_args['plural'];
			if ( ! wp_verify_nonce( $nonce, $action ) ) {
				wp_die( 'Nope! Security check failed!', 'hocwp-pdl' );
			}
		}
		$action = $this->current_action();

		switch ( $action ) {
			case 'delete':
				$api_key = isset( $_GET['api_key'] ) ? $_GET['api_key'] : '';
				$key     = get_post( $api_key );
				if ( $key instanceof WP_Post && 'hpdl_api_key' == $key->post_type ) {
					$deleted = wp_delete_post( $key->ID, true );
					if ( false != $deleted ) {
						?>
						<div class="notice notice-success is-dismissible">
							<p>
								<strong><?php _e( 'Info:', 'hocwp-pdl' ); ?></strong>&nbsp;<?php printf( __( '%s API Key has been deleted.', 'hocwp-pdl' ), 1 ); ?>
							</p>
						</div>
						<?php
					}
				} else {
					$api_keys = isset( $_POST['api_keys'] ) ? $_POST['api_keys'] : '';
					if ( is_array( $api_keys ) ) {
						$count = 0;
						foreach ( $api_keys as $api_key ) {
							$key = get_post( $api_key );
							if ( $key instanceof WP_Post && 'hpdl_api_key' == $key->post_type ) {
								$deleted = wp_delete_post( $key->ID, true );
								if ( false != $deleted ) {
									$count ++;
								}
							}
						}
						?>
						<div class="notice notice-success is-dismissible">
							<p>
								<strong><?php _e( 'Info:', 'hocwp-pdl' ); ?></strong>&nbsp;<?php printf( __( '%s API Key deleted.', 'hocwp-pdl' ), $count ); ?>
							</p>
						</div>
						<?php
					}
				}
				break;
		}
	}
}

Đoạn code bên trên mình tạo 1 custom post type là hpdl_api_key. Sau đó, mình muốn hiển thị danh sách các API Key trên trang options thay vì quản lý như post bình thường. Đoạn code tạo custom post type mình có như sau:

$labels = array(
	'name'               => _x( 'API Keys', 'post type general name', 'hocwp-pdl' ),
	'singular_name'      => _x( 'API Key', 'post type singular name', 'hocwp-pdl' ),
	'menu_name'          => _x( 'API Keys', 'admin menu', 'hocwp-pdl' ),
	'name_admin_bar'     => _x( 'API Key', 'add new on admin bar', 'hocwp-pdl' ),
	'add_new'            => _x( 'Add New', 'api key', 'hocwp-pdl' ),
	'add_new_item'       => __( 'Add New API Key', 'hocwp-pdl' ),
	'new_item'           => __( 'New API Key', 'hocwp-pdl' ),
	'edit_item'          => __( 'Edit API Key', 'hocwp-pdl' ),
	'view_item'          => __( 'View API Key', 'hocwp-pdl' ),
	'all_items'          => __( 'All API Keys', 'hocwp-pdl' ),
	'search_items'       => __( 'Search API Keys', 'hocwp-pdl' ),
	'parent_item_colon'  => __( 'Parent API Keys:', 'hocwp-pdl' ),
	'not_found'          => __( 'No API Key found.', 'hocwp-pdl' ),
	'not_found_in_trash' => __( 'No API Key found in Trash.', 'hocwp-pdl' )
);
$args   = array(
	'labels'              => $labels,
	'show_in_nav_menus'   => false,
	'show_in_menu'        => true,
	'show_in_admin_bar'   => false,
	'show_ui'             => true,
	'capabilities'        => array(
		'edit_post'          => 'manage_options',
		'read_post'          => 'manage_options',
		'delete_post'        => 'manage_options',
		'edit_posts'         => 'manage_options',
		'edit_others_posts'  => 'manage_options',
		'publish_posts'      => 'manage_options',
		'read_private_posts' => 'manage_options',
		'create_posts'       => 'manage_options',
		'delete_posts'       => 'manage_options'
	),
	'supports'            => array( 'revisions' ),
	'rewrite'             => false,
	'exclude_from_search' => true,
	'publicly_queryable'  => false
);
register_post_type( 'hpdl_api_key', $args );

Chú ý là bạn phải để đoạn code trên vào trong action init của WordPress nhé.

Hiển thị danh sách trong list table

Để hiển thị danh sách này thì mình tạo 1 action admin_notices như bên dưới:

function hocwp_pdl_admin_notices_action() {
	global $pagenow, $plugin_page;
	if ( 'hocwp_pdl' == $plugin_page ) {
		global $hocwp_pdl;
		$hocwp_pdl->list_api_keys_table->process_bulk_action();
	}
}

add_action( 'admin_notices', 'hocwp_pdl_admin_notices_action' );

Bạn thay hocwp_pdl thành slug trang option mà bạn đang viết. Cuối cùng là trong hàm callback hiển thị HTML của trang option bạn để như sau:

<div style="padding-top: 20px;">
	<a href="<?php echo admin_url( 'edit.php?post_type=hpdl_api_key' ); ?>"
	   class="page-title-action button-primary" style="display: none"><?php _e( 'View Full Lists', 'hocwp-pdl' ); ?></a>
	<a href="<?php echo admin_url( 'post-new.php?post_type=hpdl_api_key' ); ?>"
	   class="page-title-action"><?php _e( 'Add New', 'hocwp-pdl' ); ?></a>
	<?php
	global $hocwp_pdl, $plugin_page;
	$hocwp_pdl->list_api_keys_table->prepare_items();
	?>
	<form method="post">
		<input type="hidden" name="page" value="<?php echo $plugin_page; ?>">
		<?php
		$hocwp_pdl->list_api_keys_table->search_box( __( 'Search Key', 'hocwp-pdl' ), 'search_id' );
		$hocwp_pdl->list_api_keys_table->display();
		?>
	</form>
</div>

Như vậy là bạn đã có thể hiển thị danh sách bất kỳ theo kiểu quản lý danh sách bài viết hoặc tài khoản. Bài viết chỉ mang tính chất tham khảo, các bạn không nên sao chép mà chỉ nên áp dụng có chọn lọc. Chúc bạn thành công.