Assuming you’ve read the following two posts (on the single responsibility principle and WordPress submenus), then this is a good place to tear apart the code and start trying to apply the principle.

Single Responsibility: One Reason to Change

If on the other hand, you haven’t read the posts, there are two things to note. First, the single responsibility principle says the following:

A class should have only one reason to change.

Secondly, review the code in this gist as this is the basis for the rest of the content of this article. Specifically, I’m going to be taking the above principle and try to apply it to the code.

Single Responsibility Principle: Options Pages

Anyone who has worked with the WordPress API to add a submenu item to the existing WordPress menu likely knows that for each submenu item, there is an associated options page.

In fact, the API function requires a function to render said options page as one of its parameters. The description of the last parameter says:

The function to be called to output the content for this page.

And this is where I the single responsibility principle, WordPress submenu items, and WordPress options pages come into play.

If a class should have only one reason to change, then it’s reasonable to look at the existing code and ask: “What is the reason (or what are the reasons) this class may change?” And looking at the code as it stands, I can see at least two reasons:

  1. The submenu item text or attributes need altering,
  2. The display of the options page needs to be modified.

Already, we have two reasons this class may change. This shows that there’s potential for separating this class into two classes. Given one reason the class may change is that of the submenu item and the other reason is that the options page needs to be modified, the code can be separated such that we have a class for each.

Single Responsibility Principle in WordPress: add_options_page

That is, we can create a class for the submenu and a class for the for the options page. There are a few ways to do this, but the approach I’ve been taking (which is always likely to change) has been to do the following:

  1. Create a class for the Submenu,
  2. Create a class for the Options Page,
  3. Add a property to the submenu so that it’s aware of the options page (since it has to invoke the function for rendering it),
  4. Add a property to the submenu page such as the array of options it’s responsible for rendering, saving, deleting, and so on.

With that said, the submenu may look something like this:

The options page may look something like this:

And then you’d instantiate the Submenu by passing the Options_Page in as a parameter:

Ultimately, this separates the submenu from the options page. There is still a dependency between the two classes, but I think that’s okay since the submenu needs to be aware of the page it’s responsible for rendering.

Regardless, this separates the responsibilities of each class, makes each more testable (especially the Options Page class), and makes the code a bit more maintainable.

Category:
Articles
Tags:

Join the conversation! 3 Comments

  1. Hey Tom, thanks for the awesome 3-part series. I love the idea of translating a simple Admin Menu example into something deeper that encourages thought on OOP patterns.

    I’m curious how you’d implement repeatability, if you needed to add multiple admin pages, say with different parent menu items, using a similar pattern. In other words, how do you see the Options_Page class fitting into the grand scheme of all WP Admin pages? Sorry if this is not possible to answer without another 3-part series :)

    It seems like the Submenu class is already totally generalized and leaves it to the Options_Page instance to know what the parent menu item is (via add_options_page), as well as what the Title/etc should be.

    In my estimation, the Submenu class could be used as-is to create an admin page under any menu item, and all that would need to change is some stuff in the Options_Page class. That is pretty cool!

    My thought after exploring how this fits into the bigger picture is that maybe Options_Page could be an extension of something more general like Admin_Page. And then other classes like Users_Page, Theme_Page, etc could also extend Admin_Page.

    Does this type of generalization sound like a reasonable approach, or do you recognize any “gotchas” that could result? When in doubt, I tend to (over-) generalize; and sometimes it back-fires!

    • thanks for the awesome 3-part series. I love the idea of translating a simple Admin Menu example into something deeper that encourages thought on OOP patterns.

      You got it. Happy to publish it! There are likely some other aspects of it that could be dealt with on a  much deeper level, but I wanted to make sure to hit the high  points.

      I’m curious how you’d implement repeatability, if you needed to add multiple admin pages, say with different parent menu items, using a similar pattern. In other words, how do you see the Options_Page class fitting into the grand scheme of all WP Admin pages? Sorry if this is not possible to answer without another 3-part series :)

      This is a good question and this depends:

      If you’re having multiple options pages, then that implies you’re going to have multiple submenu items. If that’s the case, then you’d just set up the same structure that’s outlined in the series.
      If you’re going to have tabs on a single options page, then I’d create one template and then house the each tab as a partial. This is what I do in my own work (which may be worth covering in a future series, as well).

      Note that it’s a good practice to generalize this even more by creating a PHP interface to outline what a Submenu and what a Submenu_Page should have. That way, you can implement the repeatability by implementing that interface.

      It seems like the Submenu class is already totally generalized and leaves it to the Options_Page instance to know what the parent menu item is (via add_options_page), as well as what the Title/etc should be.

      This can be managed in a couple of ways (like passing properties around), but having too much knowledge floating around and breaking encapsulation can be lame (and violate the whole purpose of OO, really). 

      In this case you can actually use some of WordPress’ helper functions like get_admin_page_title and not have to worry bout passing properties around.

      My thought after exploring how this fits into the bigger picture is that maybe Options_Page could be an extension of something more general like Admin_Page. And then other classes like Users_Page, Theme_Page, etc could also extend Admin_Page.

      This all depends on how you’re structuring you’re plugin or whatever project you’re working on. Aspects of it could certainly work that way, but I tend to work from the bottom up versus trying to start as general as possible and working downward.

      By that, I mean I aim to start with just getting an admin menu item and admin page set up, then I begin to break it apart into more manual pieces.

      Similarly, with the other page types you’ve mentioned, there’d have to be some type accountability on the actual concrete type of class it is and the properties it’s responsible for managing. 

      Does this type of generalization sound like a reasonable approach, or do you recognize any “gotchas” that could result? When in doubt, I tend to (over-) generalize; and sometimes it back-fires!

      Those are things that can be done, but I don’t have a solid approach for it right now because, many times, external factors can dictate that.

      • You’re the man, Tom – I appreciate your breakdown of my 1,001 questions.

        Here’s what happened after I read your responses:

        Thinks to self, “I want to play around with making an abstract class Admin_Page even though I don’t have a specific project in mind as a use case, even though it’s pretty clear that this is overkill for a simple task, and even though Tom says it totally depends on the project at hand.”
        Writes some crappy code, bangs head against keyboard.
        Repeats the above step several times.
        Realizes, “Things are getting messy and unnecessarily complicated because I’m trying to think about all use cases at once, for a basic task that doesn’t necessarily call for a generalized and repeatable solution.“
        Tells self, “I will never question Tom’s advice again.”

        Of course, I’m being silly on that last one – I think it’s the process of trying something new/different that is the best teacher of all, even if I’m not expecting my efforts to produce something I can keep and use.

        One specific thing of value that I can bring back to the discussion (and may hopefully help future readers) is that in order to get the submenu item to be registered, I had to add a call to the $submenu->init() method within the 03-create-submenu-page.php file. I’m sure that you just meant to leave that as an exercise for the reader, so my apologies if this spoils the surprise ending :)

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.