# Designing a class representing a WordPress admin page

A WordPress plugin can have a lot of different components. For example, some might need to use [shortcodes](https://codex.wordpress.org/shortcode) to achieve their purpose. While others might need to create [custom post types](https://codex.wordpress.org/custom_post_type). But one component that almost every plugin needs is an [admin page](https://codex.wordpress.org/Administration_Menus). (Or they might even need more than one!) That said, designing an admin page using object-oriented programming isn't that straightforward. There are a lot of different moving pieces that you have to take in consideration in your design. A well-designed admin page class will combine these different moving pieces into a cohesive class. The key to achieving this is to understand the role of these different moving pieces in the larger picture of an admin page. We'll do that by analyzing what makes up an admin page in the first place.

# Elements of an admin page

With this in mind, let's go ahead and look at the different elements of an admin page. All of them fall into two broad categories. These are registering the admin page and rendering it. ## Registering the admin page

We're going to start with registering an admin page. With WordPress, you register an admin page using the [`add_submenu_page`](https://developer.wordpress.org/reference/functions/add_submenu_page/) function. There are also shortcut functions such as [`add_options_page`](https://codex.wordpress.org/add_options_page) that let you add a submenu page to a specific WordPress menu. That said, behind the scenes, all these functions call the `add_submenu_page` function. The `add_submenu_page` function itself has a lot of parameters and it requires that you use all but one of them. If you're not using a shortcut function like the `add_options_page` function, you first need to give it a `parent_slug`. That's the slug of the parent menu that the admin page will show up on. You then need to give the function a `page_title` and a `menu_title`. The first is the `title` tag when you go to the admin page. The other is the title of the admin page will have in the parent menu that we defined in the first parameter. Next, you have to give the function a `capability`. That's the [WordPress capability](https://codex.wordpress.org/Roles_and_Capabilities#Capabilities) that the user must have to see the menu in the WordPress admin. That said, there are a lot of possible capabilities to choose from. This can be a bit overwhelming when you're trying to find the right one to use. In general, a lot of plugins use the [`manage_options`](https://codex.wordpress.org/Roles_and_Capabilities#manage_options) capability to only display an admin page to administrators. Outside that common scenario, it varies on the context of the admin page. For example, a plugin that deals with comments might want to use the [`moderate_comments`](https://codex.wordpress.org/Roles_and_Capabilities#moderate_comments). The last mandatory parameter is `menu_slug`. This is the slug that the admin page will use. You need this slug to be unique otherwise you'll run into weird display bugs. The final parameter is `function`. It's a [callable](https://secure.php.net/manual/en/language.types.callable.php) that WordPress will use to display the content of the admin page. The parameter is optional, but, in practice, you need to use it if you want to display your admin page. (And who doesn't want that!?) ## Rendering the admin page

The `function` parameter is a good way to bring up the most problematic element of an admin page. It's how to render the HTML of the admin page. There's no consensus among WordPress developers on the best way to do this. This means that there are almost as many ways to do this as there are WordPress developers! Ok, this might be a *slight* exaggeration. But it's true that WordPress developers use a lot of different methods for rending the HTML of an admin page. Some (albeit few!) use the [settings API](https://codex.wordpress.org/Settings_API). While others choose to render it all themselves using PHP. And some do something even more extreme and build a [single-page application](https://en.wikipedia.org/wiki/Single-page_application) for their admin page! There are advantages and disadvantages to each of these methods. (That's why everyone has a different way of doing it!) That said, we want to use as many WordPress systems as we can for this. That's why we'll use the settings API to do this. ### Generating HTML content

There's one big problem with the settings API. (And that's one of the reasons why it's so unpopular.) It's that it doesn't help generate the HTML of the admin page. We still have to find a way to do that. Lucky for us, we've covered [how to generate HTML](https://carlalexander.ca/designing-class-generate-wordpress-html-content/) with object-oriented programming before. Now, there's no need to read this article. (That's some pretty horrible self-promotion right?) It's just there for reference. This article is just going to reuse some of the ideas seen in that article. But we're not going to design a class to do generate HTML like we did in it. It'll keep things a bit simpler. That said, feel free to do that in your code though if you prefer! # Creating our admin page class

Alright, so we have a pretty good idea of what makes up an admin page now! The next step is to create a class that can handle these different elements of an admin page. We'll name it `MyPlugin_AdminPage`. ```
class MyPlugin_AdminPage
{
}
```

Our `MyPlugin_AdminPage` class is empty for now. But it won't stay that way for very long! Let's start adding things to it. ## Registering our admin page class and the plugin API

The first thing that we're going to look at is how to register our admin page class with WordPress. This will require that we use the `add_submenu_page` function that we saw earlier. That said, one thing that we didn't mention with the `add_submenu_page` function is that we can't call it whenever we want. No, we need to call it at a specific time using the [plugin API](https://carlalexander.ca/wordpress-adventurous-plugin-api/). If you're adding a normal admin page, you want to use the [`admin_menu`](https://codex.wordpress.org/Plugin_API/Action_Reference/admin_menu) hook. (This is what you'll do 99% of the time.) But if you're looking to add an admin page for the WordPress [network](https://codex.wordpress.org/Create_A_Network) admin, you want to use the [`network_admin_menu`](https://codex.wordpress.org/Plugin_API/Action_Reference/network_admin_menu) hook. This means that we'll need a way to use the plugin API with our `MyPlugin_AdminPage`. For the sake of keeping our design simple, we're not going to design our `MyPlugin_AdminPage` class so that it uses the plugin API. We'll just imagine that our plugin uses a function like this: ```
function myplugin_add_admin_page() {
    $admin_page = new MyPlugin_AdminPage();

    // Call add_submenu_page function
}
add_action('admin_menu', 'myplugin_add_admin_page');
```

That said, you might want to push the design of your `MyPlugin_AdminPage` class further and use the plugin API. That's great! There are quite a few [articles on the plugin API](https://carlalexander.ca/tag/plugin-api/) on this site to help you with that. These articles cover a wide range of options where some are [easier to implement](https://carlalexander.ca/designing-class-wordpress-hooks/) than [others](https://carlalexander.ca/designing-system-wordpress-event-management/). ## Passing arguments to the "add\_submenu\_page" function

There's a good chance that you noticed that the `myplugin_add_admin_page` function that we saw earlier didn't add an admin page. We just had a comment mentioning that we'd call the `add_submenu_page` function. Well, that comment was there so we could talk about it. (Quite the clever ruse on my part!) That's because we saw earlier that the `add_submenu_page` function uses a lot of parameters. We need a way for our `MyPlugin_AdminPage` class to pass arguments to it. There are a few ways to do this so let's take a moment to look at them! ### One method with all the arguments

One way we can pass arguments to the`add_submenu_page` function is by using the [`call_user_func_array`](https://secure.php.net/manual/en/function.call-user-func-array.php) function. This is a special PHP function that not a lot of PHP developers know about. It lets you call a specific function or method while passing it an array of arguments. The `call_user_func_array` has two parameters: `callback` and `param_arr`. It expects `callback` to be a [callable](https://secure.php.net/manual/en/language.types.callable.php). That callable will be the function or the method that the `call_user_func_array` function will call. Of course, we already know what function or method we want to call! It's the `add_submenu_page` function. So we'll just pass `add_submenu_page` as a string for the first argument of the `call_user_func_array` function. The second argument is `param_arr`. It's an array of arguments passed to the `call_user_func_array` function will pass to the `callback`. It's important to mention that the `call_user_func_array` function expects the `param_arr` array to be an indexed array. You can't use an associative array. #### Using the "call\_user\_func\_array" function

So that's all nice. But how do we make our `MyPlugin_AdminPage` class work with the `call_user_func_array` function? Well, take a look our updated `myplugin_add_admin_page` function below: ```
function myplugin_add_admin_page() {
    $admin_page = new MyPlugin_AdminPage();

    call_user_func_array('add_submenu_page', $admin_page->get_page_arguments());
}
add_action('admin_menu', 'myplugin_add_admin_page');
```

As you can see, we replaced our comment with the call to the `call_user_func_array` function. The first argument is a string with `add_submenu_page` as we said it would be. The second argument is the array returned by the `get_page_arguments` method of our `MyPlugin_AdminPage` class. The `get_page_arguments` method is how our `MyPlugin_AdminPage` class will communicate with the `add_submenu_page` function. The method returns the values that we want to pass to it as an array. And, as we saw earlier, the `call_user_func_array` function wants the array formatted a specific way. ```
class MyPlugin_AdminPage
{
    // ...

    /**
     * Get the arguments for the "add_submenu_page" function.
     *
     * @return array
     */
    public function get_page_arguments()
    {
        return array(
            'options-general.php',
            'My Plugin Admin Page',
            'My Plugin',
            'install_plugins',
            'myplugin',
            array($this, 'render_page'),
        );
    }
}
```

Above is our updated `MyPlugin_AdminPage` class with the `get_page_arguments` method. The method returns an array with all the arguments that we want to pass to the `add_submenu_page` function. Each of these array elements is a parameter that we covered earlier. - `options-general.php` is the `parent_slug` of the parent menu of our admin page.
- `My Plugin Admin Page` and `My Plugin` are the `page_title` and `menu_title` of our admin page respectively.
- `install_plugins` is the `capability` required to view our admin page.
- `myplugin` is the `menu_slug` of our admin page.
- `array($this, 'render_page')` is the `function` callback that WordPress will use to render our admin page.

These are all pretty straightforward except for the `function` callback. We're going to talk about rendering our admin page a bit later. But, for now, this is one way that we pass arguments for our `MyPlugin_AdminPage` class to the `add_submenu_page` function. ### One method for each argument

The other alternative is to do the complete opposite! Instead of using one method to pass all the arguments of the `add_submenu_page` function, we use one method for each argument. This makes our `MyPlugin_AdminPage` class a bit heavier, but you don't have to rely on a PHP function to call the `add_submenu_page` function. ```
function myplugin_add_admin_page() {
    $admin_page = new MyPlugin_AdminPage();

    add_submenu_page(
        $admin_page->get_parent_slug(),
        $admin_page->get_page_title(),
        $admin_page->get_menu_title(),
        $admin_page->get_capability(),
        $admin_page->get_slug(),
        array($admin_page, 'render_page')
    );
}
add_action('admin_menu', 'myplugin_add_admin_page');
```

As you can see, the updated `myplugin_add_admin_page` function above is a lot like our `get_arguments` method from earlier. But instead of returning an array with all our arguments, each argument gets its own method. The only exception is the `function` callback which stays the same. ```
class MyPlugin_AdminPage
{
    // ...

    /**
     * Get the capability required to view the admin page.
     *
     * @return string
     */
    public function get_capability()
    {
        return 'install_plugins';
    }

    /**
     * Get the title of the admin page in the WordPress admin menu.
     *
     * @return string
     */
    public function get_menu_title()
    {
        return 'My Plugin';
    }

    /**
     * Get the title of the admin page.
     *
     * @return string
     */
    public function get_page_title()
    {
        return 'My Plugin Admin Page';
    }

    /**
     * Get the parent slug of the admin page.
     *
     * @return string
     */
    public function get_parent_slug()
    {
        return 'options-general.php';
    }

    /**
     * Get the slug used by the admin page.
     *
     * @return string
     */
    public function get_slug()
    {
        return 'myplugin';
    }
}
```

And here's our `MyPlugin_AdminPage` class with all the methods used as arguments for the `add_submenu_page` function. All these methods are pretty straightforward. They all return a string from our earlier arguments array. ### Pick whichever you like

There's no best way to pass arguments to the `add_submenu_page` function. Both have their advantages and disadvantages. (This is often the case when designing something!) Using a single method is simpler because you only have one method as opposed to several. But having multiple methods is useful too. You might want to reuse some of these methods in your code. So, in the end, it comes down to a personal choice. But, for this article, we're going to use latter option. That's because we're going to reuse some of these methods in our code. ## Configuring our admin page

Now that we've seen how to register our admin page, we still have to configure it. This is a required step before we look at how we'll render it. As we mentioned earlier in the article, we're going to use the settings API to do this. The settings API is an umbrella for a bunch of different tasks. But there are only two that we're interested in for the configuration step. Let's take a look at them. ### Registering a setting

The first task that we need to do is register a setting with the API. You can do this is by using the [`register_setting`](https://codex.wordpress.org/Function_Reference/register_setting) function. You can see how we do that below: ```
class MyPlugin_AdminPage
{
    // ...

    /**
     * Configure the admin page using the Settings API.
     */
    public function configure()
    {
        // Register settings
        register_setting($this->get_slug(), 'myplugin_option');
    }

    // ...
}
```

You can see that first step in the `configure` method above. We used the `register_setting` function to register a setting with the API. The `register_setting` has two mandatory parameters: `option_group` and `option_name`. `option_group` is the settings group that the setting will belong to. We're going to use the string returned our `get_slug` method for that argument. (This is one reason why we're using individual methods for our call to the `add_submenu_page` function.) The `option_name` parameter is the actual setting that we want to register. It's the name of the option that we'll use in the admin page code. In the code above, we passed the `myplugin_option` string as the `option_name` argument. ### Registering sections and fields

The second task is registering sections and fields with the settings API. These sections and fields are the way to control how WordPress will display our settings. Let's update our `configure` method before discussing them further. ```
class MyPlugin_AdminPage
{
    // ...

    /**
     * Configure the admin page using the Settings API.
     */
    public function configure()
    {
        /// Register settings
        register_setting($this->get_slug(), 'myplugin_option');

        // Register section and field
        add_settings_section(
            $this->get_slug() . '-section',
            __('Section Title', 'myplugin'),
            array($this, 'render_section'),
            $this->get_slug()
        );
        add_settings_field(
            $this->get_slug() . '-option', 
            __('My option', 'myplugin'),
            array($this, 'render_option_field'), 
            $this->get_slug(), 
            $this->get_slug() . '-section'
        );
    }

    // ...
}
```

We added two new function calls in our `configure` method above. The first one is to the [`add_settings_section`](https://codex.wordpress.org/Function_Reference/add_settings_section) function. The second is the [`add_settings_field`](https://codex.wordpress.org/Function_Reference/add_settings_field) function. #### Adding a section

The `add_settings_section` function adds a new section to our admin page. It has four parameters: `id`, `title`, `callback` and `page`. `id` is the slug used to identify the section. `title` is the title of the section. `callback` is the callable used to output the content of the section. `page` is the slug of the admin page that the section belongs to. Now, let's talk about the arguments that we pass to the `add_settings_section` function. As you can see, we made more use of our `get_slug` method. We used it with the `id` and `page` arguments. Meanwhile, we passed a translated string for our `title`. (Always be translating!) The callback is an array referring to the `render_section` method. We'll look at that method a bit later when we look at rendering the admin page. #### Adding a field

The `add_settings_field` function has a similar parameters to the `add_settings_section` function. It has six parameters: `id`, `title`, `callback`, `page`, `section` and `args`. The first four ones are the same as the ones from the `add_settings_section` function. The two new ones are `section` and `args`. `section` is the slug of the section where our field will appear. `args` is an array of extra arguments that you can pass to the `add_settings_field` function. For this article, we're only interested in the `section` parameter. We passed it the same `id` value that we gave to the `add_settings_section` function. This was the value returned by our `get_slug` method concatenated with `-section`. Meanwhile, the first four arguments are almost identical to the ones from the `add_settings_section` function. The one that's worth pointing out is the argument passed as the `callback`. It's a reference to a new method called `render_option_field`. ## Rendering our admin page

Alright! So, at this point, we haven't talked about any of our rendering methods at all. But now that we've done all the work of registering and configuring our admin page, we can look at this last part of the puzzle. In case you hadn't been keeping track, our code had three undefined rendering methods in total. In order of appearance, they were the `render_page`, `render_section` and `render_option_field` methods. Their job is to generate the HTML for parts of our admin page. ### Rendering HTML using templates

This brings us to the most interesting decision that we have to take while designing our `MyPlugin_AdminPage` class. It's how are we going to render the HTML of our admin pages. This is really the design part where you have the most [creative liberties](https://carlalexander.ca/creativity-and-teaching-programming/). As we mentioned earlier though, we're going to use a solution that we explored in a [previous article](https://carlalexander.ca/designing-class-generate-wordpress-html-content/). (Linking it again in case you missed it!) That solution revolves around using PHP templates to generate our HTML content. This removes the [coupling](https://carlalexander.ca/coupling-cohesion-wordpress/) between our `MyPlugin_AdminPage` class and the HTML templating code. ```
class MyPlugin_AdminPage
{
    /**
     * Path to the admin page templates.
     *
     * @var string
     */
    private $template_path;

    /**
     * Constructor.
     *
     * @param string $template_path
     */
    public function __construct($template_path)
    {
        $this->template_path = rtrim($template_path, '/');
    }

    // ...
}
```

The first thing that we want to do to implement our HTML templating solution is to add a path to our admin page templates. We did this by adding `template_path` as a [private property](https://carlalexander.ca/first-thing-learn-object-oriented-programming/). We also added a [constructor](https://en.wikipedia.org/wiki/Constructor_(object-oriented_programming)) that has `template_path` as a parameter. The constructor assigns our `template_path` after passing it through [`rtrim`](https://secure.php.net/manual/en/function.rtrim.php) to ensure that it doesn't end with a slash. Next, we want to use this `template_path` property to render our PHP templates. We'll need to create a method to do that. You can see it below: ```
class MyPlugin_AdminPage
{
    // ...

    /**
     * Renders the given template if it's readable.
     *
     * @param string $template
     */
    private function render_template($template)
    {
        $template_path = $this->template_path . '/' . $template . '.php';

        if (!is_readable($template_path)) {
            return;
        }

        include $template_path;
    }
}
```

The `render_template` method above has one parameter: `template`. We use the `template` argument to generate `template_path`. This is the complete path to the PHP template file that we want to render. We then pass the `template_path` through a [guard clause](https://carlalexander.ca/mastering-php-conditionals/). In it, we use [`is_readable`](https://secure.php.net/manual/en/function.is-readable.php) to check if we can read the template file. If we can't read it, we return right away. Otherwise, we [include](https://secure.php.net/manual/en/function.include.php) it. ### Rendering the different parts of our admin page

Now that we have our `render_template` method, we can use it to render the different parts of our admin page. We render these parts using the `render_page`, `render_section` and `render_option_field` methods. These are the last three methods of our `MyPlugin_AdminPage` class that we've seen but not yet implemented. ```
class MyPlugin_AdminPage
{
    // ...

    /**
     * Renders the option field.
     */
    public function render_option_field()
    {
        $this->render_template('option_field');
    }

    /**
     * Render the plugin's admin page.
     */
    public function render_page()
    {
        $this->render_template('page');
    }

    /**
     * Render the top section of the plugin's admin page.
     */
    public function render_section()
    {
        $this->render_template('section');
    }

    // ...
}
```

Above is our `MyPlugin_AdminPage` class with these three methods added. As you can see, these methods don't have a lot of code. All that they do is call the `render_template` method that we created in the last section. Each method asks it to render a specific template. That's also where most of the magic happens. So that's what we'll look at next. #### Page template

The first and largest template that we need is the one rendered by the `render_page` method. This is the template that contains most of the admin page. Based on the convention in `render_template`, you need to name this template `page.php`. ```
<div class="wrap" id="myplugin-admin">
    <div id="icon-tools" class="icon32"><br></div>
    <h2><?php echo $this->get_page_title(); ?></h2>
    <?php if (!empty($_GET['updated'])) : ?>
        <div id="setting-error-settings_updated" class="updated settings-error notice is-dismissible">
            <p><strong><?php _e('Settings saved.') ?></strong></p><button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
        </div>
    <?php endif; ?>
    <form action="options.php" method="POST">
        <?php settings_fields($this->get_slug()); ?>
        <?php do_settings_sections($this->get_slug()); ?>
        <?php submit_button(__('Save')); ?>
    </form>
</div>
```

So here's the content of `page.php`. There's quite a bit going on, but most of it is just HTML markup. That said, there are a few things going on that are worth going over. First, you probably noticed a lot of use of `this` inside the template. That's because, as far as PHP knows, the template is still within the scope of our `MyPlugin_AdminPage` class. (Woah! Trippy!) That means that we can do the same things that we would do inside a regular class method. That's why we're using our `get_page_title` inside the `h2` tag. We also make use the `get_slug` method later with the settings API functions. But we'll cover those in a second. Next, we have an `if` statement that checks if the [`$_GET`](https://secure.php.net/manual/en/reserved.variables.get.php) [superglobal](https://secure.php.net/manual/en/language.variables.superglobals.php) has a value for the `updated` key. This is the parameter that WordPress passes as a [query string](https://en.wikipedia.org/wiki/Query_string) to tell you that it's updated the options on the page. If it's not empty, we display a message to say that we've updated the plugin's settings. Last, we have a form and inside are the settings API functions that we mentioned earlier. You must put these two functions inside the `form` tag. Otherwise, the HTML that they output won't work. The first one is the [`settings_fields`](https://codex.wordpress.org/Function_Reference/settings_fields) function. This is a function that outputs the [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce) and `option_page` hidden values for the admin page. Without them, WordPress won't process our submitted admin page form. The second function is the [`do_settings_sections`](https://codex.wordpress.org/Function_Reference/do_settings_sections). It renders all the HTML for the sections and fields that we registered earlier. The only HTML that it doesn't render is the submit button. That's why we use the [`submit_button`](https://codex.wordpress.org/Function_Reference/submit_button) function after. #### Section and option field templates

The other two templates don't share the same level of complexity as the `page.php` template. The `section.php` template will often just be nothing more than a few words. Something to go under your section header to describe the fields registered in that section. ```
<p><?php _e('A small description about this section of the admin page', 'myplugin'); ?></p>
```

The code sample above is all that there is to our `section.php` template. You shouldn't forget that you need to put your text inside a `p` tag. You also shouldn't forget to translate your text if your plugin supports internationalization. (Which it should!) ```
<input type="text" name="myplugin_option" value="<?php get_option('myplugin_option', ''); ?>" />
```

The `option_field.php` template is a bit more complicated. (But not too much!) It needs to render the `input` element with the value of the option. We use the [`get_option`](https://developer.wordpress.org/reference/functions/get_option/) function to get that value from the WordPress database. (If you don't want to use a WordPress always, [create a class](https://carlalexander.ca/designing-classes-wordpress-options-api/) to interact with WordPress options.) The name `myplugin_option` used in the `input` tag and the `get_option` function comes from earlier in the article. It's the name of the setting that we registered using the `register_setting` function. (This was a little while ago so you might have forgotten where it came from!) # A good starting point

And this wraps things up for our `MyPlugin_AdminPage` class! We had to go over a lot of different things to build it. But this is something that can happen when designing classes that are more complex. And one last thing. There's also no need for you to go through the article to piece all the code samples together. You can find the code for the entire `MyPlugin_AdminPage` class below: ```
class MyPlugin_AdminPage
{
    /**
     * Path to the admin page templates.
     *
     * @var string
     */
    private $template_path;

    /**
     * Constructor.
     *
     * @param string $template_path
     */
    public function __construct($template_path)
    {
        $this->template_path = rtrim($template_path, '/');
    }

    /**
     * Configure the admin page using the Settings API.
     */
    public function configure()
    {
        /// Register settings
        register_setting($this->get_slug(), 'myplugin_option');

        // Register section and field
        add_settings_section(
            $this->get_slug() . '-section',
            __('Section Title', 'myplugin'),
            array($this, 'render_section'),
            $this->get_slug()
        );
        add_settings_field(
            $this->get_slug() . '-api-status',
            __('My option', 'myplugin'),
            array($this, 'render_option_field'),
            $this->get_slug(),
            $this->get_slug() . '-section'
        );
    }

    /**
     * Get the capability required to view the admin page.
     *
     * @return string
     */
    public function get_capability()
    {
        return 'install_plugins';
    }

    /**
     * Get the title of the admin page in the WordPress admin menu.
     *
     * @return string
     */
    public function get_menu_title()
    {
        return 'My Plugin';
    }

    /**
     * Get the title of the admin page.
     *
     * @return string
     */
    public function get_page_title()
    {
        return 'My Plugin Admin Page';
    }

    /**
     * Get the parent slug of the admin page.
     *
     * @return string
     */
    public function get_parent_slug()
    {
        return 'options-general.php';
    }

    /**
     * Get the slug used by the admin page.
     *
     * @return string
     */
    public function get_slug()
    {
        return 'myplugin';
    }

    /**
     * Renders the option field.
     */
    public function render_option_field()
    {
        $this->render_template('option_field');
    }

    /**
     * Render the plugin's admin page.
     */
    public function render_page()
    {
        $this->render_template('page');
    }

    /**
     * Render the top section of the plugin's admin page.
     */
    public function render_section()
    {
        $this->render_template('section');
    }

    /**
     * Renders the given template if it's readable.
     *
     * @param string $template
     */
    private function render_template($template)
    {
        $template_path = $this->template_path . '/' . $template . '.php';

        if (!is_readable($template_path)) {
            return;
        }

        include $template_path;
    }
}
```

We also saw early in the article that we needed a way to register our admin page. We had to use the `add_submenu_page` function to do it. Below is the way that we're doing it with the `MyPlugin_AdminPage` that we designed: ```
function myplugin_add_admin_page() {
    $admin_page = new MyPlugin_AdminPage();

    add_submenu_page(
        $admin_page->get_parent_slug(),
        $admin_page->get_page_title(),
        $admin_page->get_menu_title(),
        $admin_page->get_capability(),
        $admin_page->get_slug(),
        array($admin_page, 'render_page')
    );
}
add_action('admin_menu', 'myplugin_add_admin_page');
```

This should give you all the building blocks that you need to build admin page classes of your own. All that's left is for you to implement it in your own project. Feel free to leave any specific implementation questions you might have down in the comments!