PHPackages                             inpsyde/metabox-orchestra - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. inpsyde/metabox-orchestra

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

inpsyde/metabox-orchestra
=========================

Package for OOP WordPress metabox orchestration.

0.5.0(9mo ago)20121.0k↑10.8%1[1 PRs](https://github.com/inpsyde/MetaboxOrchestra/pulls)GPL-2.0-or-laterPHPPHP &gt;=7.2CI passing

Since Sep 26Pushed 9mo ago2 watchersCompare

[ Source](https://github.com/inpsyde/MetaboxOrchestra)[ Packagist](https://packagist.org/packages/inpsyde/metabox-orchestra)[ RSS](/packages/inpsyde-metabox-orchestra/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (3)Dependencies (4)Versions (17)Used By (0)

Metabox Orchestra [![Latest Stable Version](https://camo.githubusercontent.com/95ccb05d467868e354407c76cd92c148f784f4ba4389756c95613a9ab64b5a63/68747470733a2f2f706f7365722e707567782e6f72672f696e70737964652f6d657461626f782d6f72636865737472612f762f737461626c65)](https://packagist.org/packages/inpsyde/metabox-orchestra) [![Project Status](https://camo.githubusercontent.com/5b5a2250da48f45495a817a4bcdabb5d101fff298acebe00a55a52815b7119ed/687474703a2f2f6f70656e736f757263652e626f782e636f6d2f6261646765732f6163746976652e737667)](http://opensource.box.com/badges) [![License](https://camo.githubusercontent.com/e5870e315d815eab7873211f73f6a46e8d60f4d649d45e26914755abba9d6cb0/68747470733a2f2f706f7365722e707567782e6f72672f696e70737964652f6d657461626f782d6f72636865737472612f6c6963656e7365)](https://packagist.org/packages/inpsyde/metabox-orchestra)
=============================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================

[](#metabox-orchestra---)

> A Composer package that provides OOP metabox orchestration for WordPress.

---

Features
--------

[](#features)

- Allows to add metaboxes to taxonomy terms
- Automatically handles nonces and authorizations.
- Agnostic about the actual rendering and saving mechanism of metaboxes
- OOP infrastructure

---

Bootstrap
---------

[](#bootstrap)

"Metabox Orchestra" is **not** a plugin, but a Composer package. It can be required by themes, plugins or at website level for sites entirely managed by Composer.

After it is installed via Composer, and composer autoload is required, Metabox Orchestra needs to be bootstrapped, like this:

```
MetaboxOrchestra\Bootstrap::bootstrap();
```

- This can be done in any plugin, MU plugin or theme `functions.php` with no need to wrap the call in any hook.
- There's no need to check if the library is *already* bootstrapped, the snippet above can be called multiple times without any negative effect.

After this single line of code is in place, "Metabox Orchestra" is fully working and ready to be used.

---

Usage
-----

[](#usage)

After "Metabox Orchestra" is loaded and bootstrapped, it's time to add some metaboxes.

**Each metabox is Composed by 4 simple objects**, for 3 of them "Metabox Orchestra" provides the interface, for the fourth it provides a ready made implementation.

The objects are:

- A metabox *builder*
- A metabox *info*
- A metabox *view*
- A metabox *action*

### Metabox Builder

[](#metabox-builder)

The builder is an object implementing `PostMetabox` for boxes to be printed in posts edit screen, and `TermMetabox` for boxes to be printed in taxonomy terms edit screen.

Below the signature of `PostMetabox` public methods:

```
interface PostMetabox extends Metabox {

	public function create_info( string $show_or_save, Entity $entity ): BoxInfo;

	public function accept( \WP_Post $post, string $save_or_show ): bool;

	public function create_view( \WP_Post $post ): BoxView;

	public function create_action( \WP_Post $post ): BoxAction;
}
```

`TermMetabox` interface is practically identical, only in places where `PostMetabox` expects a `WP_Post`, `TermMetabox` expects a `WP_Term`.

> ***Note**: both `PostMetabox` and `TermMetabox` extend the `Metabox` interface which only contains the `create_info()` method, which (for the sake of readability) is shown as part of `PostMetabox` in the snippet above.*

### Metabox Info

[](#metabox-info)

`PostMetabox::create_info()` (and `TermMetabox::create_info()`) must return an instance of `BoxInfo`.

This is a value object shipped with the library. It encapsulates the scalar arguments that are usually passed to `add_meta_box()` WordPress function: metabox id, title, context and priority.

From inside `create_info()` method an info object can be returned just by instantiating it:

```
public function create_info( string $show_or_save, Entity $entity ): BoxInfo {

	return new BoxInfo( 'My Sample Metabox' );
}
```

The full constructor signature looks like this:

```
public function __construct( string $title, string $id = '', string $context = '', string $priority = '' )
```

However, only the title is mandatory, all other arguments will be set to sensitive default when not provided.

`$context` and `$priority` are the same arguments taken by `add_meta_box()` WordPress function.

`BoxInfo` comes with a set of class constants that help in setting them, if one wants to. For example:

```
public function create_info( string $show_or_save, Entity $entity ): BoxInfo {

	return new BoxInfo(
		__( 'My Sample Metabox', 'my-txt-domain' ),
		'sample-metabox',
		BoxInfo::CONTEXT_SIDE,
		BoxInfo::PRIORITY_HIGH,
	);
}
```

The `$show_or_save` argument can be used to distinguish if the `create_info()` is called when *showing* the metabox or when *saving* it; for this purpose the passed value has to be compared to the constants: `Metabox::SHOW` and `Metabox::SAVE`.

The `$entity` argument is an object wrapping the `WP_Post` (or `WP_Term`) the metabox will be shown for.

The object has a method `is()` to know what kind of object it actually wraps, and other useful methods, including `expose()` that returns the wrapped object.

For example, to use the post type label as part of metabox title it is possible to do:

```
public function create_info( string $show_or_save, Entity $entity ): BoxInfo {

    $metabox_name = 'Term';
    if ( $entity->is( \WP_Post::class ) ) {
        $post_type = get_post_type_object( $entity->post_type );
        $metabox_name = $post_type->labels->singular_name;
    }

	return new BoxInfo( sprintf( 'My %s Metabox', $metabox_name ) );
}
```

Note how above the `post_type` property is accessed as public property of the `Entity` object, this works thanks to "magic" `__get()` method of `Entity` that delegates public properties access to the wrapped entity, being it a `WP_Post` or a `WP_Term` object.

### Metabox View

[](#metabox-view)

"Metabox Orchestra" does **not** provide any view class, but just a view *interface* that is the same for post and term metaboxes.

The whole interface methods signature is:

```
interface BoxView {

	public function render( BoxInfo $info ): string;
}
```

So it is a *very* simple object. What happens inside `render()` it's up to you.

The `BoxInfo` instance passed to `render()` is the same that is returned by `Metabox::create_info()`.

Very likely the render method will need to access the current object that is being edited (either a `WP_Post` or a `WP_Term`), but `render()` does not receive it.

That's not an issue, because the view object is returned from `PostMetabox::create_view()` (or `TermMetabox::create_view()`) that receives that object.

Which means that the view object could accept it in the constructor the object.

For example:

```
public function create_view( \WP_Post $post ): BoxView {

	$view = new MyAwesomeBoxView( $post );

	return $view;
}
```

Note that adding a nonce field inside the `BoxView::render()` method is **not** necessary: "Metabox Orchestra" handles all nonce things.

### Metabox Action

[](#metabox-action)

"Metabox Orchestra" does **not** provide any action class, but just an action *interface* that is the same for post and term metaboxes.

The whole interface method signature is:

```
interface BoxAction {

	public function save( AdminNotices $notices ): bool;
}
```

So it is a *very* simple object. What happens inside `save()` it's up to you.

Very likely the render method will need to access the current object that is being saved (either a `WP_Post` or a `WP_Term`), but the `save()` does not receive it.

That's not an issue, because the action object is returned from `PostMetabox::create_action()` (or `TermMetabox::create_action()`) that receives that object.

Which means that the action object could accept it in the constructor the object.

For example:

```
public function create_action( \WP_Post $post ): BoxAction {

	$action = new MyAwesomeBoxAction( $post );

	return $this->action;
}
```

The `AdminNotices` instance passed to the `BoxAction::save()` method, is an object that allows to show an error or a success message as admin notice.

It is absolutely optional and should not be abused, but can be useful especially to inform the user if same errors happen during the saving routine.

Note that checking for nonces or for capability inside the `BoxAction::save()` method is **not** necessary: "Metabox Orchestra" does it for you.

When saving a post it is also not necessary to skip check for autosave or revision and skip saving, that's done by "Metabox Orchestra" as well.

### Add boxes

[](#add-boxes)

After all objects are written, it is just a matter of hooking **`Boxes::REGISTER_BOXES`** and calling the `Boxes::add_box()` method on the `Boxes` instance that is passed as argument to that hook.

For example:

```
add_action( Boxes::REGISTER_BOXES, function ( Boxes $boxes ) {
	$boxes->add_box( new MyAwesomeMetabox() );
} );
```

---

Complete Example
----------------

[](#complete-example)

Below there's a trivial yet complete example on how to add a working box to the category edit screen.

First the "Box" object:

```
namespace MyProject;

use MetaboxOrchestra\Entity;
use MetaboxOrchestra\BoxInfo;
use MetaboxOrchestra\BoxView;
use MetaboxOrchestra\BoxAction;

class SampleMetabox implements MetaboxOrchestra\TermMetabox {

	public function create_info( string $show_or_save, Entity $entity ): BoxInfo {
    	return new BoxInfo( 'My Sample Box' );
    }

    public function accept_term( \WP_Term $term, string $save_or_show ): bool {
    	return true;
    }

    public function view_for_term( \WP_Term $term ): BoxView {
    	return new SampleView( $term );
    }

    public function action_for_term( \WP_Term $term ): BoxAction {
    	return new SampleAction( $term );
    }
}
```

then the "View" object:

```
namespace MyProject;

use MetaboxOrchestra\BoxView;
use MetaboxOrchestra\BoxInfo;

class SampleView implements BoxView {

	private $term;

	public function __construct( \WP_Term $term ) {
		$this->term = $term;
	}

	public function render( BoxInfo $info ): string {

        return sprintf(
            '',
            esc_attr( get_term_meta( $this->term->term_id, '_my_sample_key', TRUE ) ?: '' )
        );
	}
}
```

and the "Action" object:

```
namespace MyProject;

use MetaboxOrchestra\BoxAction;
use MetaboxOrchestra\AdminNotices;

class SampleAction implements BoxAction {

	private $term;

	public function __construct( \WP_Term $term ) {
		$this->term = $term;
	}

	public function save( AdminNotices $notices ): bool {

		$cur_value = get_term_meta( $this->term->term_id, '_my_sample_key', TRUE ) ? : '';
		$new_value = esc_html( $_POST[ '_my_sample_key' ] ?? '' );

		$success = TRUE;

		if ( $new_value && is_string( $new_value ) && $new_value !== $cur_value ) {
			$success = $this->update_value( $new_value, $notices );
		} elseif ( ! $new_value && $cur_value ) {
			$success = $this->delete_value( $new_value, $notices );
		}

		return $success;
	}

    private function update_value( string $value, AdminNotices $notices ): bool {

        if ( ! update_term_meta( $this->term->term_id, '_my_sample_key', $value ) ) {
            $notices->add('Error saving sample value.', 'Error!', AdminNotices::ERROR );

            return false;
        }

        $notices->add( 'Sample value saved.', 'Success!', AdminNotices::SUCCESS );

        return true;
    }

    private function delete_value( AdminNotices $notices ): bool {

        if ( ! delete_term_meta( $this->term->term_id, '_my_sample_key' ) ) {
            $notices->add( 'Error deleting sample value.', 'Error!', AdminNotices::ERROR );

            return false;
        }

        $notices->add( 'Sample value deleted.', 'Success!', AdminNotices::SUCCESS );

        return true;
    }
}
```

and finally the "bootstrapping" that will probably happen in the main plugin file:

```
namespace MyProject;

use MetaboxOrchestra;

MetaboxOrchestra\Bootstrap::bootstrap();

add_action(
    MetaboxOrchestra\Boxes::REGISTER_BOXES,
    function ( MetaboxOrchestra\Boxes $boxes ) {
		$boxes->add_box( new SampleMetabox() );
	}
);
```

This is more code than it would be necessary with "normal" WordPress procedural approach, but it is modular, it is testable, enables re-usability and composition and it does all the boring repetitive tasks automatically.

Also is not *that* more: adding proper checks for capability and nonces, adding the code to print the admin notices the "standard" WordPress procedural approach will not take *that* less code.

Plus, the above snippets print the box on the *term* edit screen: doing it needs a big chunk of code that "Metabox Orchestra" does for you.

---

Requirements
------------

[](#requirements)

- PHP 7+
- Composer to install

---

Installation
------------

[](#installation)

Via Composer, package name is **`inpsyde/metabox-orchestra`**.

---

License and Copyright
---------------------

[](#license-and-copyright)

This repository is a free software, and is released under the terms of the GNU General Public License version 2 or (at your option) any later version. See [LICENSE](./LICENSE) for complete license.

###  Health Score

46

—

FairBetter than 93% of packages

Maintenance58

Moderate activity, may be stable

Popularity40

Moderate usage in the ecosystem

Community17

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~221 days

Recently: every ~704 days

Total

14

Last Release

277d ago

PHP version history (2 changes)0.1.0PHP &gt;=7

0.4.4PHP &gt;=7.2

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/2208282?v=4)[Giuseppe Mazzapica](/maintainers/gmazzap)[@gmazzap](https://github.com/gmazzap)

![](https://www.gravatar.com/avatar/003585053f7d5d2127d31d5a4e79ba309acc075154f5752de8f429ac42c54dbe?d=identicon)[Syde](/maintainers/Syde)

---

Top Contributors

[![gmazzap](https://avatars.githubusercontent.com/u/2208282?v=4)](https://github.com/gmazzap "gmazzap (30 commits)")[![Chrico](https://avatars.githubusercontent.com/u/3417446?v=4)](https://github.com/Chrico "Chrico (15 commits)")[![AlexP11223](https://avatars.githubusercontent.com/u/5680466?v=4)](https://github.com/AlexP11223 "AlexP11223 (8 commits)")[![kashalo](https://avatars.githubusercontent.com/u/23155483?v=4)](https://github.com/kashalo "kashalo (3 commits)")[![Soean](https://avatars.githubusercontent.com/u/695201?v=4)](https://github.com/Soean "Soean (3 commits)")[![MVarugh](https://avatars.githubusercontent.com/u/102949922?v=4)](https://github.com/MVarugh "MVarugh (2 commits)")[![meszarosrob](https://avatars.githubusercontent.com/u/6097856?v=4)](https://github.com/meszarosrob "meszarosrob (1 commits)")

---

Tags

metaboxesmetadataphpwordpress

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/inpsyde-metabox-orchestra/health.svg)

```
[![Health](https://phpackages.com/badges/inpsyde-metabox-orchestra/health.svg)](https://phpackages.com/packages/inpsyde-metabox-orchestra)
```

###  Alternatives

[eecli/addon-templates

Templates for the eecli generate:addon command.

386.2k1](/packages/eecli-addon-templates)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
