Registering Meta Boxes with CMB2 and Creating a Real Estate Plugin

by | Mar 26, 2021 | Building a Plugin, Custom Post Types, Plugin Development, WordPress | 0 comments

Wanna register meta boxes like a boss? Hundreds of lines of codes are for loosers who just didn’t take the time to learn how to wield the power of the almighty CMB2! In my previous blog post Register Meta Boxes for Custom Post Types I covered how using the core WordPress functions we could add meta fields to any custom post type or page we wanted. In this post we’ll learn how you can quickly get up and running using CMB2 in your next WordPress theme or plugin. Before we get started I’m going to assume you have a basic understanding of what a custom post type and meta boxes are as well as basic understanding of WordPress plugin development.

What is CMB2?

CMB2 is a developer’s toolkit for building metaboxes, custom fields, and forms for WordPress that will blow your mind. Easily manage meta for posts, terms, users, comments, or create custom option pages.

CMB2.IO

The first in a long time a framework clearly states what it’s used for in two sentences or less. To me, CMB2 is a toolkit that allows me to speed up my plugin development and help keep my code more DRY. Recently developing my new plugin Connected Sermons I was able to reduce my total lines of code for meta boxes from 200 to 30!

Building Time!

Personally, I’m a contextual learner, I create read dev docs… but the content on sticks when it’s applied in real examples. To learn CMB2 we are going to create a Real Estate listing plugin! Let’s start with what a Listing looks like.

  1. Listing has a featured image. We’ll use the WP’s Featured Image for this.
  2. Listing has a description. We’ll use the WP’s built-in Gutenberg Editor for this.
  3. Listing has an address (Street, Postal, and Region). We’ll register meta fields for this.
  4. Listing has a sale price. We’ll register meta fields for this.
  5. Listing has a listing agent. We’ll register a custom meta field for this with an associated taxonomy.
  6. Listing has multiple images for a gallery. You guessed it, we’ll be using a meta field for this too.
  7. Listing has a dynamic list of features. We’ll use a repeater field of text meta fields for this.
  8. There are more things properties I’m just too lazy to keep going.

Before we get started with CMB2 we need to register our post types and taxonomies first. Trust me we’ll get to it!

Plugin Setup and Registering Post Types

To get started let’s create a new folder in the /wp-content/plugins folder with a name simple-real-estate and create a new file simple-real-estate.php in it.

-/wp-content/
--/simple-real-estate/
---simple-real-estate.php

Let’s go ahead and update our plugin file header and activate it.

<?php

/**
 * Plugin Name: Simple Real Estate
 * Description: Easily organize and display Property Listings.
 * Version:     1.0
 * Author:      Scott Anderson
 * Author URI:  https://thriftydeveloper.com
 * License:     GPL2
 * Textdomain:  simple-real-estate
 *
 */

Registering Post Type


Next, let’s register our Listing post type. I’m not going to cover all the details of registering a custom post type, if you are interested I wrote a post a little ago about it Registering a custom post type.

Within our new plugin folder let’s create a listings.php file.

-/wp-content/
--/simple-real-estate/
---simple-real-estate.php
---listings.php

Now let’s paste in our create post type code.

<?php

class Listings
{

	/**
	 * Permalink slug for this post type
	 *
	 * @var string $slug Permalink prefix
	 * @since NEXT
	 */
	private $slug = 'ca_listings';

	/**
	 * Post ID of this post type.
	 *
	 * @var int $post_id Id of Post.
	 * @since NEXT
	 */
	private $post_id;

	/**
	 * Construct
	 *
	 * @since  0.1.0
	 */
	public function __construct()
	{
		$this->hooks();
	}

	/**
	 * Register Hooks
	 *
	 * @since  NEXT
	 */
	private function hooks(): void
	{
		add_action('init', [$this, 'register_post']);
	}

