inpsyde/metabox-orchestra

用于对象导向 WordPress metabox 奏效的包。

0.4.4 2023-01-20 15:44 UTC

This package is auto-updated.

Last update: 2024-09-18 13:19:57 UTC


README

一个 Composer 包,为 WordPress 提供 OOP metabox 奏效。

特性

  • 允许将 metabox 添加到分类法术语
  • 自动处理非ces 和授权。
  • 对 metabox 的实际渲染和保存机制无偏见
  • 面向对象的基础设施

引导

"Metabox Orchestra" 不是一个插件,而是一个 Composer 包。它可以由主题、插件或网站级别需要,以 Composer 管理的网站。

通过 Composer 安装后,需要引导 composer 自动加载,Metabox Orchestra 需要像这样引导

MetaboxOrchestra\Bootstrap::bootstrap();
  • 这可以在任何插件、MU 插件或主题 functions.php 中完成,无需在任何钩子中包装调用。
  • 无需检查库是否已经引导,上面的片段可以多次调用而不会产生任何负面影响。

在这行代码就绪后,"Metabox Orchestra" 就完全可用。

用法

在 "Metabox Orchestra" 加载和引导后,是时候添加一些 metabox 了。

每个 metabox 都由 4 个简单的对象组成,其中 3 个 "Metabox Orchestra" 提供接口,第 4 个它提供了一个现成的实现。

这些对象是

  • 一个 metabox 构建器
  • 一个 metabox 信息
  • 一个 metabox 视图
  • 一个 metabox 操作

Metabox 构建器

构建器是一个对象,实现了 PostMetabox 用于在文章编辑屏幕中打印的框,以及 TermMetabox 用于在分类法术语编辑屏幕中打印的框。

以下 PostMetabox 公共方法的签名

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 接口实际上与 PostMetabox 相同,只是在 PostMetabox 预期 WP_Post 的地方,TermMetabox 预期 WP_Term

注意PostMetaboxTermMetabox 都扩展了只包含 create_info() 方法的 Metabox 接口,为了可读性,它在上面的片段中被显示为 PostMetabox 的一部分。

Metabox 信息

PostMetabox::create_info()(和 TermMetabox::create_info())必须返回一个 BoxInfo 实例。

这是一个与库一起提供的值对象。它封装了通常传递给 WordPress 函数 add_meta_box() 的标量参数:metabox id、标题、上下文和优先级。

create_info() 方法的内部,可以通过实例化它来返回一个信息对象

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

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

完整的构造函数签名如下

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

然而,标题是必填的,所有其他参数在未提供时将设置为敏感默认值。

$context$priority 是与 WordPress 函数 add_meta_box() 采取相同参数的相同参数。

BoxInfo 带有一组类常量,可以帮助设置它们,如果需要的话。例如

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,
	);
}

可以使用的 $show_or_save 参数用于区分 create_info() 调用时是否显示 metabox 或保存它;为此目的,传递的值必须与常量比较:Metabox::SHOWMetabox::SAVE

参数 $entity 是一个封装 WP_Post(或 WP_Term)的对象,metabox 将为其显示。

该对象有一个方法 is() 来知道它实际上封装了什么类型的对象,以及其他有用的方法,包括返回封装对象的 expose() 方法。

例如,要使用帖子类型标签作为元框标题的一部分,可以这样做:

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 ) );
}

注意上面的post_type属性是如何作为Entity对象的公共属性来访问的,这是由于Entity的“魔法”__get()方法的作用,该方法将公共属性访问委托给包装的实体,无论它是WP_Post还是WP_Term对象。

元框视图

“Metabox Orchestra”不提供任何视图类,只提供一个适用于帖子元框和术语元框的相同视图接口

整个接口方法签名是:

interface BoxView {

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

所以它是一个非常简单的对象。在render()中发生的事情由您决定。

传递给render()BoxInfo实例与由Metabox::create_info()返回的实例相同。

很可能渲染方法需要访问正在编辑的当前对象(要么是WP_Post要么是WP_Term),但render()不会接收它。

这不是问题,因为视图对象是从PostMetabox::create_view()(或TermMetabox::create_view())返回的,后者接收该对象。

这意味着视图对象可以在构造函数中接受该对象。

例如

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

	$view = new MyAwesomeBoxView( $post );
	
	return $view;
}

请注意,在BoxView::render()方法内部添加nonce字段是不必要的:“Metabox Orchestra”处理所有nonce相关的事情。

元框动作

“Metabox Orchestra”不提供任何动作类,只提供一个适用于帖子元框和术语元框的相同动作接口

整个接口方法签名是:

interface BoxAction {

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

所以它是一个非常简单的对象。在save()中发生的事情由您决定。

很可能渲染方法需要访问正在保存的当前对象(要么是WP_Post要么是WP_Term),但save()不会接收它。

这不是问题,因为动作对象是从PostMetabox::create_action()(或TermMetabox::create_action())返回的,后者接收该对象。

这意味着动作对象可以在构造函数中接受该对象。

例如

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

	$action = new MyAwesomeBoxAction( $post );
	
	return $this->action;
}

传递给BoxAction::save()方法的AdminNotices实例是一个允许以管理员通知的形式显示错误或成功消息的对象。

这是完全可选的,不应滥用,但在保存过程中发生相同错误时特别有用。

请注意,在BoxAction::save()方法中检查nonce或权限是不必要的:“Metabox Orchestra”会为您处理这些。

在保存帖子时,也不需要跳过自动保存或修订版本的检查以及跳过保存,"Metabox Orchestra"也会这样做。

添加盒子

在所有对象都写入之后,只是将Boxes::REGISTER_BOXES挂钩并调用传递给该钩子的Boxes实例上的Boxes::add_box()方法的问题。

例如

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

完整示例

下面有一个关于如何将工作框添加到分类编辑屏幕的简单而完整的示例。

首先,“Box”对象

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 );
    }
}

然后,“视图”对象

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(
            '<input name="_my_sample_key" type="text" value="%s">',
            esc_attr( get_term_meta( $this->term->term_id, '_my_sample_key', TRUE ) ?: '' )
        );
	}
}

接着,“动作”对象

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;
    }
}

最后,将在主插件文件中发生的“引导”

namespace MyProject;

use MetaboxOrchestra;

MetaboxOrchestra\Bootstrap::bootstrap();

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

这比使用“正常”WordPress过程方法需要更多的代码,但它具有模块化、可测试性、可重用性和组合性,并且可以自动执行所有乏味的重复性任务。

此外,添加适当的权限和nonce检查、添加打印管理员通知的代码与“标准”WordPress过程方法相比,代码量并没有增加太多。

此外,上述代码片段在术语编辑屏幕上打印了盒子:这样做需要一大块代码,而“Metabox Orchestra”为您处理。

要求

  • PHP 7+
  • Composer来安装

安装

通过Composer,包名是inpsyde/metabox-orchestra

许可和版权

本仓库是免费软件,并且按照GNU通用公共许可证第2版或(根据您的选择)任何更新版本的条款发布。请参阅LICENSE获取完整的许可证信息。