Group
Admin_Menu allows for the defining of Groups, compared to the vanilla add_menu_page() provided by WordPress. Using groups allows for custom Group Titles separate of the main page. This allows for the dynamic shifting of pages based on any conditions (user role, subscriptions etc), to alter which pages are added or not and keeping a unified appearance.
To define a group, there are a few properties which must be defined and a few methods which can be used to share assets and functionality between all pages within the group.
All groups must extend from
PinkCrab\Perique_Admin_Menu\Group\Abstract_Group.
Group Properties
These must all be declared as protected and can either be defined directly or via the constructor.
protected string $group_title
@var string
@throws Group_Exception (code 252) If not defined and fails validation.
Define the title of the Group.
class My_Group extends Abstract_Group{
protected string $group_title = 'My Page Group';
}
protected string $capability
@var string
@default ‘manage_options’ if not defined
Define the min capabilities a user must have for the group to be displayed.
class My_Group extends Abstract_Group{
protected string $capability = 'edit_posts';
}
protected string $icon
@var string
@default ‘dashicons-admin-generic’ if not defined
Define which dash icon or img url to use as the group icon.
class My_Group extends Abstract_Group{
protected string $icon = 'edit_posts';
}
protected string $primary_page
@var string / class-string
@throws Group_Exception (code 252) If not defined and fails validation.
The fully namespaced class name for the primary page (this must also be included in pages)
class My_Group extends Abstract_Group{
protected string $primary_page = 'Acme\My_Plugin\Page\Primary_Page';
}
protected array $pages
@var class-string
An array of fully namespaced class names, which extend the Menu_Page object
class My_Group extends Abstract_Group{
protected array $pages = [
'Acme\My_Plugin\Page\Primary_Page',
\Acme\My_Plugin\Page\Child_Page::class
];
}
You can use the
::classhelper constant if you wish.
protected int $position
@var int
@default 65 if not defined
Define the menu position See for more details
class My_Group extends Abstract_Group{
protected int $position = 24;
}
Group Methods
These must all be declared as public and are optional.
Shared Group Methods are called before individual page methods are called. Applied to enqueue() and load()
public function enqueue( Abstract_Group $group, Page $page ): void
@param Abstract_Group $group @param Page $page
This allows for the enqueueing of Scripts and Styles using wp_enqueue_script(), wp_enqueue_style() or PinkCrab - Enqueue. Any scripts of styles defined here, will be applied to every page registered in the group.
class My_Group extends Abstract_Group{
public function enqueue( Abstract_Group $group, Page $page ): void {
wp_enqueue_script(
'acme_script',
'https://www.acme.com/wp-content/plugins/acme/assets/script.js',
array( 'jquery' ),
'1.2.4',
true
);
}
}
You have access to the Group and Page as its being enqueued, so some conditional logic can be handled at runtime, if needed.
public function load( Abstract_Group $group, Page $page ): void
@param Abstract_Group $group @param Page $page
This allows for the handling for form submissions or other checks before any page which is added as part of the group.
class My_Group extends Abstract_Group{
public function load( Abstract_Group $group, Page $page ): void {
// If data has expired in transient, refresh.
$from_transient = get_transient('something');
if(false === $from_transient){
$data = do_something();
update_transient('something', $data, 12 * HOURS_IN_SECONDS);
}
}
}
Hooks
Hooks::GROUPS_PROCESSED — pinkcrab/admin_menu/groups_processed
Fires once during the registration pass, from Page_Middleware::tear_down(), after Admin_Menu has finished scanning every registered class for Abstract_Group instances. Subscribers receive the populated Group_Page_Registry as the only argument.
This is the extension point for other Perique modules (e.g. Settings_Page) that want to apply DI rules to page classes declared inside Groups — pages that would otherwise never be visible to a downstream middleware’s process() because they were never listed in App::registration_classes().
Subscribers must register the listener no later than pre_register so the action is in place before middleware processing begins. The hook is order-independent — it fires from Admin_Menu’s own tear_down(), so it does not matter where the consumer module sits in the module list.
Payload:
| Argument | Type | Description |
|---|---|---|
$registry | PinkCrab\Perique_Admin_Menu\Registry\Group_Page_Registry | Populated registry of every page class declared inside an Abstract_Group, mapped to the declaring group instance. |
Group_Page_Registry exposes:
has( string $page_class ): boolgroup_for( string $page_class ): ?Abstract_Groupall(): array<class-string, Abstract_Group>all_for_subclass( string $base_class ): array<class-string, Abstract_Group>— convenience filter, e.g.$registry->all_for_subclass( My_Settings_Page::class ).
Example — a downstream module module subscribing in pre_register to wire DI rules for every Group-declared page that extends a base My_Page type:
use PinkCrab\Perique_Admin_Menu\Hooks;
use PinkCrab\Perique_Admin_Menu\Registry\Group_Page_Registry;
class My_Module implements Module {
public function pre_register( App_Config $config, Hook_Loader $loader, DI_Container $di_container ): void {
$loader->action(
Hooks::GROUPS_PROCESSED,
function ( Group_Page_Registry $registry ) use ( $di_container ): void {
foreach ( $registry->all_for_subclass( My_Page::class ) as $page_class => $group ) {
$di_container->addRule( $page_class, [
'shared' => true,
'call' => [ [ 'configure', [ /* ... */ ] ] ],
] );
}
}
);
}
// ...
}
By the time WordPress fires admin_menu (where Page_Dispatcher materialises the pages via the DI container), the consumer’s rules are in place and applied to the constructed page instances.