This is the final post in a series on An Object-Oriented Approach To The WordPress Settings API. Part 5.

Over the last few posts, I’ve covered topics ranging from creating interfaces to base classes and how to implement and inherit from both. One outstanding issue with the approach that this has covered thus far is that it didn’t take into account any type of file organization.

Anyone who has worked with any project of any size knows just how important having a clear organizational structure can be.

Later versions of PHP have feature of namespaces which can help us to further organize the code, but if you’re having to work with an old version, you don’t have that luxury. That’s no excuse for not properly organizing your files, though. You can still mimic what the namespace organization may look like.

So in this final post, I wanted to cover the approach that I normally take when organizing a plugin like the one we’ve been building.

Organization For The WordPress Settings API

First, recall that we have the following files:

  1. The plugin’s bootstrap file that is responsible for starting the whole thing up
  2. We have two classes: one that represents the dashboard and one that represents the Company Name setting
  3. We also have an interface that defines the methods that any implementing class but define
  4. We have a view for displaying the user interface
  5. We have a partial that’s rendered within the context of the user interface

If you’ve been following along, there’s a chance that your files may look something like this:

Unorganized Acme Files

And although that’s functional, it’s not maintainable. So let’s do a little bit of work to organize these files so that they are easier to understand where they are located.

  1. We’ll create a classes directory that will be used to house both our dashboard class and our company-name class.
  2. Within the classes directory, we’ll create a subdirectory called settings. This is actually where the company-name class will reside since it represents a setting. If you were to further develop this plugin, additional settings would reside in this directory, as well.
  3. We’ll have an interfaces directory that will contain the interface that we defined in the series. Similarly to the previous step, if you were to introduce additional interfaces, they would live in this, as well.
  4. Finally, we’ll introduce a views directory that also includes a partials subdirectory. The views directory houses the parent view that makes a call to the settings about (for the do_settings_sections calls, et. al.) where as the partials contains the individual files that actually contain the user interface elements with which users may interact.

If you follow the above steps, the following version of the plugin will look something like this:

Organized Plugin Files

Of course, the last thing that you’d need to change is the bootstrap file so that it’s properly loading the files from their new location:

Once done, you should have a much more organized directory structure that stands up to additional development and change rather than just throwing everything into a single directory and hoping for the best.

Series Posts

  1. An Interface For The WordPress Settings API
  2. Defining An Interface For The WordPress Settings API
  3. Implementing An Interface For The WordPress Settings API
  4. Inheritance With The WordPress Settings API
  5. Data in The WordPress Settings API
  6. Organizing Files For The WordPress Settings API
Category:
Articles
Tags:
,

