Jake Vanderwerf
2026-05-31 d7e7d248cbe41cd7a9ef9c2fb022b6c4831f99a3
inc/registrar/Posts.php
@@ -1,6 +1,8 @@
<?php
namespace JVBase\registrar;
use JVBase\forms\TaxonomySelector;
if (!defined('ABSPATH')) {
   exit;
}
@@ -73,7 +75,7 @@
    * Set this to true for the post type to be available in the block editor.
    * @var bool
    */
   public bool $show_in_rest;
   public bool $show_in_rest = true;
   /**
    * To change the base URL of REST API route. Default is $post_type.
    * @var string
@@ -208,9 +210,12 @@
    * @var string|false
    */
   public string|false $template_lock;
   protected string $unbased;
   protected ?string $taxonomyRewrite = null;
   public function __construct(string $postType, string $singular, string $plural)
   {
      $this->unbased = jvbNoBase($postType);
      $this->postType = jvbCheckBase($postType);
      $this->labels = $this->buildLabels($singular, $plural);
   }
@@ -312,6 +317,29 @@
      unset ($args['postType']);
//    error_log('Registering PostType '.$this->postType.', with args: '.print_r($args, true));
      $registrar = Registrar::getInstance($this->postType);
      $rewrite = $args['rewrite']['slug']??'';
      if ($registrar) {
         $hasSlug = array_key_exists('rewrite', $args) && array_key_exists('slug', $args['rewrite']);
         if ($registrar->rewrite_taxonomy && !str_contains($rewrite, '%')) {
            if (!$hasSlug && !array_key_exists('rewrite', $args)) {
               $args['rewrite'] = [];
            }
            $tax = is_array($registrar->rewrite_taxonomy) ? $registrar->rewrite_taxonomy[array_key_first($registrar->rewrite_taxonomy)] : $registrar->rewrite_taxonomy;
            $args['rewrite']['slug'] = $hasSlug ? $rewrite."/%{$tax}%" : $this->unbased."/%{$tax}%";
         }
         if ($registrar->hasFeature('is_calendar')) {
            if (!$hasSlug && !array_key_exists('rewrite', $args)) {
               $args['rewrite'] = [];
            }
            $args['rewrite']['slug'] = $hasSlug ? $rewrite."/%eyear%/%emonth%/%eday%" : $this->unbased."/%eyear%/%emonth%/%eday%";
         }
      }
      $registered = register_post_type($this->postType, $args);
      if (is_wp_error($registered)) {
         JVB()->error()->log('JVBase\registrar\Posts', 'Could not register post type', $registered->get_error_messages());
@@ -337,4 +365,94 @@
         'not_found_in_trash' => "No {$plural} found in Trash.",
      ];
   }
   public function addTaxonomyRewrite(string $taxonomy):void
   {
      $exists = Registrar::getInstance($taxonomy);
      if (!$exists) return;
      $this->taxonomyRewrite = $taxonomy;
      if (!isset($this->rewrite)) {
         $this->rewrite = [];
      }
      if (array_key_exists('slug', $this->rewrite)) {
         if (str_contains($this->rewrite['slug'], '%')) {
            return;
         }
         $this->rewrite['slug'] = $this->rewrite['slug'].'/%'.$taxonomy.'%';
      } else {
         $this->rewrite['slug'] = $this->unbased.'/%'.$taxonomy.'%';
      }
      $this->addTaxonomyRewriteRules();
      add_action('post_type_link', [$this, 'rewriteTaxonomySingle'], 15, 2);
      add_action('post_type_archive_link', [$this, 'rewriteTaxonomyArchive'], 15, 2);
   }
   public function addTaxonomyRewriteRules(): void
   {
      if (!$this->taxonomyRewrite) return;
      $tax = jvbCheckBase($this->taxonomyRewrite);
      // Rule 1: Post type archive - /faq/
      add_rewrite_rule(
         "{$this->unbased}/?$",
         "index.php?post_type={$this->postType}",
         'top'
      );
      // Rule 2: Single posts with taxonomy - /faq/section/post/
      add_rewrite_rule(
         "{$this->unbased}/([^/]+)/([^/]+)/?$",
         "index.php?post_type={$this->postType}&name=\$matches[2]&{$tax}=\$matches[1]",
         'top'
      );
      // Rule 3: Un-sectioned posts - /faq/post/
      // Use 'bottom' priority so taxonomy rules match first
      add_rewrite_rule(
         "{$this->unbased}/([^/]+)/?$",
         "index.php?post_type={$this->postType}&name=\$matches[1]",
         'bottom'
      );
   }
   /**
    * Set $this->rewrite_taxonomy to a valid taxonomy
    * @param string $url
    * @param \WP_Post $post
    * @return string
    */
   public function rewriteTaxonomySingle(string $url, \WP_Post $post): string
   {
      if ($post->post_type === $this->postType && !is_null($this->taxonomyRewrite)) {
         $type = $this->taxonomyRewrite;
         $taxonomy = jvbCheckBase($type);
         $terms = wp_get_post_terms($post->ID, $taxonomy);
         if (!empty($terms) && !is_wp_error($terms)) {
            $path = TaxonomySelector::getTermPath($terms[0], true);
            $path = implode('/', array_map(function($term) {
               return sanitize_title($term);
            }, $path));
            return str_replace("%{$type}%", $path, $url);
         }
         return str_replace("/%{$type}%", '', $url);
      }
      return $url;
   }
   /**
    * Set $this->rewrite_taxonomy to a valid taxonomy
    * @param string $url
    * @param string $post_type
    * @return string
    */
   public function rewriteTaxonomyArchive(string $url, string $post_type):string
   {
      if ($post_type === $this->postType && !is_null($this->taxonomyRewrite)) {
         $url = get_home_url(null, "/{$this->postType}/");
      }
      return $url;
   }
}