	/**
	 * Post Labels
	 *
	 * @since  NEXT
	 * @return array
	 */
	private function post_labels(): array
	{
		return [
			'name'                  => _x('Listings', 'Post type general name', 'Listing'),
			'singular_name'         => _x('Listing', 'Post type singular name', 'Listing'),
			'menu_name'             => _x('Listings', 'Admin Menu text', 'Listing'),
			'name_admin_bar'        => _x('Listing', 'Add New on Toolbar', 'Listing'),
			'add_new'               => __('Add New', 'Listing'),
			'add_new_item'          => __('Add New Listing', 'Listing'),
			'new_item'              => __('New Listing', 'Listing'),
			'edit_item'             => __('Edit Listing', 'Listing'),
			'view_item'             => __('View Listing', 'Listing'),
			'all_items'             => __('All Listings', 'Listing'),
			'search_items'          => __('Search Listings', 'Listing'),
			'parent_item_colon'     => __('Parent Listings:', 'Listing'),
			'not_found'             => __('No Listings found.', 'Listing'),
			'not_found_in_trash'    => __('No Listings found in Trash.', 'Listing'),
			'featured_image'        => _x('Listing Cover Image', 'Overrides the "Featured Image" phrase for this post type. Added in 4.3', 'Listing'),
			'set_featured_image'    => _x('Set cover image', 'Overrides the "Set featured image" phrase for this post type. Added in 4.3', 'Listing'),
			'remove_featured_image' => _x('Remove cover image', 'Overrides the "Remove featured image" phrase for this post type. Added in 4.3', 'Listing'),
			'use_featured_image'    => _x('Use as cover image', 'Overrides the "Use as featured image" phrase for this post type. Added in 4.3', 'Listing'),
			'archives'              => _x('Listing archives', 'The post type archive label used in nav menus. Default "Post Archives". Added in 4.4', 'Listing'),
			'insert_into_item'      => _x('Insert into Listing', 'Overrides the "Insert into post"/"Insert into page" phrase (used when inserting media into a post). Added in 4.4', 'Listing'),
			'uploaded_to_this_item' => _x('Uploaded to this Listing', 'Overrides the "Uploaded to this post"/"Uploaded to this page" phrase (used when viewing media attached to a post). Added in 4.4', 'Listing'),
			'filter_items_list'     => _x('Filter Listings list', 'Screen reader text for the filter links heading on the post type listing screen. Default "Filter posts list"/"Filter pages list". Added in 4.4', 'Listing'),
			'items_list_navigation' => _x('Listings list navigation', 'Screen reader text for the pagination heading on the post type listing screen. Default "Posts list navigation"/"Pages list navigation". Added in 4.4', 'Listing'),
			'items_list'            => _x('Listings list', 'Screen reader text for the items list heading on the post type listing screen. Default "Posts list"/"Pages list". Added in 4.4', 'Listing'),
		];
	}

	/**
	 * Post Arguments
	 *
	 * @since  NEXT
	 * @return array
	 */
	private function post_arguments(): array
	{
		return [
			'labels'             => $this->post_labels(),
			'description'        => 'Listing custom post type.',
			'public'             => true,
			'publicly_queryable' => true,
			'show_ui'            => true,
			'show_in_menu'       => true,
			'query_var'          => true,
			'rewrite'            => array('slug' => 'Listing'),
			'capability_type'    => 'post',
			'has_archive'        => true,
			'hierarchical'       => false,
			'menu_position'      => 20,
			'supports'           => array('title', 'editor', 'author', 'thumbnail'),
			'taxonomies'         => array('category', 'post_tag'),
			'show_in_rest'       => true,
		];
	}

	/**
	 * Register Post Type
	 *
	 * @since  NEXT
	 */
	public function register_post(): void
	{
		register_post_type($this->slug, $this->post_arguments());
	}
}

new Listings();

Now let’s update our main plugins file to include our listings file.

<?php

/**
 * Plugin Name: Simple Real Estate
 * Description: Easily organize and display Property Listings.
 * Version:     1.0
 * Author:      Scott Anderson
 * Author URI:  https://thriftydeveloper.com
 * License:     GPL2
 * Textdomain:  simple-real-estate
 *
 */


define('CS_DIRECTORY', __DIR__);

require CS_DIRECTORY . '/listings.php';

new Listings();

Registering Taxonomy

Gif Break

Let’s move onto registering our taxonomy. Go ahead and create an agent taxonomy. Rinse and repeat create agents.php in our plugins main directory.

-/wp-content/
--/simple-real-estate/
---simple-real-estate.php
---agents.php

And the code for agents.php

<?php //phpcs:ignore WordPress.Files.Filename
/**
 * Custom Post Type for Agents
 *
 */

/**
 * Custom post type for Agents
 *
 */
class Agents
{

	/**
	 * Permalink slug for this post type
	 *
	 * @var string $slug Permalink prefix
	 * @since NEXT
	 */
	private $slug = 'ca_agents';

	/**
	 * Construct
	 *
	 * @since  0.1.0
	 */
	public function __construct()
	{
		$this->hooks();
	}

	/**
	 * Register Hooks
	 *
	 * @since  NEXT
	 */
	private function hooks(): void
	{
		add_action('init', [$this, 'register_taxonomy']);
	}

	/**
	 * Register Taxonomy
	 *
	 * @since  NEXT
	 */
	public function register_taxonomy(): void
	{

		$args = array(
			'label'        => __('Agents', 'simple-real-estate'),
			'show_in_rest' => true,
		);

		register_taxonomy($this->slug, 'ca_listings', $args);
	}
}

And again update our main plugin file to include our agents taxonomy.

<?php

/**
 * Plugin Name: Simple Real Estate
 * Description: Easily organize and display Property Listings.
 * Version:     1.0
 * Author:      Scott Anderson
 * Author URI:  https://thriftydeveloper.com
 * License:     GPL2
 * Textdomain:  simple-real-estate
 *
 */


define('CS_DIRECTORY', __DIR__);

require CS_DIRECTORY . '/listings.php';
require CS_DIRECTORY . '/agents.php';

new Listings();
new Agents();

At this point you should see a menu page for Listings and a submenu page of Agents. Go ahead and create a few random agents that we’ll be using for test data later.

CMB2 Time Baby!

To start using CMB2 we must first download it from GitHub and drop it in our plugins directory.

-/wp-content/
--/simple-real-estate/
---/cmb2/*
---simple-real-estate.php
---agents.php

Next in our main plugin file let’s activate CMB2 so we can use it else where in our plugin.

<?php

/**
 * Plugin Name: Simple Real Estate
 * Description: Easily organize and display Property Listings.
 * Version:     1.0
 * Author:      Scott Anderson
 * Author URI:  https://thriftydeveloper.com
 * License:     GPL2
 * Textdomain:  simple-real-estate
 *
 */


define('CS_DIRECTORY', __DIR__);

if (file_exists(dirname(__FILE__) . '/cmb2/init.php')) {
	require_once dirname(__FILE__) . '/cmb2/init.php';
} elseif (file_exists(dirname(__FILE__) . '/CMB2/init.php')) {
	require_once dirname(__FILE__) . '/CMB2/init.php';
}

require CS_DIRECTORY . '/listings.php';
require CS_DIRECTORY . '/agents.php';

new Listings();
new Agents();

Now we can go ahead and start creating all our meta boxes! All we have to do is create a simple function in our listing.php file with CMB2 helper functions and we are done. There are dozens of different field types that CMB2 offers from simple text, checkbox, image, repeater, and more. We will only be using a small sub-set today but for a full list check out fields type documentation. I also found looking at the source code of example-functions.php being extremely helpful.

In our demo we are going to use the text, taxonomy, image gallery, and repeater field types.

Text Field Types

If we look back at our requirements we need to have meta fields for street, city, postal code, region, and listing price. All of these can simply be registered as text fields. In our demonstration we will not be doing data validation, but you can always write a save_post action to sanitize the data before the listing saves.

Note: For better formatting we will be using text_money field type for the listing price.

In order to register fields we need to first register our meta box for our fields to go in. Let’s create a function that gets called on the cmb2_admin_init hook. Within the function we simple call new_cmb2_box and our new meta box is created!

add_action('cmb2_admin_init', [$this, 'register_post_meta']);

.
.
.

/**
 * Register Listing Details Meta Fields
 *
 * @since  NEXT
 * @return void
 */
public function register_post_meta(): void
{
	$listing_details = new_cmb2_box(array(
		'id'            => 'ca_listing_details', // Custom ID of the box.
		'title'         => esc_html__('Listing Details', 'simple-real-estate'), // Title visible to the use.
		'object_types'  => array($this->slug),
	));

}

Next we can now create our text fields but calling the add_field() method on the newly created cmb2_box.

$listing_details->add_field(array(
	'name'       => esc_html__('Street', 'simple-real-estate'),
	'desc'       => esc_html__('What is the street and unit number?', 'connected_sermons'),
	'id'         => 'ca_street',
	'type'       => 'text',
));

Now we just rinse and repeated for all our field types. At the end your listing.php should look like this.

<?php

class Listings
{

	/**
	 * Permalink slug for this post type
	 *
	 * @var string $slug Permalink prefix
	 * @since NEXT
	 */
	private $slug = 'ca_listings';

	/**
	 * Post ID of this post type.
	 *
	 * @var int $post_id Id of Post.
	 * @since NEXT
	 */
	private $post_id;

	/**
	 * Construct
	 *
	 * @since  0.1.0
	 */
	public function __construct()
	{
		$this->hooks();
	}

	/**
	 * Register Hooks
	 *
	 * @since  NEXT
	 */
	private function hooks(): void
	{
		add_action('init', [$this, 'register_post']);
		add_action('cmb2_admin_init', [$this, 'register_post_meta']);
	}


	/**
	 * Post Labels
	 *
	 * @since  NEXT
	 * @return array
	 */
	private function post_labels(): array
	{
		return [
			'name'                  => _x('Listings', 'Post type general name', 'Listing'),
			'singular_name'         => _x('Listing', 'Post type singular name', 'Listing'),
			'menu_name'             => _x('Listings', 'Admin Menu text', 'Listing'),
			'name_admin_bar'        => _x('Listing', 'Add New on Toolbar', 'Listing'),
			'add_new'               => __('Add New', 'Listing'),
			'add_new_item'          => __('Add New Listing', 'Listing'),
			'new_item'              => __('New Listing', 'Listing'),
			'edit_item'             => __('Edit Listing', 'Listing'),
			'view_item'             => __('View Listing', 'Listing'),
			'all_items'             => __('All Listings', 'Listing'),
			'search_items'          => __('Search Listings', 'Listing'),
			'parent_item_colon'     => __('Parent Listings:', 'Listing'),
			'not_found'             => __('No Listings found.', 'Listing'),
			'not_found_in_trash'    => __('No Listings found in Trash.', 'Listing'),
			'featured_image'        => _x('Listing Cover Image', 'Overrides the "Featured Image" phrase for this post type. Added in 4.3', 'Listing'),
			'set_featured_image'    => _x('Set cover image', 'Overrides the "Set featured image" phrase for this post type. Added in 4.3', 'Listing'),
			'remove_featured_image' => _x('Remove cover image', 'Overrides the "Remove featured image" phrase for this post type. Added in 4.3', 'Listing'),
			'use_featured_image'    => _x('Use as cover image', 'Overrides the "Use as featured image" phrase for this post type. Added in 4.3', 'Listing'),
			'archives'              => _x('Listing archives', 'The post type archive label used in nav menus. Default "Post Archives". Added in 4.4', 'Listing'),
			'insert_into_item'      => _x('Insert into Listing', 'Overrides the "Insert into post"/"Insert into page" phrase (used when inserting media into a post). Added in 4.4', 'Listing'),
			'uploaded_to_this_item' => _x('Uploaded to this Listing', 'Overrides the "Uploaded to this post"/"Uploaded to this page" phrase (used when viewing media attached to a post). Added in 4.4', 'Listing'),
			'filter_items_list'     => _x('Filter Listings list', 'Screen reader text for the filter links heading on the post type listing screen. Default "Filter posts list"/"Filter pages list". Added in 4.4', 'Listing'),
			'items_list_navigation' => _x('Listings list navigation', 'Screen reader text for the pagination heading on the post type listing screen. Default "Posts list navigation"/"Pages list navigation". Added in 4.4', 'Listing'),
			'items_list'            => _x('Listings list', 'Screen reader text for the items list heading on the post type listing screen. Default "Posts list"/"Pages list". Added in 4.4', 'Listing'),
		];
	}

