Jake Vanderwerf
2026-03-29 9f672be1f7bb5f8462374ca4732d095d4f24685b
inc/registrar/config/seo/Schema.php
@@ -34,7 +34,10 @@
      'description' => '{{post_excerpt}}',
   ];
   protected array $defaultArchive = [];
   protected array $defaultArchive = [
      'type'   => 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage',
      'title' => '{{registrar.plural}}'
   ];
   public function __construct(string $slug, string $type)
   {
@@ -52,6 +55,16 @@
         $this->cache->connect($registrar->getType());
         $this->referenceCache->connect($registrar->getType());
         $this->archiveCache->connect($registrar->getType());
         switch ($registrar->getType()) {
            case 'term':
               $this->defaultSchema['type'] = 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage';
               break;
            case 'user':
               $this->defaultSchema['type'] = 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\ProfilePage';
               break;
         }
         $this->defaultArchive['description'] = '{{registrar.'.$slug.'.description}}';
      }
      $this->initFilters();
      $this->registerHooks();
@@ -75,6 +88,8 @@
   {
      add_action('wp_head', [$this, 'outputSchema'], 1);
      add_filter('the_seo_framework_schema_graph_data', [$this, 'filterTSFSchema'], 10, 2);
      add_filter('the_seo_framework_title_from_custom_field', [$this, 'filterTSFOGTitle'], 10, 2);
      $this->maybeExcludeSingles();
   }
      protected function maybeExcludeSingles(): void
