id = sanitize_key($id);
$this->taxonomy = jvbCheckBase($taxonomy);
$this->name = str_replace(BASE, '', $taxonomy);
$this->icon = new JVBIcons();
$this->cache = new CacheManager('taxonomy');
$this->base = $config['base'] ?? '';
$this->config = wp_parse_args($config, [
'name' => $id,
'multiple' => true,
'max_selections'=> 0,
'hierarchical' => false,
'search' => true,
'createNew' => false,
'taxonomy' => $this->taxonomy,
'required' => false,
'group_by_family'=> false,
'placeholder' => '',
'show_examples' => false,
'show_breadcrumbs'=> true,
'expand_siblings'=> true,
'show_popularity'=> false,
'no_results' => 'Nothing here.',
'base' => $this->base,
// 'association' => array(),
'common' => array(),
'types' => '', //for feed block implementation
'hidden' => false,
'label' => '',
'renderTemplates' => true,
]);
add_action('wp_footer', [$this, 'outputDialog']);
$tax = get_taxonomy($this->taxonomy);
$this->plural = JVB_TAXONOMY[$taxonomy]['plural'];
$this->taxonomyName = JVB_TAXONOMY[$taxonomy]['singular'];
}
/**
* Render selector component
* @param array $selected
*
* @return string
*/
public function render(array $selected = []):string
{
$this->selected = $selected;
$wrapper_classes = $this->getWrapperClasses();
ob_start();
?>
renderSelectedItems($selected); ?>
selected = $selected;
$wrapper_classes = $this->getWrapperClasses();
$icon = new JVBIcons();
ob_start();
?>
config['hidden'] ?>>
get_param('query'));
$page = (int)$request->get_param('page') ?: 1;
$per_page = 10;
$args = [
'post_type' => BASE.'artist',
'posts_per_page' => $per_page,
'paged' => $page,
'orderby' => 'title',
'order' => 'ASC',
'post_status' => 'publish',
's' => $query
];
// If shop_id provided, exclude artists already in shop
$shop_id = $request->get_param('shop_id');
if ($shop_id) {
$existing_artists = get_posts([
'post_type' => BASE.'artist',
'posts_per_page' => -1,
'fields' => 'ids',
'tax_query' => [[
'taxonomy' => BASE.'shop',
'terms' => $shop_id
]]
]);
if (!empty($existing_artists)) {
$args['post__not_in'] = $existing_artists;
}
}
$key = $this->cache->generateKey($args);
$cache = $this->cache->get($key);
if ($cache) {
return new WP_REST_Response($cache);
}
$posts = get_posts($args);
$results = [];
foreach ($posts as $post) {
$city_terms = wp_get_object_terms($post->ID, BASE.'city');
$city = !empty($city_terms) ? $city_terms[0]->name : '';
$results[] = [
'id' => $post->ID,
'title' => $post->post_title,
'thumbnail' => get_the_post_thumbnail_url($post->ID, 'thumbnail'),
'city' => $city,
'url' => get_permalink($post->ID)
];
}
$result = [
'results' => $results,
'hasMore' => count($posts) === $per_page
];
$this->cache->set($key, $result);
return new WP_REST_Response($result);
}
/**
* Get wrapper CSS classes
* @return string
*/
protected function getWrapperClasses():string
{
$classes = [
'jvb-selector',
'selector-' . strtolower($this->name)
];
if ($this->config['multiple']) {
$classes[] = 'multiple';
}
if ($this->config['hierarchical']) {
$classes[] = 'hierarchical';
}
if ($this->config['required']) {
$classes[] = 'required';
}
return implode(' ', $classes);
}
/**
* Get configuration for frontend JavaScript
* @return array
*/
protected function getFrontendConfig(array $selected = []):array
{
$out = [
// 'name' => $this->config['name'],
'multiple' => $this->config['multiple'],
'maxSelections' => $this->config['max_selections'],
// 'hierarchical' => $this->config['hierarchical'],
'search' => $this->config['search'],
'createNew' => (bool)$this->config['createNew'],
'required' => $this->config['required'],
// 'placeholder' => $this->config['placeholder'],
'noResults' => $this->config['no_results'],
// 'group_by_family' => $this->config['group_by_family'],
// 'labels' => $this->getLabels(),
'base' => $this->config['base'],
'selected' => $this->getSelectedData($selected),
// 'values' => $this->getAvailableTerms(),
// 'hierarchy' => [],
// 'breadcrumbs' => [],
// 'association' => $this->config['association']
// 'common' => $this->config['common']
];
if ($this->config['hierarchical']) {
$out['hierarchy'] = $this->getTermHierarchy();
$out['breadcrumbs'] = $this->getBreadcrumbData();
}
return $out;
}
/**
* Get theme hierarchy data
* @param int $parent ID of parent
*
* @return array
*/
protected function getTermHierarchy(int $parent = 0):array
{
$terms = get_terms([
'taxonomy' => $this->taxonomy,
'parent' => $parent,
'hide_empty' => false
]);
if (is_wp_error($terms)) {
return [];
}
$hierarchy = [];
foreach ($terms as $term) {
$children = $this->getTermHierarchy($term->term_id);
$hierarchy[] = [
'id' => $term->term_id,
'name' => $term->name,
'parent' => $term->parent,
'children' => $children
];
}
return $hierarchy;
}
/**
* Get theme breadcrumb data
* @return array
*/
protected function getBreadcrumbData():array
{
$breadcrumbs = [];
foreach ($this->selected as $term_id => $title) {
$breadcrumbs[$term_id] = $this->getTermAncestors($term_id);
}
return $breadcrumbs;
}
/**
* Get term ancestors with names
* @param int $term_id
*
* @return array
*/
protected function getTermAncestors(int $term_id):array
{
$cache = $this->cache->get('term-ancestors-'.$term_id);
if ($cache) {
return $cache;
}
$ancestors = get_ancestors($term_id, $this->taxonomy, 'taxonomy');
$path = [];
foreach (array_reverse($ancestors) as $ancestor_id) {
$term = get_term($ancestor_id, $this->taxonomy);
if ($term && !is_wp_error($term)) {
$path[] = [
'id' => $term->term_id,
'name' => $term->name
];
}
}
$this->cache->set('term-ancestors-'.$term_id, $path);
return $path;
}
/**
* @return array
*/
protected function getAvailableTerms():array
{
$cache = $this->cache->get($this->taxonomy . '-terms');
if ($cache) {
return $cache;
}
$terms = get_terms([
'taxonomy' => $this->taxonomy,
'hide_empty' => false,
'fields' => 'id=>name'
]);
$result = is_wp_error($terms) ? [] : $terms;
$this->cache->set($this->taxonomy . '-terms', $result);
return $result;
}
/**
* Get text labels
* @return array
*/
protected function getLabels():array
{
return [
'search' => __('Search...', 'jvb'),
'select' => __('Select', 'jvb'),
'selected' => __('Selected', 'jvb'),
'remove' => __('Remove', 'jvb'),
'clear' => __('Clear', 'jvb'),
'createNew' => __('Create New', 'jvb'),
'loading' => __('Loading...', 'jvb'),
'saving' => __('Saving...', 'jvb'),
'parent_themes' => __('Parent Themes', 'jvb'),
'sub_themes' => __('Sub-themes', 'jvb'),
'related_themes' => __('Related Themes', 'jvb'),
'popular_combinations' => __('Popular Combinations', 'jvb'),
'view_examples' => __('View Examples', 'jvb'),
'back_to_parent' => __('Back to Parent', 'jvb')
];
}
/**
* Render selected items
* @return void
*/
protected function renderSelectedItems(array $selected = []):void
{
if (empty($selected) && empty($this->selected)) {
echo '';
return;
}
$selected = empty($selected) ? $this->selected : $selected;
$out = '';
foreach ($selected as $ID) {
$term = get_term((int)$ID, $this->taxonomy);
if (!$term || is_wp_error($term)) {
continue;
}
$out .= $this->renderSelectedItem($term);
}
$out .= '
';
echo $out;
}
/**
* Render single selected item
* @param WP_Term $term
*
* @return string
*/
protected function renderSelectedItem(WP_Term $term):string
{
return '
'.$term->name.'
';
}
/**
* Render modal content
* @return void
*/
protected function renderModalContent():void
{
?>
config['common'])) : ?>
Your Go Tos:
{ loading items }
config['search'] || $this->config['createNew']) : ?>
config['search']) :
echo jvbSearch('Search '.$this->plural.'...');
endif; ?>
config['createNew']) : ?>
Add new = $this->taxonomyName ?>
|
= (jvbSiteVerifiesUsers()) ? '
Not finding what you\'re looking for?
Suggest a new '.strtolower($this->taxonomyName).'.
' : '' ?>
config)): ?>
{ loading items }
{ nothing found }
taxonomy);
return sprintf(
__('Select %s', 'jvb'),
$tax_obj ? $tax_obj->labels->name : __('Items', 'jvb')
);
}
/**
* Get popularity label
* @param int $count
*
* @return string
*/
protected function getPopularityLabel(int $count):string
{
switch (true) {
case $count > 100:
return __('Very Popular', 'jvb');
case $count > 50:
return __('Popular', 'jvb');
case $count > 10:
return __('Common', 'jvb');
default:
return __('Unique', 'jvb');
}
}
/**
* Render single selectable item
* @param WP_Term $term
* @param bool $selected
*
* @return void
*/
protected function renderSelectableItem(WP_Term $term, bool $selected = false):void
{
?>
>
$this->get_term_examples($term->term_id),
// 'popular_combinations' => $this->get_term_combinations($term->term_id),
// 'related_themes' => carbon_get_term_meta($term->term_id, 'jvb_related_themes') ?: []
// ];
// }
/**
* Get theme examples
*/
// protected function get_term_examples($term_id, $limit = 4) {
// $examples = [];
// $posts = get_posts([
// 'post_type' => "e_{$this->context}",
// 'posts_per_page' => $limit,
// 'tax_query' => [[
// 'taxonomy' => $this->taxonomy,
// 'terms' => $term_id
// ]],
// 'orderby' => 'rand'
// ]);
//
// foreach ($posts as $post) {
// if (has_post_thumbnail($post)) {
// $examples[] = [
// 'title' => get_the_title($post),
// 'thumbnail' => get_the_post_thumbnail_url($post, 'thumbnail'),
// 'full' => get_the_post_thumbnail_url($post, 'full')
// ];
// }
// }
//
// return $examples;
// }
/**
* Render hidden inputs for form submission
* @return void
*/
protected function renderHiddenInputs():void
{
if (empty($this->selected)) {
return;
}
foreach ($this->selected as $term_id => $name) {
printf(
'',
esc_attr($this->id),
esc_attr($term_id)
);
}
}
/**
* Get selected terms data for frontend
* @return array
*/
protected function getSelectedData(array $selected = []):array
{
$data = [];
$selected = (empty($selected)) ? $this->selected : $selected;
foreach ($selected as $ID) {
$term = get_term((int) $ID, $this->taxonomy);
if (!$term || is_wp_error($term)) {
continue;
}
$data[] = [
'id' => $term->term_id,
'name' => $term->name,
'parent' => $term->parent
];
}
return $data;
}
/**
* @var array Loading state messages
*/
protected $loading_quips = [
"Making you look good...",
"Processing perfection...",
"Converting ink to pixels...",
"Teaching robots about art..."
];
/**
* Get loading state HTML
* @return string
*/
protected function getLoadingState():string
{
ob_start();
?>
= esc_html($this->getRandomQuip()); ?>
loading_quips[array_rand($this->loading_quips)];
}
public function outputDialog() {
?>