	/**
	 * Post Arguments
	 *
	 * @since  NEXT
	 * @return array
	 */
	private function post_arguments(): array
	{
		return [
			'labels'             => $this->post_labels(),
			'description'        => 'Listing custom post type.',
			'public'             => true,
			'publicly_queryable' => true,
			'show_ui'            => true,
			'show_in_menu'       => true,
			'query_var'          => true,
			'rewrite'            => array('slug' => 'Listing'),
			'capability_type'    => 'post',
			'has_archive'        => true,
			'hierarchical'       => false,
			'menu_position'      => 20,
			'supports'           => array('title', 'editor', 'author', 'thumbnail'),
			'taxonomies'         => array('category', 'post_tag'),
			'show_in_rest'       => true,
		];
	}

	/**
	 * Register Post Type
	 *
	 * @since  NEXT
	 */
	public function register_post(): void
	{
		register_post_type($this->slug, $this->post_arguments());
	}

	/**
	 * Register Sermon Details Meta Fields
	 *
	 * @since  NEXT
	 * @return void
	 */
	public function register_post_meta(): void
	{
		$listing_details = new_cmb2_box(array(
			'id'            => 'ca_listing_details', // Custom ID of the box.
			'title'         => esc_html__('Listing Details', 'simple-real-estate'), // Title visible to the use.
			'object_types'  => array($this->slug),
		));

		$listing_details->add_field(array(
			'name'       => esc_html__('Street', 'simple-real-estate'),
			'desc'       => esc_html__('What is the street and unit number?', 'simple-real-estate'),
			'id'         => 'ca_street',
			'type'       => 'text',
		));

		$listing_details->add_field(array(
			'name'       => esc_html__('City', 'simple-real-estate'),
			'desc'       => esc_html__('What is the City?', 'simple-real-estate'),
			'id'         => 'ca_city',
			'type'       => 'text',
		));

		$listing_details->add_field(array(
			'name'       => esc_html__('Region', 'simple-real-estate'),
			'desc'       => esc_html__('What is the region?', 'simple-real-estate'),
			'id'         => 'ca_region',
			'type'       => 'text',
		));

		$listing_details->add_field(array(
			'name'       => esc_html__('Postal Code', 'simple-real-estate'),
			'desc'       => esc_html__('What is the postal code?', 'simple-real-estate'),
			'id'         => 'ca_postal_code',
			'type'       => 'text',
		));

		$listing_details->add_field(array(
			'name'       => esc_html__('Listing Price', 'simple-real-estate'),
			'desc'       => esc_html__('What is the asking price?', 'simple-real-estate'),
			'id'         => 'ca_listing_price',
			'type'       => 'text_money',
		));
	}
}

new Listings();

We should now see our new meta fields when creating or editing a listing.

Taxonomy Field Type

To associate the listing with our realtors we’ll be using the taxonomy field type. Fortunately this one is fairly straight forward.

