Class Registries and Apps

In version 2.1, with the creation of the hooks API, the concept of the “app” was introduced. Apps are basically just objects that provide access to a collection of related methods, and/or possibly collections of other objects and sub-apps. An app is basically the developer-facing representation of, or container for, an API. For the hooks API, there is the hooks app, for the entities API, the entities app.

Class Registries #

The apps are built around the concept of class registries. Class registries are objects that act as object factories. You register a class with the registry by supplying it the class name, the slug that the class should be known by, and possibly also other information relating to the class. When you need an object of that type, you don’t instantiate your class directly then, instead you ask the class registry for it by its slug, and an object of the type registered with that slug will be returned to you.

The value of this approach is that it can give you a central location to register all classes of a particular type. For example, in the hooks API, all of the hook event classes are registered in a single app registry. This then all of them to be retrieved in a very simple manner.

What really makes the class registries useful though, is the apps API. Think about the above code for a minute. How would we get the registry in different parts of the code where we want to register or retrieve objects? How do we ensure that all of the objects that need to be registered are before we start trying to get objects from the registry?

Apps API #

This is where the apps API comes in. It provides the backbone for registering, retrieving, initializing, and persisting class registries.

The main app object can be retrieved using the wordpoints_apps() function. It will return the main WordPoints_App object, which basically just provides a class registry for all of the (sub-)apps, except with a retrieval method. To get one of the apps, you’d do this:

If the retrieved app is a class registry, it will automatically be initialized: an action will be called to trigger the registration of an any classes that should be registered with that registry. Of course, the app could also be any other object that encompasses an API. It could even be a collection of sub-apps itself. Or it could be any combination of these.

Registering sub-apps #

When an app object is initialized, it fires an action so that any sub-apps now to register themselves. The action has the format 'wordpoints_init_app-{$full_slug}', where the full slug is the slug of the app itself, prepended with the slug of its parent app(s), if any. For the main app registry, the slug 'apps' is used, so its action is 'wordpoints_init_app-apps'. For apps registered for the main app, like the hooks API app, the action is like 'wordpoints_init_app-hooks' (the main apps registry’s slug is not prepended). If there was an app that was a child of the hooks app and it had the slug 'child_app', then its action would be 'wordpoints_init_app-hooks-child_app'.

So the code that WordPoints uses to register the main apps for its core APIs looks something like this:

As you can see, we have a variety of different types of apps being registered here:

  • For the hooks API, a custom object, that is also an app object itself (meaning it can have its own sub-apps).
  • For the entities API, an “app registry” which is a class registry and an app object.
  • For the data types API, a regular class registry.

The apps API allows us to register these varied core APIs in a single location, so that they can be retrieved at will. But the classes for each of them doesn’t have to be loaded and instantiated until it is called upon. And the main apps app will take care of storing a reference to each sub-app’s instance that we can retrieve them wherever we need them.

Class Registry initialization #

So that tells us how we can have our class registries stored for us in an easy to access manner using the apps API. But that is only part of what the app API does. It also ensures that whenever one of its sub-apps that is a class registry is first retrieved, an action is called so that the registry can be initialized. What this means is that all of the classes that belong in that registry can be registered for it just-in-time when that action is called. That way we know that all of the classes that belong in that registry will be registered when we use it, and that the registration code will only have to run when the registry is actually going to be used for something.

The class registry init actions have the format 'wordpoints_init_app_registry-{$full_slug}', where the full slug is the slug of the class registry sub-app, prepended with the slug of the parent app(s), if any. For children of main app, like the data types API, the slug of the main app, 'apps', is also prepended, like this: 'wordpoints_init_app_registry-apps-data_types'. For grandchildren apps, the apps part is omitted, so the action for the events class registry that is a sub-app of the hooks API is 'wordpoints_init_app_registry-hooks-events'.

The function that registers the data type classes in core looks something like this:

Types of Class Registries #

WordPoints core includes several different kinds of class registries. There are two main kinds, defined by two interfaces:

  1. WordPoints_Class_RegistryI — This is the basic class registry interface, where all of the classes are to be stored indexed by unique slugs.
  2. WordPoints_Class_Registry_ChildrenI — Children class registries differ from the above in that the classes are grouped together under different “parent” slugs. Both the parent and “child” slugs are needed to retrieve a class. So you can have two classes with the same child slug, but different parent slugs. This is useful when you sometimes need to retrieve all classes in the registry, but at other times just need those in a particular group.

Each of these interfaces can be implemented in several different ways. Here are the implementations available in core:

  • WordPoints_Class_Registry — The basic class registry implementation.
  • WordPoints_Class_Registry_Persistent — A class registry that keeps an internal reference to the objects that it creates, and returns the same object when called multiple times with the same slug. (The regular registry will return a new object each time it is called.)
  • WordPoints_App_Registry — A class registry that is also an app object where sub-apps can be registered.
  • WordPoints_Class_Registry_Children — A class registry where the classes are grouped together as “children” of parent slugs.

Modules #

For modules that want to register their own apps, WordPoints provides the modules app. All of the apps for a module should be registered as children of a parent app for that module, and this parent for all of the module’s apps should be a child of the modules app. The main app for a module should have a slug matching the module’s slug (the lowercase form of the namespace declared in the module’s headers).

The main app for a module can be retrieved using the wordpoints_module() function:

Components #

Components follow a similar pattern to modules, so that all of a component’s apps are registered as children of a parent app for that component that is itself a child of the components app. The main app for a particular component can be retrieved using the wordpoints_component() function: