<?php
|
namespace JVBase\meta;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
/**
|
* Static utility for rendering meta values on frontend
|
*
|
* Usage:
|
* echo Render::render('price', 150, ['type' => 'number', 'prefix' => '$']);
|
* echo Render::render('gallery', $images, ['type' => 'gallery']);
|
*/
|
class Render
|
{
|
/**
|
* Render a field value based on type
|
*/
|
public static function render(string $name, mixed $value, array $config = []): string
|
{
|
if (!apply_filters('jvbShouldRenderMeta', true, $name, $config['type'] ?? 'text', null)) {
|
return '';
|
}
|
|
$type = $config['type'] ?? 'text';
|
$method = 'render' . str_replace('_', '', ucwords($type, '_'));
|
|
$output = method_exists(static::class, $method)
|
? static::$method($name, $value, $config)
|
: static::renderDefault($name, $value, $config);
|
|
return apply_filters('jvbRenderFrontendMeta', $output, $name, $config, $value, null);
|
}
|
|
/**
|
* Render with Meta instance (convenience method)
|
*/
|
public static function renderFrom(Meta $meta, string $name, bool $hideEmpty = true): string
|
{
|
$value = $meta->get($name);
|
$config = $meta->config($name) ?? ['type' => 'text'];
|
|
$output = static::render($name, $value, $config);
|
|
if (empty($output) && !$hideEmpty) {
|
return static::getEmptyTemplate($config['type'] ?? 'text', $name);
|
}
|
|
return $output;
|
}
|
|
// ─────────────────────────────────────────────────────────────
|
// Type Renderers
|
// ─────────────────────────────────────────────────────────────
|
|
protected static function renderDefault(string $name, mixed $value, array $config): string
|
{
|
if (empty($value)) {
|
return '';
|
}
|
return sprintf('<span class="%s">%s</span>', esc_attr($name), esc_html($value));
|
}
|
|
protected static function renderText(string $name, mixed $value, array $config): string
|
{
|
if (empty($value)) {
|
return '';
|
}
|
return sprintf('<span class="%s">%s</span>', esc_attr($name), esc_html($value));
|
}
|
|
protected static function renderTextarea(string $name, mixed $value, array $config): string
|
{
|
if (empty($value)) {
|
return '';
|
}
|
return sprintf('<div class="%s">%s</div>', esc_attr($name), wp_kses_post(wpautop($value)));
|
}
|
|
protected static function renderNumber(string $name, mixed $value, array $config): string
|
{
|
if ($value === '' || $value === null) {
|
return '';
|
}
|
|
$prefix = $config['prefix'] ?? '';
|
$suffix = $config['suffix'] ?? '';
|
$decimals = $config['decimals'] ?? 0;
|
|
return sprintf(
|
'<span class="%s">%s%s%s</span>',
|
esc_attr($name),
|
esc_html($prefix),
|
esc_html(number_format((float)$value, $decimals)),
|
esc_html($suffix)
|
);
|
}
|
|
protected static function renderEmail(string $name, mixed $value, array $config): string
|
{
|
if (empty($value)) {
|
return '';
|
}
|
return sprintf(
|
'<a class="%s" href="mailto:%s">%s%s</a>',
|
esc_attr($name),
|
esc_attr($value),
|
jvbIcon('envelope'),
|
esc_html($value)
|
);
|
}
|
|
protected static function renderUrl(string $name, mixed $value, array $config): string
|
{
|
if (empty($value)) {
|
return '';
|
}
|
|
$label = $config['label'] ?? parse_url($value, PHP_URL_HOST) ?? $value;
|
|
return sprintf(
|
'<a class="%s" href="%s" target="_blank" rel="noopener">%s%s</a>',
|
esc_attr($name),
|
esc_url($value),
|
jvbIcon('link'),
|
esc_html($label)
|
);
|
}
|
|
protected static function renderDate(string $name, mixed $value, array $config): string
|
{
|
if (empty($value)) {
|
return '';
|
}
|
|
$format = $config['format'] ?? get_option('date_format');
|
$timestamp = strtotime($value);
|
|
if (!$timestamp) {
|
return '';
|
}
|
|
return sprintf(
|
'<time class="%s" datetime="%s">%s%s</time>',
|
esc_attr($name),
|
esc_attr(date('Y-m-d', $timestamp)),
|
jvbIcon('calendar'),
|
esc_html(date_i18n($format, $timestamp))
|
);
|
}
|
|
protected static function renderTime(string $name, mixed $value, array $config): string
|
{
|
if (empty($value)) {
|
return '';
|
}
|
|
$format = $config['format'] ?? get_option('time_format');
|
$timestamp = strtotime($value);
|
|
if (!$timestamp) {
|
return '';
|
}
|
|
return sprintf(
|
'<time class="%s" datetime="%s">%s%s</time>',
|
esc_attr($name),
|
esc_attr(date('H:i', $timestamp)),
|
jvbIcon('clock'),
|
esc_html(date_i18n($format, $timestamp))
|
);
|
}
|
|
protected static function renderDatetime(string $name, mixed $value, array $config): string
|
{
|
if (empty($value)) {
|
return '';
|
}
|
|
$format = $config['format'] ?? get_option('date_format') . ' ' . get_option('time_format');
|
$timestamp = strtotime($value);
|
|
if (!$timestamp) {
|
return '';
|
}
|
|
return sprintf(
|
'<time class="%s" datetime="%s">%s%s</time>',
|
esc_attr($name),
|
esc_attr(date('Y-m-d\TH:i:s', $timestamp)),
|
jvbIcon('calendar'),
|
esc_html(date_i18n($format, $timestamp))
|
);
|
}
|
|
protected static function renderTrueFalse(string $name, mixed $value, array $config): string
|
{
|
$isTrue = filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
$labels = $config['labels'] ?? ['Yes', 'No'];
|
|
return sprintf(
|
'<span class="%s bool-%s">%s</span>',
|
esc_attr($name),
|
$isTrue ? 'true' : 'false',
|
esc_html($isTrue ? $labels[0] : $labels[1])
|
);
|
}
|
|
protected static function renderImage(string $name, mixed $value, array $config): string
|
{
|
if (empty($value)) {
|
return '';
|
}
|
|
$size = $config['size'] ?? 'medium';
|
$image = wp_get_attachment_image($value, $size, false, [
|
'class' => "{$name} attachment-{$size}"
|
]);
|
|
return $image ?: '';
|
}
|
|
protected static function renderGallery(string $name, mixed $value, array $config): string
|
{
|
if (empty($value)) {
|
return '';
|
}
|
|
$ids = is_array($value) ? $value : explode(',', $value);
|
$ids = array_filter(array_map('intval', $ids));
|
|
if (empty($ids)) {
|
return '';
|
}
|
|
$size = $config['size'] ?? 'thumbnail';
|
$output = sprintf('<div class="%s gallery">', esc_attr($name));
|
|
foreach ($ids as $id) {
|
$image = wp_get_attachment_image($id, $size);
|
if ($image) {
|
$output .= $image;
|
}
|
}
|
|
$output .= '</div>';
|
return $output;
|
}
|
|
protected static function renderTaxonomy(string $name, mixed $value, array $config): string
|
{
|
if (empty($value)) {
|
return '';
|
}
|
|
$ids = is_array($value) ? $value : explode(',', $value);
|
$ids = array_filter(array_map('intval', $ids));
|
|
if (empty($ids)) {
|
return '';
|
}
|
|
$terms = [];
|
foreach ($ids as $id) {
|
$term = get_term($id);
|
if ($term && !is_wp_error($term)) {
|
$link = get_term_link($term);
|
$terms[] = sprintf(
|
'<a href="%s">%s</a>',
|
esc_url($link),
|
esc_html($term->name)
|
);
|
}
|
}
|
|
return sprintf(
|
'<span class="%s taxonomy">%s</span>',
|
esc_attr($name),
|
implode(', ', $terms)
|
);
|
}
|
|
protected static function renderUser(string $name, mixed $value, array $config): string
|
{
|
if (empty($value)) {
|
return '';
|
}
|
|
$ids = is_array($value) ? $value : explode(',', $value);
|
$ids = array_filter(array_map('intval', $ids));
|
|
if (empty($ids)) {
|
return '';
|
}
|
|
$users = [];
|
foreach ($ids as $id) {
|
$user = get_userdata($id);
|
if ($user) {
|
$users[] = esc_html($user->display_name);
|
}
|
}
|
|
return sprintf(
|
'<span class="%s user">%s</span>',
|
esc_attr($name),
|
implode(', ', $users)
|
);
|
}
|
|
protected static function renderSelect(string $name, mixed $value, array $config): string
|
{
|
if (empty($value) || empty($config['options'])) {
|
return '';
|
}
|
|
$label = $config['options'][$value] ?? $value;
|
|
return sprintf(
|
'<span class="%s">%s</span>',
|
esc_attr($name),
|
esc_html($label)
|
);
|
}
|
|
protected static function renderSet(string $name, mixed $value, array $config): string
|
{
|
if (empty($value) || empty($config['options'])) {
|
return '';
|
}
|
|
$values = is_array($value) ? $value : explode(',', $value);
|
$labels = [];
|
|
foreach ($values as $v) {
|
$v = trim($v);
|
if (isset($config['options'][$v])) {
|
$labels[] = $config['options'][$v];
|
}
|
}
|
|
if (empty($labels)) {
|
return '';
|
}
|
|
return sprintf(
|
'<span class="%s">%s</span>',
|
esc_attr($name),
|
esc_html(implode(', ', $labels))
|
);
|
}
|
|
protected static function renderLocation(string $name, mixed $value, array $config): string
|
{
|
if (empty($value) || empty($value['address'])) {
|
return '';
|
}
|
|
return sprintf(
|
'<address class="%s">%s%s</address>',
|
esc_attr($name),
|
jvbIcon('map-pin'),
|
esc_html($value['address'])
|
);
|
}
|
|
protected static function renderRepeater(string $name, mixed $value, array $config): string
|
{
|
if (empty($value) || !is_array($value)) {
|
return '';
|
}
|
|
$fields = $config['fields'] ?? [];
|
$output = sprintf('<div class="%s repeater">', esc_attr($name));
|
|
foreach ($value as $index => $row) {
|
$output .= '<div class="repeater-row">';
|
foreach ($row as $fieldName => $fieldValue) {
|
$fieldConfig = $fields[$fieldName] ?? ['type' => 'text'];
|
$output .= static::render($fieldName, $fieldValue, $fieldConfig);
|
}
|
$output .= '</div>';
|
}
|
|
$output .= '</div>';
|
return $output;
|
}
|
|
protected static function renderGroup(string $name, mixed $value, array $config): string
|
{
|
if (empty($value) || !is_array($value)) {
|
return '';
|
}
|
|
$fields = $config['fields'] ?? [];
|
$output = sprintf('<div class="%s group">', esc_attr($name));
|
|
foreach ($value as $fieldName => $fieldValue) {
|
$fieldConfig = $fields[$fieldName] ?? ['type' => 'text'];
|
$output .= static::render($fieldName, $fieldValue, $fieldConfig);
|
}
|
|
$output .= '</div>';
|
return $output;
|
}
|
|
// ─────────────────────────────────────────────────────────────
|
// Empty Templates
|
// ─────────────────────────────────────────────────────────────
|
|
public static function getEmptyTemplate(string $type, string $name): string
|
{
|
$template = match ($type) {
|
'text', 'textarea', 'number' => '<p class="' . esc_attr($name) . '"></p>',
|
'url', 'email' => '<a class="' . esc_attr($name) . '">' . jvbIcon('link') . '</a>',
|
'set', 'checkbox', 'radio', 'taxonomy', 'user' => '<span class="' . esc_attr($name) . '"></span>',
|
'image', 'gallery' => '<div class="' . esc_attr($name) . ' images"><img/></div>',
|
'date' => '<p class="' . esc_attr($name) . '">' . jvbIcon('calendar') . '<span></span></p>',
|
'time' => '<p class="' . esc_attr($name) . '">' . jvbIcon('clock') . '<time></time></p>',
|
'true_false' => '<p class="' . esc_attr($name) . '"></p>',
|
default => ''
|
};
|
|
return apply_filters('jvbMetaTypeTemplate', $template, $type);
|
}
|
}
|