Skip to content

Component Meta

alex_prokopenko edited this page Jan 19, 2018 · 2 revisions

Almost every WordPress site use some custom fields plugin to display extra information for Pages, Posts or Custom Post Types. For example you create a Property details page on real estate site, so you need to print a lot of specific fields from postmeta table.

Standard WordPress functions require to specify a lot of parameters. You need to remember all your field names. You can't pre-process data outside the template and need to keep some logic inside the template.

Another case that you need to create temporary variables to check if meta value is empty and then print it.

We suggest a better way to work with custom fields (post- or term- meta). We have ready-to-use classes to get data from custom fields created by Just Custom Fields or Advanced Custom Fields plugins. We have both classes to work with Postmeta and Termmeta.

Postmeta and Termmeta objects

Postmeta and Termmeta classes has code to get meta values from JCF/ACF plugins in easy way with internal caching. In the instructions we will share the code for both objects in parallel, because usage is similar.

For a start you need to create an instance of required meta object:

<?php
// if you need to get meta from Posts
$postmeta = new \JustCoded\WP\Framework\Objects\Postmeta();

// if you need to get meta from Taxonomy term
$termmeta = new \JustCoded\WP\Framework\Objects\Termmeta();

By default these objects initialized with current loaded page/post or taxonomy term. If you print some data inside a loop you can use such methods to reset object ID:

<?php
// set new post ID from get_the_ID()
$postmeta->the_post();

// set new post ID manually
$postmeta->the_post( $post_id );

// set new term ID to get data from
$termmeta->the_term( $term_id );

That's all, now you can get meta values by accessing them as instance properties. Imagine that we have meta data for home page with keys subheading (simple text) and banner (image). And we also added fields subheading and featured_image for standard Categories taxonomy. To get values you need to access instance property, furthermore you can use it inside if() statement without !empty() and it won't generate any notices:

<!-- safe print of post meta values -->
<?php if ($postmeta->subheading) echo '<h2>' . esc_html($postmeta->subheading) . '</h2>'; ?>
<?php if ($postmeta->banner) echo wp_get_attachment_image($postmeta->banner, 'large'); ?>

<!-- safe print of term meta values -->
<?php if ($termmeta->subheading) echo '<h2>' . esc_html($meta->subheading) . '</h2>'; ?>
<?php if ($termmeta->featured_image) echo wp_get_attachment_image($meta->featured_image, 'large'); ?>

Now you ROCK!

Template examples

front-page.php

<?php
$this->extends( 'layouts/main' );

$meta = new \JustCoded\WP\Framework\Objects\Postmeta();
?>

<?php while ( have_posts() ) : the_post(); $meta->the_post(); ?>

	<?php the_title( '<h1 class="entry-title">', '</h1>' ); ?>
	<h2><?php echo esc_html($meta->subheading); ?></h2> 

    <?php if ($meta->banner) : ?>
        <?php echo wp_get_attachment_image($meta->banner, 'large'); ?>
    <?php endif; ?>
    
    ...

<?php endwhile; ?>

category.php

<?php
$this->extends( 'layouts/main' );
$termmeta = new \JustCoded\WP\Framework\Objects\Termmeta();
?>

	<?php if ( have_posts() ) : ?>

		<header class="page-header">
			<?php
				the_archive_title( '<h1 class="page-title category-title">', '</h1>' );
				the_archive_description( '<div class="taxonomy-description">', '</div>' );
			?>
			<?php if ($termmeta->subheading) echo '<h2>' . esc_html($meta->subheading) . '</h2>'; ?>
			<?php if ($termmeta->featured_image) echo wp_get_attachment_image($meta->featured_image, 'large'); ?>

		</header><!-- .page-header -->

		<?php /* Start the Loop */ ?>
		<?php while ( have_posts() ) : the_post(); ?>

			<?php $this->include( 'post/_content' ); ?>

		<?php endwhile; ?>

	    ...

	<?php endif; ?>

How it works

These classes define magic __get() method, which is called when you try to access non-existing class property. Inside this method we call generic method get_value(), which calls get_value_jcf() or get_value_acf() methods and cache the real value returned.

So actually to get some meta value you can use code like this:

echp esc_html( $postmeta->get_value('subheading') ); ?>

Advanced usage

Describe complex pages structure

When you have complex pages with a lot of fields we suggest to create custom classes to describe the structure of this post type and write some pre-processing.

Let's create Property_Meta class:

<?php
namespace Boilerplate\Theme\Models;

/**
 * Property Meta class describes the structure of Property object and have some helpers
 * 
 * @property string $location
 * @property array  $features
 * @property float  $price_per_week
 * @property float  $price_per_month
 */
class Property_Meta extends \JustCoded\WP\Framework\Objects\Postmeta {
	public function get_price() {
		if ( $this->price_per_week ) {
			return money_format( '%i', $this->price_per_week ) . ' per week';
		} elseif ( $this->price_per_month ) {
			return money_format( '%i', $this->price_per_month ) . ' per week';
		} else {
			return 'rented';
		}
	}
}

Now if you create an object of such class, IDE (PHPStorm or Netbeans) will suggest you field names automatically, so it will be much easier to edit your templates. This is possible because of smart PHPDoc comment added before the class declaration starts.

 * @property string $location
 * @property string $location
 * @property array  $features
 * @property float  $price_per_week
 * @property float  $price_per_month

Such statements helps IDE to suggest non-existed class properties inside and outside the class methods and templates.

Furthermore, we can have some data preprocessing, like we have several price options and we can write a helper here, which will print the price which is really filled in or some default value.

Default values

One more example is to add default values to meta keys, which are empty. Of course you can add them inside the template, but if you need to print values in several places - you will need to duplicate your code.

Let's modify our Property_Meta to add default values:

<?php
namespace Boilerplate\Theme\Models;

/**
 * Property Meta class describes the structure of Property object and have some helpers
 * 
 * @property string $location
 * @property array  $features
 * @property float  $price_per_week
 * @property float  $price_per_month
 */
class Property_Meta extends \JustCoded\WP\Framework\Objects\Postmeta {
	public $defaults = array(
        'location' => 'London',
        'features' => 'TV, Wifi, Furtniture',    
    );
	
	public function get_value( $field_name, $object_id = null, $format_value = true ) {
		$value = parent::get_value($field_name, $object_id, $format_value);
		if ( empty($value) && isset( $this->defaults[ $field_name ] ) ) {
			$value = $this->defaults[ $field_name ]; 
		}
		return $value;
	}
}

What have we done:

  • We added a new property with default values
  • We over-write generic method to get any property value get_value()
  • We still use the whole code to get_value as we usually do with $value = parent::get_value(...)
  • At the end we check empty value and that we defined default and replace it