From 266aa37c48222993bf7bdad6834e31bd08736f5e Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sat, 23 May 2026 21:22:07 +0000
Subject: [PATCH] =Post type archive meta title fix
---
inc/registrar/config/seo/Schema.php | 322 ++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 276 insertions(+), 46 deletions(-)
diff --git a/inc/registrar/config/seo/Schema.php b/inc/registrar/config/seo/Schema.php
index b48b8de..6b01794 100644
--- a/inc/registrar/config/seo/Schema.php
+++ b/inc/registrar/config/seo/Schema.php
@@ -1,6 +1,7 @@
<?php
namespace JVBase\registrar\config\seo;
+use JVBase\base\SchemaHelper;
use JVBase\managers\Cache;
use JVBase\managers\SEO\render;
use JVBase\meta\Meta;
@@ -35,7 +36,7 @@
];
protected array $defaultArchive = [
- 'type' => 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage',
+ 'type' => 'JVBase\inc\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage\CollectionPage',
'title' => '{{registrar.plural}}'
];
@@ -58,7 +59,7 @@
switch ($registrar->getType()) {
case 'term':
- $this->defaultSchema['type'] = 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage';
+ $this->defaultSchema['type'] = 'JVBase\inc\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage\CollectionPage';
break;
case 'user':
$this->defaultSchema['type'] = 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\ProfilePage';
@@ -79,7 +80,7 @@
$registrar = Registrar::getInstance($this->slug);
$this->defaultArchive = [
- 'type' => 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage',
+ 'type' => 'JVBase\inc\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage\CollectionPage',
'name' => $registrar->getPlural(),
'description' => $registrar->getDescription()
];
@@ -148,6 +149,7 @@
public function outputSchema():void
{
$registrar = Registrar::getInstance($this->slug);
+
if (is_singular()) {
$this->outputSingularSchema();
} elseif (is_post_type_archive(jvbCheckBase($this->slug) || is_tax(jvbCheckBase($this->slug)))) {
@@ -163,15 +165,25 @@
$this->cache->flush();
}
+ $registrar = Registrar::getInstance($this->slug);
+
return $this->cache->remember(
$ID,
- function () use ($ID) {
+ function () use ($ID, $registrar) {
$meta = Meta::forPost($ID);
+ if ($registrar->hasFeature('is_faq')) {
+ return $this->outputQASchema($ID, $meta);
+ }
+ if ($registrar->hasFeature('is_timeline')) {
+ return $this->outputTimelineSchema($ID, $meta);
+ }
+
$config = $this->getConfig();
- $class = $this->classFromConfig($config, $meta);
-
+ $class = JVB()->schemaHelper()::classFromConfig($config, $meta);
$class->setAuthor(JVB()->seo()->getCreator(true));
+
+ $class = apply_filters('jvb_single_'.$this->slug.'_schema_output', $class, $ID);
return $class->outputSchema();
}
);
@@ -188,15 +200,16 @@
$ID,
function() use ($ID) {
$action = BASE.ucfirst($this->slug).'Schema';
- $config = get_option($action, apply_filters($action, $this->defaultSchema));
+ $config = JVB()->schemaHelper()::schema($action);
+
if (!array_key_exists('type', $config)) {
- $config['type'] = 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage';
+ $config['type'] = 'JVBase\inc\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage\CollectionPage';
}
if (!class_exists($config['type'])) {
error_log('No class found for archive schema output: '.$config['type']);
return [];
}
- $class = $this->classFromConfig($config);
+ $class = JVB()->schemaHelper()::classFromConfig($config);
$class->setIsPartOf(get_home_url().'/#website');
$itemList = new render\Thing\Intangible\ItemList\ItemList();
@@ -235,18 +248,28 @@
$this->slug,
function() {
$action = BASE.ucfirst($this->slug).'Archive';
- $config = get_option($action, apply_filters($action, $this->defaultArchive));
+ $config = JVB()->schemaHelper()->archive($this->slug);
if (!array_key_exists('type', $config)) {
- $config['type'] = 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage';
+ $config['type'] = 'JVBase\inc\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage\CollectionPage';
}
if (!class_exists($config['type'])) {
error_log('No class found for archive schema output: '.$config['type']);
return [];
}
+
+ $registrar = Registrar::getInstance($this->slug);
+ if ($registrar->hasFeature('is_glossary')) {
+ return $this->outputGlossarySchema();
+ } else if ($registrar->hasFeature('is_faq')) {
+ return $this->outputFAQSchema();
+ }
+ if ($registrar->hasFeature('is_timeline')) {
+ return $this->outputTimelineArchiveSchema();
+ }
$obj = get_queried_object();
$meta = (property_exists($obj, 'taxonomy')) ? Meta::forTerm($obj->term_id) : null;
- $class = $this->classFromConfig($config, $meta);
+ $class = JVB()->schemaHelper()::classFromConfig($config, $meta);
$class->setIsPartOf(get_home_url().'/#website');
$itemList = new render\Thing\Intangible\ItemList\ItemList();
@@ -261,6 +284,7 @@
foreach ($items->posts as $ID) {
$item = $this->outputReferenceSchema($ID, 'post',false);
$listItem = new render\Thing\Intangible\ListItem();
+ $listItem->setId('listitem-'.$ID);
$listItem->setPosition($pos);
$listItem->setItem($item);
$itemListItems[] = $listItem;
@@ -271,7 +295,10 @@
$class->setMainEntity($itemList);
$schema = $class->outputSchema();
- error_log('Generated archive schema: '.print_r($schema, true));
+ if (JVB_TESTING) {
+// error_log('Generated archive schema: '.print_r($schema, true));
+ }
+
return $schema;
}
);
@@ -300,8 +327,8 @@
error_log('Invalid type used for reference: '.print_r($type, true));
$meta = null;
}
- $config = $this->getConfig('archive');
- $class = $this->classFromConfig($config, $meta);
+ $config = $this->getConfig();
+ $class = JVB()->schemaHelper()::classFromConfig($config, $meta);
$class->delete('about');
switch ($type) {
@@ -327,9 +354,29 @@
error_log('[SEO]Schema::getConfig Invalid type: '.$type);
return [];
}
- $action = BASE.ucfirst($this->slug).ucfirst($type);
- $default = 'default'.ucfirst($type);
- return get_option($action, apply_filters($action, $this->$default));
+ return JVB()->schemaHelper()::getConfig($this->slug, $type);
+ }
+
+ public function resolveMeta(array $config, Meta $meta):array
+ {
+ foreach ($config as $property => $value) {
+ if (is_array($value)) {
+ $config[$property] = $this->resolveMeta($value, $meta);
+ if ($property === 'additionalProperty' && (!array_key_exists('value', $config[$property]) || empty($config[$property]['value']))) {
+ unset($config[$property]);
+ }
+ }
+ if (is_string($value) && str_contains($value, '{{')) {
+ $value = Resolver::resolve($value, $meta);
+ if (empty($value)){
+ unset($config[$property]);
+ } else {
+ $config[$property] = $value;
+ }
+ }
+ }
+
+ return $config;
}
public function define(string $property, string $value):void
@@ -356,7 +403,7 @@
$config['type'] = $this->defaultSchema['type'];
update_option(BASE.ucfirst($this->slug).'Schema', $config);
}
- $class = $this->getConfig()['type'];
+ $class = $config['type'];
if (!class_exists($class)) {
error_log('[SEO]Schema::defineReference Class not found: '.$class);
return;
@@ -393,40 +440,223 @@
if (is_singular($based)){
$config = $this->getConfig('meta');
$meta = Meta::forPost(get_the_ID());
- $title = Resolver::resolve($config['name'], $meta);
- } elseif (is_post_type_archive($based) || is_tax($based)) {
+ $title = Resolver::resolve($config['name']??$config['title']??'', $meta);
+ } elseif (is_post_type_archive($based) ) {
$config = $this->getConfig('archive');
- $title = $config['name'];
+ $title = Resolver::resolve($config['name'], null);
+ } elseif (is_tax($based)) {
+ $config = $this->getConfig('archive');
+ $meta = Meta::forTerm(get_queried_object_id());
+ $title = Resolver::resolve($config['name'], $meta);
+ } else {
+ error_log('[Schema]::filterTSFOGTitle Unmatched condition: '.$this->slug);
}
return $title;
}
-
- protected function classFromConfig(array $config, ?Meta $meta = null):mixed
+ public function outputQASchema(int $ID, Meta $meta):array
{
- if (!array_key_exists('type', $config)) {
- error_log('[Schema]::classFromConfig No class defined in config: '.print_r($config, true));
- return false;
- }
- $className = $config['type'];
- unset($config['type']);
- $class = new $className();
+ $registrar = Registrar::getInstance($this->slug);
+ global $wp;
+ $current = get_home_url(null, $wp->request).'/';
+ $config = $this->getConfig();
- foreach ($config as $property=>$value) {
- if (is_array ($value)) {
- $value = $this->classFromConfig($value, $meta);
+ $config['type'] = 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\QAPage';
+ $page = SchemaHelper::classFromConfig($config, $meta);
+
+ $post = get_queried_object();
+ $question = [
+ 'id' => $current.'#question-'.$post->post_name,
+ 'type' => 'JVBase\inc\managers\SEO\render\Thing\CreativeWork\Comment\Question',
+ 'name' => $meta->get('post_title'),
+ 'acceptedAnswer' => [
+ 'id' => $current.'#answer-'.$post->post_name,
+ 'type' => 'JVBase\inc\managers\SEO\render\Thing\CreativeWork\Comment\Answer',
+ 'text' => wp_strip_all_tags(str_replace("\n", '', $meta->get('post_content'))),
+ ],
+ ];
+ $question = SchemaHelper::classFromConfig($question);
+ $page->setMainEntity($question);
+ $page->setAuthor(JVB()->seo()->getCreator(true));
+ return $page->outputSchema();
+
+ }
+ public function outputTimelineSchema(int $ID, Meta $meta):array
+ {
+ $registrar = Registrar::getInstance($this->slug);
+ global $wp;
+ $current = get_home_url(null, $wp->request).'/';
+ $config = $this->getConfig();
+
+ $config['type'] = 'JVBase\inc\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage\ImageGallery';
+ $this->resolveMeta($config, $meta);
+ $page = SchemaHelper::classFromConfig($config, $meta);
+
+ $post = get_queried_object();
+ $images = [];
+ $children = new WP_Query([
+ 'post_type'=> jvbCheckBase($this->slug),
+ 'post_status' => 'publish',
+ 'post_parent' => $post->ID,
+ ]);
+ $children = $children->posts;
+ array_unshift($children, $post);
+
+ foreach ($children as $index => $child) {
+ $meta = Meta::forPost($child->ID);
+
+ $image = render\Thing\Thing::createImageFromID($meta->get('post_thumbnail'));
+ $image->setId(($index === 0) ? '#before-treatment' : '#treatment-'.$index);
+ $image->setPosition($index+1);
+ $image->setName(($index === 0) ? 'Before Laser Tattoo Removal' : 'After '.$index.' Laser Tattoo Removal Treatments');
+ if (!empty ($meta->get('post_excerpt'))){
+ $image->setDescription($meta->get('post_excerpt'));
}
- $method = 'set'.ucfirst($property);
- if (!method_exists($class, $method)) {
- error_log('[Schema]::classFromConfig - method: '.$method.' does not exist in class: '.$className);
- continue;
- }
- if (is_string($value) && str_contains($value, '{{')) {
- $value = Resolver::resolveForSchema($property, $value, $config, $meta);
- }
- if (!empty($value)) {
- $class->$method($value);
- }
+
+ $images[] = $image;
}
- return $class;
+
+ $page->setImage($images);
+ $page->setAuthor(JVB()->seo()->getCreator(true));
+ return $page->outputSchema();
+
+ }
+
+ /*********************************************
+ * Archive Presets
+ *********************************************/
+ public function outputFAQSchema():array
+ {
+ $registrar = Registrar::getInstance($this->slug);
+ global $wp;
+ $current = get_home_url(null, $wp->request).'/';
+ $config = $this->getConfig('archive');
+ $page = [
+ 'id' => $current.'#'.$registrar->getSlug(),
+ 'type' => 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\FAQPage',
+ 'name' => array_key_exists('name', $config) ? $config['name'] : $registrar->getPlural(),
+ 'description' => array_key_exists('description', $config) ? $config['description'] : $registrar->getDescription(),
+ 'url' => $current,
+ ];
+
+ $page = SchemaHelper::classFromConfig($page);
+
+ $args = [
+ 'post_type' => $registrar->getBased(),
+ 'posts_per_page'=> -1,
+ 'post_status' => 'publish',
+ ];
+ $obj = get_queried_object();
+
+ if (property_exists($obj, 'taxonomy')) {
+ $page->setName('FAQ on '.$obj->name);
+
+ $args['post_type'] = array_map('jvbCheckBase', $registrar->registrar->for);
+
+ $args['tax_query'] = [
+ [
+ 'taxonomy' => $obj->taxonomy,
+ 'terms' => $obj->term_id,
+ ]
+ ];
+ }
+
+ $questions = [];
+ $posts = new WP_Query($args);
+ foreach ($posts->posts as $post) {
+ $meta = Meta::forPost($post->ID);
+ $question = [
+ 'id' => $current.'#question-'.$post->post_name,
+ 'type' => 'JVBase\inc\managers\SEO\render\Thing\CreativeWork\Comment\Question',
+ 'name' => $meta->get('post_title'),
+ 'acceptedAnswer' => [
+ 'id' => $current.'#answer-'.$post->post_name,
+ 'type' => 'JVBase\inc\managers\SEO\render\Thing\CreativeWork\Comment\Answer',
+ 'text' => $meta->get('post_excerpt'),
+ ],
+ 'url' => get_the_permalink($post->ID),
+ ];
+ $questions[] = SchemaHelper::classFromConfig($question);
+ }
+ wp_reset_postdata();
+ $page->setMainEntity($questions);
+
+ return $page->outputSchema();
+
+ }
+ public function outputTimelineArchiveSchema():array
+ {
+ $registrar = Registrar::getInstance($this->slug);
+ global $wp;
+ $current = get_home_url(null, $wp->request).'/';
+ $config = $this->getConfig('archive');
+ $page = array_merge($config, [
+ 'id' => $current.'#'.$registrar->getSlug(),
+ 'type' => 'JVBase\inc\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage\CollectionPage',
+ 'name' => array_key_exists('name', $config) ? $config['name'] : $registrar->getPlural(),
+ 'description' => array_key_exists('description', $config) ? $config['description'] : $registrar->getDescription(),
+ 'url' => $current,
+ ]);
+ $page = SchemaHelper::classFromConfig($page);
+
+ $parts = [];
+ $timelines = new WP_Query([
+ 'post_type' => $registrar->getBased(),
+ 'posts_per_page'=> 50,
+ 'post_status' => 'publish',
+ 'post_parent' => 0,
+ ]);
+ foreach ($timelines->posts as $post) {
+ $item = $this->outputReferenceSchema($post->ID, 'post', false);
+ $item->setId($current.'#'.$post->post_name);
+ $item->setName($post->post_title);
+ $item->setUrl(get_the_permalink($post->ID));
+ $parts[] = $item;
+ }
+
+ $page->setHasPart($parts);
+ return $page->outputSchema();
+ }
+ public function outputGlossarySchema():array
+ {
+ $registrar = Registrar::getInstance($this->slug);
+ global $wp;
+ $current = get_home_url(null, $wp->request).'/';
+ $config = $this->getConfig('archive');
+ $page = [
+ 'id' => $current.'#'.$registrar->getSlug(),
+ 'type' => 'JVBase\inc\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage\CollectionPage',
+ 'name' => array_key_exists('name', $config) ? $config['name'] : $registrar->getPlural(),
+ 'description' => array_key_exists('description', $config) ? $config['description'] : $registrar->getDescription(),
+ 'url' => $current,
+ ];
+ $page = SchemaHelper::classFromConfig($page);
+
+
+ //Defined Termset
+ $termset = [
+ 'type' => 'JVBase\managers\SEO\render\Thing\CreativeWork\DefinedTermSet',
+ 'id' => $current.'#definedtermset',
+ 'name' => $registrar->getPlural(),
+ 'description' => $registrar->getDescription(),
+ ];
+ $termset = SchemaHelper::classFromConfig($termset);
+
+ $terms = new WP_Query([
+ 'post_type' => $registrar->getBased(),
+ 'posts_per_page' => -1,
+ 'post_status' => 'publish',
+ ]);
+
+ $outputTerms = [];
+ foreach ($terms->posts as $post) {
+ $item = $this->outputReferenceSchema($post->ID, 'post', false);
+ $item->setId($current.'#'.$post->post_name);
+ $item->setName($post->post_title);
+ $outputTerms[] = $item;
+ }
+ $termset->setHasDefinedTerm($outputTerms);
+
+ $page->setMainEntity($termset);
+ return $page->outputSchema();
}
}
--
Gitblit v1.10.0