| | |
| | | '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) |
| | | { |
| | |
| | | $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(); |
| | |
| | | { |
| | | 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 |
| | |
| | | } |
| | | 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 = $this->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 outputContentTaxArchiveSchema():array |
| | | { |
| | | $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 = get_option($action, apply_filters($action, $this->defaultSchema)); |
| | | 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 = $this->classFromConfig($config); |
| | | |
| | | $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 |
| | | { |
| | | $this->archiveCache->flush(); |
| | | if (JVB_TESTING){ |
| | | $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)); |
| | | |
| | | 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 = new $config['type']; |
| | | unset($config['type']); |
| | | foreach ($config as $property=>$value) { |
| | | $method = 'set'.ucfirst($property); |
| | | $class->$method($value); |
| | | } |
| | | $class = $this->classFromConfig($config, $meta); |
| | | |
| | | $class->setIsPartOf(get_home_url().'/#website'); |
| | | $itemList = new render\Thing\Intangible\ItemList\ItemList(); |
| | | $items = new WP_Query([ |
| | |
| | | $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); |
| | |
| | | ); |
| | | } |
| | | |
| | | 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 = $this->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; |
| | | } |
| | | ); |
| | |
| | | } |
| | | 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; |
| | |
| | | $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) || is_tax($based)) { |
| | | $config = $this->getConfig('archive'); |
| | | $title = $config['name']; |
| | | } |
| | | return $title; |
| | | } |
| | | |
| | | protected function classFromConfig(array $config, ?Meta $meta = null):mixed |
| | | { |
| | | 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(); |
| | | |
| | | foreach ($config as $property=>$value) { |
| | | if (is_array ($value)) { |
| | | $value = $this->classFromConfig($value, $meta); |
| | | } |
| | | $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); |
| | | } |
| | | } |
| | | return $class; |
| | | } |
| | | } |