Note: The taxonomy field must match the slug we registered EXACTLY in the case ca_agents.

		$listing_details->add_field(array(
			'name'           => 'Listing Agent',
			'desc'           => 'Who is the listing agent?',
			'id'             => 'ca_listing_agent',
			'taxonomy'       => 'ca_agents', //Enter Taxonomy Slug
			'type'           => 'taxonomy_select',
			'remove_default' => 'true', // Removes the default metabox provided by WP core.
		));
Taxonomy picker!

File List Field Type

I think you know where this is going. For our listing photos we’ll be using the file_list field type.

		$listing_details->add_field(array(
			'name' => 'Listing Photos',
			'desc' => 'The more the merrier!',
			'id'   => 'ca_listing_photos',
			'type' => 'file_list',
			'text' => array(
				'add_upload_files_text' => 'Listing Photos', // default: "Add or Upload Files"
				'remove_image_text' => 'Listing Photo', // default: "Remove Image"
				'file_text' => 'Listing Photos', // default: "File:"
				'file_download_text' => 'Listing Photo', // default: "Download"
				'remove_text' => 'Listing Photo', // default: "Remove"
			),
		));
Listing Photos Added.

BREAK TIME!

Reading break.

Repeatable Fields

What’s the age of the roof? Does it have a swimming pool? I need to mention some feature problem. If you ever look up property details on Zillow you’ll find dozens of field types an agent might want to list as a feature of the property. Since this list is not the same for all property we want to give the agent the ability to create a key-value pair so that they can create whatever fields they need. We can accomplish this with a repeating group with text fields for detail title and detail information.

For example Roof Replaced in 2020. We will create a detail title of Roof Replaced and detail information of 2020.

In CMB2 we can create repeating groups that allows us to repeat a group of items we’d like to collect information for. To get started we need to create a group_id, think of this as a meta field for meta fields.

		$group_field_id = $listing_details->add_field(array(
			'id'          => 'ca_listing_detail_group',
			'type'        => 'group',
			'description' => __('Generates reusable form entries', 'simple-real-estate'),
			'options'     => array(
				'group_title'       => __('Property Details'),
				'add_button'        => __('Add Additional Detail', 'simple-real-estate'),
				'remove_button'     => __('Remove Detail', 'simple-real-estate'),
				'sortable'          => true,
			),
		));

With our group field created we can now create the two text fields as shown previous. However, this time we will pass the group_id in the constructor to tell CMB2 to group the new field into the repeatable group.

		$group_field_id = $listing_details->add_field(array(
			'id'          => 'ca_listing_detail_group',
			'type'        => 'group',
			'description' => __('Generates reusable form entries', 'simple-real-estate'),
			'options'     => array(
				'group_title'       => __('Property Details'),
				'add_button'        => __('Add Additional Detail', 'simple-real-estate'),
				'remove_button'     => __('Remove Detail', 'simple-real-estate'),
				'sortable'          => true,
			),
		));

		$listing_details->add_group_field($group_field_id, array(
			'name' => __('Detail Title', 'simple-real-estate'),
			'id'   => 'ca_detail_title',
			'type' => 'text',
		));

		$listing_details->add_group_field($group_field_id, array(
			'name' => __('Detail Information', 'simple-real-estate'),
			'id'   => 'ca_detail_information',
			'type' => 'text',
		));
	}
Repeating fields!

Let’s put it all together. Complete listings file.

And We Are Done

We have now created a custom real estate listing plugin in under 500ish lines? Try doing that without CMB2. While this only works for backend data storage we now have all the information that custom short codes could be written to display these properties… perhaps this is a foreshadow to a new blog post!

<?php

class Listings
{

	/**
	 * Permalink slug for this post type
	 *
	 * @var string $slug Permalink prefix
	 * @since NEXT
	 */
	private $slug = 'ca_listings';

	/**
	 * Post ID of this post type.
	 *
	 * @var int $post_id Id of Post.
	 * @since NEXT
	 */
	private $post_id;

