gebruederheitz / wp-easy-cpt
用于强大WordPress自定义帖子类型设置的简单易用的工具。
v2.3.3
2024-01-15 09:32 UTC
Requires
- php: >=7.3
- gebruederheitz/simple-singleton: ^1.0
Requires (Dev)
- phpstan/phpstan: ^1.4
- szepeviktor/phpstan-wordpress: ^1.0
README
用于强大WordPress自定义帖子类型设置的简单易用的工具。
安装
通过composer
> composer require gebruederheitz/wp-easy-cpt
确保您已安装Composer自动加载或备用的类加载器。
用法
帖子类型抽象类
use Gebruederheitz\Wordpress\CustomPostType\PostType; use Gebruederheitz\Wordpress\CustomPostType\PostTypeRegistrationArgs; class NewsPostType extends PostType { /** * @required Supply a slug-type name which will be used in the DB amongst * other places */ public static $postTypeName = 'news'; /** * @required The name users will see */ public static $prettyName = 'News'; /** * @optional Where the metabox will appear: one of 'side', 'normal', 'advanced' * @default 'side' */ public static $context = 'normal'; /** * @var bool Whether to use a Gutenberg editor and call the allowed block * types hook. Set to "false" by default, so if you don't need * Gutenberg you can just leave this out. */ protected $withGutenberg = true; /** * @var bool Whether to load the media picker scripts on the edit page. * If you don't need to use a media picker, you can leave this * bit out. */ protected $withMedia = true; /** * @var array List of allowed block types if Gutenberg is enabled. If you * did not set $withGutenberg to `true` you won't need this. * Otherwise supply a string-array of block names * (e.g. `core/button`). */ protected $allowedBlockTypes = [ 'core/paragraph', 'core/columns', ]; /** * @var string The translation domain to use for menu labels etc. * * If you are using the "ghwp" domain, you can skip this setting, otherwise * set it to your theme / plugin's i18n domain. */ protected $translationDomain = 'ghwp'; // ------------------------------------------------------------------------- // There are only two methods you need to define, and one you will want to // override: // ------------------------------------------------------------------------- /** * Renders the meta box for users to edit additional post type fields. * * @return void */ public function renderMetabox() { /** @var WP_Post */ global $post; // --------------------------------------------------------------------- // You could go old-school // --------------------------------------------------------------------- ?> <input name="postid" type="text" value="<?php echo $post->ID; ?>" /> <?php // or use a library like cmb2 $metabox = new_cmb2_box([ 'id' => self::$postTypeName . 'something', 'title' => 'Details', ]); // --------------------------------------------------------------------- // or use the dependency-free MetaForms class and maybe even combine it // with the repository (more below) // --------------------------------------------------------------------- /** @var Newspost $news */ $news = NewspostRepository::getInstance()->find($post->ID); MetaForms::renderTextInputField( Newspost::tagMetaFieldName, $news->getTagsAsString(), 'Tags (separate with semicolons)', false ); } /** * Handle the submission of user edited metadata. * * @param WP_POST $post * @param array $data * * @return void */ public function savePostMeta($post, $data) { /** @var Newspost $news */ $news = NewspostRepository::getInstance()->find($post->ID); $news->setTagsFromString($data[Newspost::tagMetaFieldName] ?? ''); NewspostRepository::getInstance()->save($news)->flush(); } /* * The PostType class handles the registration for you. For easy access and * type definitions it uses the PostTypeRegistrationArgs configuration * object. * To modify the arguments passed to `register_post_type` you can override * the method editRegistrationArgs() and call the fluent setters on the * PostTypeRegistrationArgs object provided. */ /** * Modify the arguments for the post type's registration call. */ protected function editRegistrationArgs(PostTypeRegistrationArgs $args): PostTypeRegistrationArgs { $args->addAuthorSupport(); // Setters in PostTypeRegistrationArgs can be chained: $args->setCanExport(true) ->setPluralLabel('Newsposts') ->makePublic(); return $args; } }
使用实体存储库
实体存储库缓存帖子类型实体的数据库调用,并有助于帖子类型数据的规范化。
作为第一步,您将需要定义您的帖子类型实体(以及可能的子实体),实现StorableEntity
接口
use Gebruederheitz\Wordpress\Domain\StorableEntity; class Newspost implements StorableEntity { /** @var int|null */ private $postId; /** @var string */ private $title = ''; /** @var array */ private $tags = []; /* * ------------------------------------------------------------------------- * For easier access, and to avoid duplication and typo-style errors, we * define the post_meta keys as class constants. It's up to you if you * want to adopt this pattern. * You are encouraged to prefix your post_meta keys. * ------------------------------------------------------------------------- */ /** @var string */ public const tagMetaFieldName = 'ghwp-faq-tags'; /* * ------------------------------------------------------------------------- * Construct the entity from the post object and the associated raw * post_meta. * ------------------------------------------------------------------------- */ public function __construct(WP_Post $post = null, $meta = []) { $this->postId = $post->ID ?? null; $this->title = $post->post_title ?? ''; $this->tags = $this->unserializeTags($meta[self::tagMetaFieldName] ?? ''); } /* * ------------------------------------------------------------------------- * How you approach this bit is up to you. We'll be using a getter / setter * pattern in this example. These methods will be used wherever the CPT * is going to be consumed – template partials for instance. * ------------------------------------------------------------------------- */ public function getPostId(): ?int { return $this->postId; } public function setPostId(?int $postId): Newspost { $this->postId = $postId; return $this; } public function getTitle(): string { return $this->title; } public function setTitle(string $title): Newspost { $this->title = $title; return $this; } public function getTags(): array { return $this->tags; } public function getTagsAsString(): string { return $this->serializeTags($this->tags); } public function setTags(array $tags): Newspost { $this->tags = $tags; return $this; } public function setTagsFromString(string $tags): Newspost { $this->tags = $this->unserializeTags($tags); return $this; } /* * ------------------------------------------------------------------------- * Implement the toMetaValues() method: Return an associative array of * post_meta keys with the corresponding string value. * ------------------------------------------------------------------------- */ public function toMetaValues(): array { return [ self::tagMetaFieldName => $this->serializeTags($this->tags), ]; } /* * ------------------------------------------------------------------------- * We're using some utility functions here; you could also use JSON or * any other serialization format. Beware however, that JSON might cause * issues with escaped quoting – your best bet is to base64_encode() your * JSON before storing and base64_decode() right after retrieving the values. * * Alternatively you can work around this using wp_slash() on already * escaped JSON strings: * https://developer.wordpress.org/reference/functions/update_post_meta/#character-escaping * ------------------------------------------------------------------------- */ private function serializeTags(array $tags): string { return join(';', $this->tags); } private function unserializeTags(string $tags): array { return array_map('trim', explode(';', $tags)); } }
然后您可以为此新实体创建一个存储库
use Gebruederheitz\Wordpress\Domain\CustomPostTypeRepository; class NewspostRepository extends CustomPostTypeRepository { /* * ------------------------------------------------------------------------- * First off, define your meta key. Use a prefix. Prefix that with an * underscore to hide the metadata from WP menus. * ------------------------------------------------------------------------- */ public static $metaKey = '_ghwp_news'; /* * ------------------------------------------------------------------------- * Now define the entity and post type classes you want the repository * to use. The $entityClass needs to implement the StorableEntity interface, * while $postTypeClass must implement PostTypeInterface. * ------------------------------------------------------------------------- */ /** @var string */ protected static $entityClass = Newspost::class; /** @var string */ protected static $postTypeClass = NewsPostType::class; /* * ------------------------------------------------------------------------- * You can override the type for the internal $entities property to get * better type hinting: * ------------------------------------------------------------------------- */ /** @var Newspost[] */ protected $entities = []; }
基本上您就完成了。如果您进行任何非常规操作,例如
- 具有多个属性的大型实体,您希望将它们存储在不同的元字段中
- 处理不是整个自定义帖子类型的元字段
- ...
您可以在下面找到示例下面
实体存储库的非CPT使用
CustomPostTypeRepository
类与PostType
实体类紧密耦合,但有时您可能希望在不进行这种紧密关联的情况下使用实体存储库。例如,您可以使用它来存储普通post
或page
的元字段。
在这些场景中,您可以使用基类AbstractRepository
,它不会将您限制在单个帖子类型上
use Gebruederheitz\Wordpress\Domain\AbstractRepository; class ViewCountRepository extends AbstractRepository { /* * ------------------------------------------------------------------------- * Here, you do not need to provide a CPT, only the meta field key and the * entity class this repository will manage. * ------------------------------------------------------------------------- */ public static $metaKey = 'ghwp_view_count'; protected static $entityClass = ViewCount::class; /* * ------------------------------------------------------------------------- * You will have to implement the abstract method `getPosts()` in order * to define the selection your entity is based on. We could, for instance, * introduce a view counter on all posts with a tag "counted", and by * default sort them alphabetically by title. * ------------------------------------------------------------------------- */ /** * Retrieve all posts that will be processed in getAllFromDB(). Use this * method to fetch the list of posts relevant to you. * If you're using a custom post type with * Gebruederheitz\Wordpress\CustomPostType\PostType this method has a basic * implementation in CustomPostTypeRepository. * * @return WP_Post[] */ protected static function getPosts(): array { return get_posts( [ 'post_type' => 'post', 'tag' => 'counted', 'orderby' => 'title', 'numberposts' => -1, ] ); } }
实体存储库示例
示例:getPosts()
重写
protected static function getPosts(): array { // Example: Sort entries by the meta value we define return get_posts( 'post_type' => 'post', 'tag' => 'counted', 'orderby' => 'meta_value_num title', 'meta_key' => self::$metaKey, 'order' => 'ASC', ) // Example: Retrieve all posts of a custom type ordered by a user-defined // index stored in a separate meta field (using // CustomPostTypeRepository) return get_posts( [ 'post_type' => MyCustomPostType::$postTypeName, 'orderby' => 'meta_value_num title', 'meta_key' => self::$metaKey . '_order', 'order' => 'ASC', 'numberposts' => -1, ] ); // This is the default for CustomPostTypeRepository – retrieves all // posts for the CPT class you set in $postTypeClass. return get_posts( [ 'post_type' => call_user_func([static::$postTypeClass, 'getPostTypeName']), 'numberposts' => -1, ] ); }
示例:在将数据存储在单独的元字段(cmb2等)时重写entityFromPostId
/** * Instantiates an entity instance from a post ID. * * @param int $postId * @param ?WP_Post $post * * @return StorableEntity An instance of the entity type defined in static::$entityClass */ protected static function entityFromPostId(int $postId, WP_Post $post = null): StorableEntity { if (empty($post)) { $post = get_post($postId); } $meta = self::getMetaValues($postId); // So far it's the default behaviour of this method. We get the post // object if it hasn't been passed, and get the meta values for // static::$metaKey. // // Now we add some meta values stored separately, e.g. when using cmb2 / // CustomMetaBoxes2 $meta['some-other-key'] = get_post_meta($postId, '_some-other-key', true); // When we're done, we instantiate an object from the entity class, // passing it the $meta array we extended and return the entity: return new static::$entityClass($post, $meta); }
示例:存储多个元字段时的persist()
重写
/** * Save a single Entity's meta fields to the database. Writes the common * fields to self::$metaKey and the "order" field's value to * "{$metaKey}_order" to allow custom sorting. * * @param StorableEntity $item The Entity to be persisted to the DB. */ protected function persist(StorableEntity $item): void { update_post_meta($item->getPostId(), self::$metaKey, $item->toMetaValues()); update_post_meta($item->getPostId(), self::$metaKey . '_order', $item->getOrder()); } }
开发
依赖项
- PHP >= 7.4
- Composer 2.x
- NVM和nodeJS LTS(v16.x)
- 推荐:GNU Make(或替代方案)