How to create a table in WordPress admin panel. Part 2.1.

How to create a table in WordPress admin panel. Part 2.1.

Hello, dear readers and guests of the blog. Yes, yes, yes. I'm lazy and haven’t written in a while. It’s easy to quit, but much harder to start writing again. But I really hope that this will change soon. I’ve gained quite a bit of interesting knowledge in WordPress — created a couple of plugins, completed several backend projects. As for frontend — still the same old story.

Now, to the point. In this article, we’ll extend the capabilities of our class — adding a search form, styles, checkboxes, and bulk actions on records. So, let’s get started.

You can find the first article in this series by following this link. Since some time has passed, I’ve changed the names of some classes and methods, but the purpose and tasks remain the same. I believe a bit of confusion will be good for you — it’ll make you think a little more carefully.

Base plugin class “Plance_INIT_Lessons2”

The first thing we need to do is update the constructor of the “Plance_INIT_Lessons2” class (note that the class name was different in the first article). Now, our constructor looks like this:

public function __construct()
{
	add_action('admin_menu', array($this, 'createMenu'));
	add_filter('set-screen-option', array($this, 'setScreenOption'), 10, 3);
	add_action('admin_head', array($this, 'adminHead'));
	add_action('plugins_loaded', array($this, 'loadTextdomain'));
}

 

Where:
We add the admin menu (there are some minor changes)

add_action('admin_menu', array($this, 'createMenu'));

We add a filter to detect which screen was triggered, so we know whether to save options or not (more on that below)

add_filter('set-screen-option', array($this, 'setScreenOption'), 10, 3);

Here we add styles for our plugin

add_action('admin_head', array($this, 'adminHead'));

And to load the translation file, we use:

add_action('plugins_loaded', array($this, 'loadTextdomain'));

Turns out, it’s not strictly necessary to do it here, but for the sake of structure, we’ll leave it as is.

Now let’s move on to our menu. We need to replace the existing method with the following:

public function createMenu()
{
	$hook = add_menu_page(
		'Table', 
		'Table', 
		'manage_options', 
		__CLASS__, 
		array($this, 'createTable')
	);

	add_action('load-'.$hook, array($this, 'showScreenOptions'));
}

In this case, we’re particularly interested in the hook at the end of the method. By using it, we can create code specific to this page. I call it the "action" method — similar to MVC programming. Why? You’ll find out in future articles. Meanwhile, `array($this, 'createTable')` is the “view” method — it’s responsible for rendering. But more on that later.

Next, we move to the `showScreenOptions` method, which contains the following code:

add_screen_option('per_page', array(
	'label' => __('Records', 'plance'),
	'default' => 10,
	'option' => 'plance_per_page'
));

Here, we configure pagination for the page — how many records to show per page in the table. You can adjust this option in the interface by clicking “Screen Options” at the top of the screen.

We can also configure the Help tabs for our plugin:

get_current_screen() -> add_help_tab(array(
	'id'		=> 'plance-tab-1',
	'title'		=> __('Tab 1', 'plance'),
	'content'	=> ''.__('Content 1', 'plance').'',
));

These will appear when clicking the “Help” button at the top of the screen.

At the end of the method, we initialize our table:

$this -> _Table = new Plance_Table_Lessons2();

Now take a look at the controller, and pay attention to the `adminHead` method — it defines styles for our plugin:

public function adminHead()
{
	if(__CLASS__ == (isset($_GET['page']) ? esc_attr($_GET['page']) : ''))
	{
		echo '<style type="text/css">';
		echo '.wp-list-table .column-cb { width: 4%; }';
		echo '.wp-list-table .column-ex_id { width: 5%; }';
		echo '.wp-list-table .column-ex_title { width: 30%; }';
		echo '.wp-list-table .column-ex_author { width: 15%; }';
		echo '.wp-list-table .column-ex_description { width: 36%; }';
		echo '.wp-list-table .column-ex_price { width: 10%; }';
		echo '</style>';
	}
}

There’s really not much to describe here 🙂 Just basic CSS styling.

There’s a small check at the beginning — the page name must match the global `$_GET['page']` variable. Why? Because our plugin page uses the same name.

As for localization — it’s very straightforward, so I won’t even describe it. Just look at the `loadTextdomain` method, and you’ll understand. If you still have questions — feel free to leave a comment.

Remember earlier I mentioned that `array($this, 'createTable')` is what I call a “view” method? I call it that because I typically implement display logic there. Meanwhile, the “action” method handles processing, validation, and model-view interaction (but more on that in future posts).

Let’s now look at the `createTable` method. It contains the following:

public function createTable()
{
	$this -> _Table -> prepare_items();
	?>

<div class="wrap">

<h2><?php echo __('Example List Table', 'plance')?></h2>


<form method="get">
				<input type="hidden" name="page" value="<?php echo __CLASS__ ?>" />
				<?php $this -> _Table -> search_box('search', 'search_id'); ?>
				<?php $this -> _Table -> display(); ?>
			</form>

		</div>

	<?php
}
?>

Где:
Подготовка данных таблицы для отображения

<?php
$this -> _Table -> prepare_items();

Since the form is submitted via GET, we need to specify the `page` parameter in a hidden field:

<input type="hidden" name="page" value="<?php echo __CLASS__ ?>" />

Here, we render the search form and the actual table:

<?php $this -> _Table -> search_box('search', 'search_id'); ?>
<?php $this -> _Table -> display(); ?>

Where:
`search` — the name of the search button
`search_id` — the identifier for the button (in the method, it's combined with another string)

Posts on similar topics

Are you having problems with your WordPress site? Do you need additional functionality? A custom plugin or a new page?
Then write to me via the feedback form, and I will try to help you.

Write a comment

Your email address will not be published. Required fields are marked *