	/**
	 * Construct
	 *
	 * @since  0.1.0
	 */
	public function __construct()
	{
		$this->hooks();
	}

	/**
	 * Register Hooks
	 *
	 * @since  NEXT
	 */
	private function hooks(): void
	{
		add_action('init', [$this, 'register_post']);
		add_action('cmb2_admin_init', [$this, 'register_post_meta']);
	}


	/**
	 * Post Labels
	 *
	 * @since  NEXT
	 * @return array
	 */
	private function post_labels(): array
	{
		return [
			'name'                  => _x('Listings', 'Post type general name', 'Listing'),
			'singular_name'         => _x('Listing', 'Post type singular name', 'Listing'),
			'menu_name'             => _x('Listings', 'Admin Menu text', 'Listing'),
			'name_admin_bar'        => _x('Listing', 'Add New on Toolbar', 'Listing'),
			'add_new'               => __('Add New', 'Listing'),
			'add_new_item'          => __('Add New Listing', 'Listing'),
			'new_item'              => __('New Listing', 'Listing'),
			'edit_item'             => __('Edit Listing', 'Listing'),
			'view_item'             => __('View Listing', 'Listing'),
			'all_items'             => __('All Listings', 'Listing'),
			'search_items'          => __('Search Listings', 'Listing'),
			'parent_item_colon'     => __('Parent Listings:', 'Listing'),
			'not_found'             => __('No Listings found.', 'Listing'),
			'not_found_in_trash'    => __('No Listings found in Trash.', 'Listing'),
			'featured_image'        => _x('Listing Cover Image', 'Overrides the "Featured Image" phrase for this post type. Added in 4.3', 'Listing'),
			'set_featured_image'    => _x('Set cover image', 'Overrides the "Set featured image" phrase for this post type. Added in 4.3', 'Listing'),
			'remove_featured_image' => _x('Remove cover image', 'Overrides the "Remove featured image" phrase for this post type. Added in 4.3', 'Listing'),
			'use_featured_image'    => _x('Use as cover image', 'Overrides the "Use as featured image" phrase for this post type. Added in 4.3', 'Listing'),
			'archives'              => _x('Listing archives', 'The post type archive label used in nav menus. Default "Post Archives". Added in 4.4', 'Listing'),
			'insert_into_item'      => _x('Insert into Listing', 'Overrides the "Insert into post"/"Insert into page" phrase (used when inserting media into a post). Added in 4.4', 'Listing'),
			'uploaded_to_this_item' => _x('Uploaded to this Listing', 'Overrides the "Uploaded to this post"/"Uploaded to this page" phrase (used when viewing media attached to a post). Added in 4.4', 'Listing'),
			'filter_items_list'     => _x('Filter Listings list', 'Screen reader text for the filter links heading on the post type listing screen. Default "Filter posts list"/"Filter pages list". Added in 4.4', 'Listing'),
			'items_list_navigation' => _x('Listings list navigation', 'Screen reader text for the pagination heading on the post type listing screen. Default "Posts list navigation"/"Pages list navigation". Added in 4.4', 'Listing'),
			'items_list'            => _x('Listings list', 'Screen reader text for the items list heading on the post type listing screen. Default "Posts list"/"Pages list". Added in 4.4', 'Listing'),
		];
	}

	/**
	 * Post Arguments
	 *
	 * @since  NEXT
	 * @return array
	 */
	private function post_arguments(): array
	{
		return [
			'labels'             => $this->post_labels(),
			'description'        => 'Listing custom post type.',
			'public'             => true,
			'publicly_queryable' => true,
			'show_ui'            => true,
			'show_in_menu'       => true,
			'query_var'          => true,
			'rewrite'            => array('slug' => 'Listing'),
			'capability_type'    => 'post',
			'has_archive'        => true,
			'hierarchical'       => false,
			'menu_position'      => 20,
			'supports'           => array('title', 'editor', 'author', 'thumbnail'),
			'taxonomies'         => array('category', 'post_tag'),
			'show_in_rest'       => true,
		];
	}