@@ -119,67 +134,121 @@
      }
   public function filterTSFSchema(array $graph, ?array $args):array
   {
      if (jvbTSFDoIt($this->slug, $args)){
      $based = jvbCheckBase($this->slug);
      if (is_front_page() || is_singular($based) || is_post_type_archive($based) || is_tax($based)) {
         return [];
      }
//    if (jvbTSFDoIt($this->slug, $args)){
//       return [];
//    }
      return $graph;
   }
   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)))) {
         $this->outputArchiveSchema();
      } if ($registrar && $registrar->hasFeature('is_content') && is_single(get_option(BASE.$this->slug.'_archive'))) {
         $this->outputContentTaxArchiveSchema();
      }
   }
      public function outputSingularSchema():array
      {
         $ID = get_the_ID();
         $this->cache->flush();
         if (JVB_TESTING){
            $this->cache->flush();
         }
         return $this->cache->remember(
            $ID,
            function () use ($ID) {
               $meta = Meta::forPost($ID);
               $config = $this->getConfig();
               $class = new $config['type']();
               unset($config['type']);
               foreach ($config as $property => $value){
                  $value = Resolver::resolveForSchema($property, $value, $config, $meta);
                  $method = 'set'.ucfirst($property);
                  $class->$method($value);
               }
               $class = JVB()->schemaHelper()::classFromConfig($config, $meta);
               $class->setAuthor(JVB()->seo()->getCreator(true));
               $schema = $class->outputSchema();
               error_log('Generated archive schema: '.print_r($schema, true));
               return $schema;
               return $class->outputSchema();
            }
         );
      }
      public function outputArchiveSchema():array
      public function outputContentTaxArchiveSchema():array
      {
         $this->archiveCache->flush();
         return $this->archiveCache->remember(
            $this->slug,
            function() {
               $action = BASE.ucfirst($this->slug).'Archive';
               $config = get_option($action, apply_filters($action, $this->defaultArchive));
         $ID = get_the_ID();
         if (JVB_TESTING) {
            $this->cache->flush();
         }
         return $this->cache->remember(
            $ID,
            function() use ($ID) {
               $action = BASE.ucfirst($this->slug).'Schema';
               $config = JVB()->schemaHelper()::schema($action);
               if (!array_key_exists('type', $config)) {
                  $config['type'] = 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage';
               }
               if (!class_exists($config['type'])) {
                  error_log('No class found for archive schema output: '.$config['type']);
                  return [];
               }
               $class = JVB()->schemaHelper()::classFromConfig($config);
               $class = new $config['type'];
               unset($config['type']);
               foreach ($config as $property=>$value) {
                  $method = 'set'.ucfirst($property);
                  $class->$method($value);
               $class->setIsPartOf(get_home_url().'/#website');
               $itemList = new render\Thing\Intangible\ItemList\ItemList();
               $items = get_terms([
                  'taxonomy'  => jvbCheckBase($this->slug),
//                'hide_empty' => true,
                  'fields' => 'ids',
               ]);
               $pos = 1;
               $itemListItems = [];
               foreach ($items as $ID) {
                  $item = $this->outputReferenceSchema($ID, 'term',false);
                  $listItem = new render\Thing\Intangible\ListItem();
                  $listItem->setPosition($pos);
                  $listItem->setItem($item);
                  $itemListItems[] = $listItem;
                  $pos++;
               }
               wp_reset_postdata();
               $itemList->setItemListElement($itemListItems);
               $class->setMainEntity($itemList);
               return $class->outputSchema();
            }
         );
      }
      public function outputArchiveSchema():array
      {
         if (JVB_TESTING){
            $this->archiveCache->flush();
         }
         return $this->archiveCache->remember(
            $this->slug,
            function() {
               $action = BASE.ucfirst($this->slug).'Archive';
               $config = JVB()->schemaHelper()->archive($this->slug);
               if (!array_key_exists('type', $config)) {
                  $config['type'] = 'JVBase\managers\SEO\render\Thing\CreativeWork\WebPage\CollectionPage';
               }
               if (!class_exists($config['type'])) {
                  error_log('No class found for archive schema output: '.$config['type']);
                  return [];
               }
               $obj = get_queried_object();
               $meta = (property_exists($obj, 'taxonomy')) ? Meta::forTerm($obj->term_id) : null;
               $class = JVB()->schemaHelper()::classFromConfig($config, $meta);
               $class->setIsPartOf(get_home_url().'/#website');
               $itemList = new render\Thing\Intangible\ItemList\ItemList();
               $items = new WP_Query([
@@ -191,7 +260,7 @@
               $pos = 1;
               $itemListItems = [];
               foreach ($items->posts as $ID) {
                  $item = $this->outputReferenceSchema($ID, false);
                  $item = $this->outputReferenceSchema($ID, 'post',false);
                  $listItem = new render\Thing\Intangible\ListItem();
                  $listItem->setPosition($pos);
                  $listItem->setItem($item);
@@ -209,24 +278,43 @@
         );
      }
   public function outputReferenceSchema(int $ID, bool $outputSchema = true):mixed
   public function outputReferenceSchema(int $ID, string $type, bool $outputSchema = true):mixed
   {
      $this->referenceCache->flush();
      if (JVB_TESTING){
         $this->referenceCache->flush();
      }
      $cached = $this->referenceCache->remember(
         $ID,
         function () use ($ID) {
            $meta = Meta::forPost($ID);
            $config = $this->getConfig();
            $class = new $config['type']();
            $class->setId(get_the_permalink($ID).'/#'.$class->getTypeName());
            foreach ($this->referenceProperties as $property => $value){
               $value = Resolver::resolveForSchema($property, $value, $this->schema, $meta);
               $method = 'set'.ucfirst($property);
               $class->$method($value);
         function () use ($ID, $type) {
            switch ($type) {
               case 'post':
                  $meta = Meta::forPost($ID);
                  break;
               case 'term':
                  $meta = Meta::forTerm($ID);
                  break;
               case 'user':
                  $meta = Meta::forUser($ID);
                  break;
               default:
                  error_log('Invalid type used for reference: '.print_r($type, true));
                  $meta = null;
            }
            $config = $this->getConfig('archive');
            $class = JVB()->schemaHelper()::classFromConfig($config, $meta);
            $class->delete('about');
            switch ($type) {
               case 'post':
                  $class->setId(get_the_permalink($ID).'#'.$class->getTypeName());
                  break;
               case 'term':
                  $class->setId(get_term_link($ID).'#'.$class->getTypeName());
                  break;
            }
            $schema = $class->outputSchema();
            error_log('Generated archive schema: '.print_r($schema, true));
            return $class;
         }
      );
@@ -240,9 +328,7 @@
         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 define(string $property, string $value):void
@@ -264,7 +350,12 @@
   }
   public function defineReference(string $property, string $value):void
   {
      $class = $this->getConfig('schema')['type'];
      $config = $this->getConfig();
      if (!array_key_exists('type', $config)) {
         $config['type'] = $this->defaultSchema['type'];
         update_option(BASE.ucfirst($this->slug).'Schema', $config);
      }
      $class = $this->getConfig()['type'];
      if (!class_exists($class)) {
         error_log('[SEO]Schema::defineReference Class not found: '.$class);
         return;
@@ -294,4 +385,24 @@
         $this->defineReference($property, $value);
      }
   }
   public function filterTSFOGTitle(string $title, ?array $args):string{
      $based = jvbCheckBase($this->slug);
      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) ) {
         $config = $this->getConfig('archive');
         $title = $config['name'];
      } 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;
   }
}