Join the conversation! 27 Comments

  1. Not wanting to sound lame – I really wish I’d have something more constructive to say at this point, but the links to second article in the series aren’t pointing to the correct page or any page for that matter ;)

    Hopefully have something more debate provoking once I’ve read through it all – thanks for sharing your thoughts and practices with us!

  2. Thanks Tom for the tutorial! However, I’m a bit confused as to what code should be in what files. Would you mind providing the full plugin for download so we can “check our work”?

    Thanks,

    Eric

  3. Tom, excellent tutorial series. I really appreciate the thought you put into these. Thank you.

  4. I’ve been staring at, and modifying, the code for about an hour trying to figure out how you would add a 2nd (and 3rd and 4th…) field to the settings page given the structure of the plugin.

    I guess you could duplicate the display() method making one for each field like this: display_name(), display_address(), etc… But that doesn’t really fit into this model because you seem to have named the files after their field names such as “class-acme-company-name.php”.

    I also considered moving the calls to register_setting() and add_settings_section() into the “class-acme-company-dashboard.php” file but then you’d have to instantiate each field class like this:

    $name = new Acme_Company_Name();

    $name->run();

    $address = new Acme_Company_Address();

    $address->run();

    That doesn’t look right and it’s really ugly.

    I admit that the concept of interfaces still eludes me.

    Am I missing something obvious? Does adding additional fields require a restructuring of the overall structure of the plugin or am I over thinking this?

    Thanks!

  5. An interface is simply a definition of a class Eric, a template. The inheriting class must implement all functions of the Interface.

    For say vehicles they have many things in common and many things not. Thus an interface can be used to define common attributes and methods that describe a vehicle. But the classes that inherit the Interface need implement them.

    In C# terms “An interface contains only the signatures of methods, properties, events or indexers. A class or struct that implements the interface must implement the members of the interface that are specified in the interface definition.”

    Another example of use of interfaces are iterators. Whilst PHP for example doesnt have Lists, Dictionaries, Hash Lists etc. (containers for complex data) in say C# or Visual Basic or C++ there need be a way to iterate over these structures. Interfaces are often used to mandate that said iteration code must be implemented and the structure of said implementation so for example “foreach” knows how to iterate over them.

    Unfortunately WP is pretty much procedural. Dont get me wrong, its good code although certain things in as far as efficient code I’ve already stumbled across and versatility matters. In PHP there is a trade-off in Object Oriented Code.

    While “OOP” is basically how most software is written be that for Windows, Mac, Linux, Android, iOS etc. and is considered now “the way to do it” in software engineering PHP implemented this to have said capabilities.

    OOP lends itself towards easier to maintain code, easier debuggin’, better unit tests, extensible code and more the tradeoff’s in many languages were size (bloat) and speed. These factors changed over years of languages such as C++, C# etc. coming up with better ways to deal with these complex structures and in large part of that are what are deemed addressing modes in modern CPU’s, how the CPU can treat RAM memory and access it in laymans terms.

    OOP in PHP essentially are data buckets held in arrays. PHP was never designed from the ground up to deal with Object Orientation. Instead it relies heavily on Arrays which is why it “was” outperforming the likes of C# or Visual Basic for example. PHP is written in C++. Creating a high level language off another high level language is always going to run into caveats and obviously never be close in speed/resource usage to its parent language.

    C# or Visual Basic output something called MSIL or Microsoft Intermediate Language. This is a close representation of Assembly Language which is the “human readable mnemonic form” of machine language. Machine Language is the only thing any CPU understands.

    MSIL is processor optimized. The CPU is targeted to take advantage of its varied capabilities. It does a “just in time compile” of it.

    Java is a human readable language that when run through the java compiler outputs a ByteCode representation of the software that is then executed by the Java Virtual Machine (JVM) runtime package (engine). It gives platform independence. As long as a machine be it PC, Mac, Linux PC or something else has a JVM available, the software runs.

    These languages were built with OOP in mind. C was not, C++ was, C# is.

    C and C++ can get pretty hairy at times. Memory management is managed by the application, Pointers, Pointer arithmetic, indirection can get hairy. Multiple Inheritance (inheriting more than one class) is wonderful as is polymorphic behaviors in the right hands. C# on the other hand removes alot of the foibles of C and C#.

    Its taken Microsoft literally years to get C# / ASP.NET optimized and getting its resource footprint smaller. It now tends on a factor of 300% faster than PHP and of course also allows multi-threading, something PHP desperately needs but is WAY hard implement again as it was never “in the cards” in design planning of the language.

    In C# one cannot write procedural code. Its OOP or nothing.

    Its WHY I APPRECIATE what Tom is trying to do through many of his tutorials. He is attempting to display to people that structure, maintainability, extensibility result in better code, more secure code and code that is more adept to respond to change within a core application (in this case WordPress).

    I am way more used to OOP code these days. I think the last time I made something pretty much completely procedural was pre-2000. The problem with Procedural based code is 1. It can rapidly become spaghetti. 2. Inducing new function can often negatively impact existing function. Fix this, add that and all of a sudden whamo, other procedural has bugs or flaws appear. It can end up like a cat chasing its tail to maintain. The larger the codebase, the more the odds that happens. Trying to understand procedural code is often like trying find a treasure chest. Over here, then over there, then over here following the trail of program execution to finally go “ah hah! There it is!”. It gets altered and problems cascade upward in the codebase or downward.

    Objects are meant to take care of themselves.

    With WP, I’ve only been at it about 6 weeks. I write OOP code. Having to try hybrid my code writing in OOP yet having to deal with the applications outside world of procedural is rather maddening at times.

    I’ve read a BUNCH of Toms stuff and just gone “YES! YES! YES!” with a few, “I bet this upsets him’s” as the procedural “issues” rear their heads.

    I recently was sorta chastised for saying “WP feels more like a framework” and perhaps rightfully so. But to me, an OOP coder thats how it feels.

    I would encourage anyone/everyone to follow Tom’s writings and seek out his articles. Perhaps I may write some myself down the trail of time as I get more up to snuff with WP. Theme function is an area need be OOP’d, its a mess. Themes should be objects, display (render) side, front end function side, admin side. Three main objects with a pluther of inherited objects. Its something I already began some work on.

    • An interface is simply a definition of a class Eric, a template. The inheriting class must implement all functions of the Interface.

      For say vehicles they have many things in common and many things not. Thus an interface can be used to define common attributes and methods that describe a vehicle. But the classes that inherit the Interface need implement them.

      @Rick – Thanks! This part helps make sense of interfaces.

      • Everything Rick said is on point (and thanks so much for the kind words, Rick).

        Eric: To further drive home the idea of the interface, let’s say we wanted to introduce a new option in addition to the ‘Company Name.’

        To do this, we’d introduce a new class, say, Company_Address and then we’d implement the interface that we defined thus far. We’d then register the setting, section, and option and instantiate that class within the Dashboard class.

        This was, the Dashboard is aware of all of the settings that it manages.

        Initially, some may argue “but this introduces a lot of extra files into the program,” and though that’s true, these files are small, they are very, very focused, and they are clear as to what their purpose is.

        And that wins out every single time, in my opinion, than having a few large files.

        • Hi Tom!

          First of all, thanks for sharing your work and insights. I really love what you do, I think it’s very inspiring and educational.

          Regarding the settings, I’m afraid I’m facing the same problem as Eric. If I’m not mistaken, this is how things are supposed to work within WordPress:

          You first register one “Settings Group” using the function “register_setting” (for instance, for a Cache plugin, all its settings).
          Then, you add one or more sections there (e.g. “Basic”, “Minification”, and “Advanced Options”) using the function “add_settings_section”.
          Finally, you add one or more settings in each section using the function “add_settings_field”. For example, in “Minification” I could add “CSS minification”, “JS minification”, “List of files to exclude”, and so on.

          As I understand it, each of your classes (which implement the Acme interface) define steps 1 and 2 in their register method. This looks odd, because I those two operations were somehow different!

          Moreover, the “sanitize” method should be used in each setting and, yet, it’s linked to the “Settings Group”.

          How would you deal with that? Could you show us the code of this second setting?

          • First of all, thanks for sharing your work and insights. I really love what you do, I think it’s very inspiring and educational.

            Thanks so much for the kind words – I appreciate that, David.

            As I understand it, each of your classes (which implement the Acme interface) define steps 1 and 2 in their register method. This looks odd, because I those two operations were somehow different!

            Totally understandable. This has to do with inheritance – when the base class being called doesn’t have the method that WordPress is trying to call, it hops up to the base class (or the parent class) in order to try to find it.

            So, with that said, if you want to implement a specific sanitize method, then you’d place it in the subclass of the setting in question. For now, that’s the best I can do (rather than offering code).

            I hope this answers the question but, if not, don’t hesitate to let me know in a follow-up comment.

            • Hi Tom!

              Thanks for your answer. Actually, that was not what I was asking… but I found the answer by myself.

              Apparently, you can call the “register_setting” function multiple times with only one difference each time: the sanitize function. Then, when an admin submits the settings form, all the “registered” sanitize methods are called (one by one), and each of them will take care of its associated specific setting field. Easy!

              That said, it looks like there’s no relation between the functions “register_setting” and “add_settings_section”—the relation appears between a SECTION (which has an $id and uses a $page) and a FIELD (which references the $section and the $page). Therefore, it looks like it’d be better if I defined the set of sections outside each single field and, then, I created each field individually:

              add_settings_section( ‘x’, … );
              $fa = new AField();
              $fa->register( ‘x’ );
              $fb = new BField();
              $fb->register( ‘x’ );
              add_settings_section( ‘y’, … );
              $fc = new CField();
              $fc->register( ‘y’ );

              I hope this makes sense. Honestly, the Settings API is not the best API available here :-P

              • Gotcha! I understand what you’re saying now (but clearly misunderstood the original comment :). Thanks for the clarification and for posting some example code for anyone else who may stumble across this!

  6. Years back (I have no idea where the code is) I wrote a little class that essentially did nothing but take lost of small files and concatenate them together at runtime out on the webserver based on date/time and an XML file that defined what each production file should have. It was a tad more complex as it also served as the disk interface (filename/path) wise to service includes.

    I think I’d put it up on PHP Classes.org, but maybe not. Great place by the way for interesting little doo dads (phpclasses.org).

    Lots of files .vs. not on the net is often an argument point amongst coders. My take has always been its better for maintainability in as far as my code and maintaining it goes. In production facing code I tend examine what I am working on. For like Jooml’r which I worked alot on backend stuff lots of files, front facing stuff my codebase had many files but I’d often concatenate them together to try push a bit more speed if I thought performance be an issue.

    I’ve read tons in the past few weeks and am surprised how many WP folks in blogs concern themselves with large codebases on the admin backend. The backend is the most seldom used area of a CMS. The front facing site is where most sessions will occur. I’ve saw many a plugin’s code that never separated the front facing site code from administrative backend code, thus, yes, its getting loaded in and should not be from a few parties who talk of backend bloat.

    I just started messing about with Customizer and its easy peasy. I was reading folks saying, “Oh its so hard” and chock full of slowness. Not really.

    On the other side of the coin, I was surprised to see Prototype, scriptaculous and jQuery all being loaded up by WP.

    This is what happens when Grandma bakes cookies and so do the grand kids and it all ends up in the same cookie jar.

  7. Oh…. and your welcome Tom,

    Thank YOU as your articles and tutorials have kept the lions share of my hair still on my head .vs. the floor. LOL.

    Its VERY appreciated.

  8. Hey Tom, if you were to add this to your boilerplate plugin, where would you execute plugin ? Would you create folder settings and store these files in it ?

  9. Hi Tom,

    I worked through your excellent tutorial. Thank you for putting it together.

    I organized the code as you describe above then I tried to activate the plugin but I received a fatal error. There appears to be an endless loop between the function run() and the function create settings() in the class-acme-company-dashboard file. Can you confirm it?

    Thanks again for the tutorial.

    • I worked through your excellent tutorial. Thank you for putting it together.

      Great! Of course — I’m all about trying to share as much information as I can.

      I organized the code as you describe above then I tried to activate the plugin but I received a fatal error. There appears to be an endless loop between the function run() and the function create settings() in the class-acme-company-dashboard file. Can you confirm it?

      What was the fatal error that you received? Can you place the message in a comment and also let me know a little bit more about your environment? Are you on Windows, Linux, or macOS? What type of server and what type of PHP version?

      • Hi Tom,

        Here is the error message I receive upon activation: Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 72 bytes) in /Applications/MAMP/htdocs/wordpress/wp-content/plugins/class-template-plugin/classes/class-acme-company-dashboard.php on line 8.

        If I go to the class-acme-company-dashboard.php file and comment out either line 9 $this->create_settings(); or line 27 $name->run(); the plugin will activate without error but obviously the settings menu doesn’t function properly.

        I’m running MAMP 3.0.6 with PHP 5.5.14 for Mac OS.

        • This sounds like there is a problem with your code somewhere (that I can’t detect without actually seeing it — and comments are rarely a good place to do that).

          First, this may be a problem with the code as it is a bit dated since WordPress 4.6.1. I’d recommend running LDN and see if it shows anything.

          If it doesn’t, then I’d recommend stepping through line by line and making sure your code is on par with what’s shared. If it is, then I’d start working through the code to find where the problem exists.

          Bumping up the memory limit in PHP is not going to be the right solution — this problem seems to be symptomatic of something in the code.

          • Hi Tom,

            There’s no run() method in the Acme_Company_Name class, that’s why this fatal error problem occurs.

            If I add this to the class –

            public function run() {

            add_action( ‘admin_init’, array( $this, ‘register’ ) );

            }

            then suddenly everything’s in its right place, the plugin gets activated.

            • There’s no run() method in the Acme_Company_Name class, that’s why this fatal error problem occurs.

              Ah, apologies for overlooking that. Thanks for the comment and I’ll look to have this addressed soon.

        • Hi Zach, hey everyone with the same problem,

          This fatal error and endless loop issue is caused by the missing run() method in the Acme_Company_Name class.

          So it just needs to be added and the problem fixed:

          public function run() {

          add_action( ‘admin_init’, array( $this, ‘register’ ) );

          }

Leave a Reply