	/**
	 * Register Post Type
	 *
	 * @since  NEXT
	 */
	public function register_post(): void
	{
		register_post_type($this->slug, $this->post_arguments());
	}

	/**
	 * Register Sermon Details Meta Fields
	 *
	 * @since  NEXT
	 * @return void
	 */
	public function register_post_meta(): void
	{
		$listing_details = new_cmb2_box(array(
			'id'            => 'ca_listing_details', // Custom ID of the box.
			'title'         => esc_html__('Listing Details', 'simple-real-estate'), // Title visible to the use.
			'object_types'  => array($this->slug),
		));

		$listing_details->add_field(array(
			'name'       => esc_html__('Street', 'simple-real-estate'),
			'desc'       => esc_html__('What is the street and unit number?', 'simple-real-estate'),
			'id'         => 'ca_street',
			'type'       => 'text',
		));

		$listing_details->add_field(array(
			'name'       => esc_html__('City', 'simple-real-estate'),
			'desc'       => esc_html__('What is the City?', 'simple-real-estate'),
			'id'         => 'ca_city',
			'type'       => 'text',
		));

		$listing_details->add_field(array(
			'name'       => esc_html__('Region', 'simple-real-estate'),
			'desc'       => esc_html__('What is the region?', 'simple-real-estate'),
			'id'         => 'ca_region',
			'type'       => 'text',
		));

		$listing_details->add_field(array(
			'name'       => esc_html__('Postal Code', 'simple-real-estate'),
			'desc'       => esc_html__('What is the postal code?', 'simple-real-estate'),
			'id'         => 'ca_postal_code',
			'type'       => 'text',
		));

		$listing_details->add_field(array(
			'name'       => esc_html__('Listing Price', 'simple-real-estate'),
			'desc'       => esc_html__('What is the asking price?', 'simple-real-estate'),
			'id'         => 'ca_listing_price',
			'type'       => 'text_money',
		));

		$listing_details->add_field(array(
			'name'           => 'Listing Agent',
			'desc'           => 'Who is the listing agent?',
			'id'             => 'ca_listing_agent',
			'taxonomy'       => 'ca_agents', //Enter Taxonomy Slug
			'type'           => 'taxonomy_select',
			'remove_default' => 'true', // Removes the default metabox provided by WP core.
		));

		$listing_details->add_field(array(
			'name' => 'Listing Photos',
			'desc' => 'The more the merrier!',
			'id'   => 'ca_listing_photos',
			'type' => 'file_list',
			'text' => array(
				'add_upload_files_text' => 'Listing Photos', // default: "Add or Upload Files"
				'remove_image_text' => 'Listing Photo', // default: "Remove Image"
				'file_text' => 'Listing Photos', // default: "File:"
				'file_download_text' => 'Listing Photo', // default: "Download"
				'remove_text' => 'Listing Photo', // default: "Remove"
			),
		));

		$group_field_id = $listing_details->add_field(array(
			'id'          => 'ca_listing_detail_group',
			'type'        => 'group',
			'description' => __('Generates reusable form entries', 'simple-real-estate'),
			'options'     => array(
				'group_title'       => __('Property Details'),
				'add_button'        => __('Add Additional Detail', 'simple-real-estate'),
				'remove_button'     => __('Remove Detail', 'simple-real-estate'),
				'sortable'          => true,
			),
		));

		$listing_details->add_group_field($group_field_id, array(
			'name' => __('Detail Title', 'simple-real-estate'),
			'id'   => 'ca_detail_title',
			'type' => 'text',
		));

		$listing_details->add_group_field($group_field_id, array(
			'name' => __('Detail Information', 'simple-real-estate'),
			'id'   => 'ca_detail_information',
			'type' => 'text',
		));
	}
}

new Listings();