arie_cristianD

change permalink podcast post permalink structure with podcast series term

Mar 11th, 2026
170
0
Never
8
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 4.69 KB | None | 0 0
  1.  
  2. /**
  3.  * Filter the default permalink structure before WordPress replaces permalink tags.
  4.  *
  5.  * This hook is used to switch the permalink structure only for standard posts
  6.  * that have at least one term assigned in the `jnews-series` taxonomy.
  7.  *
  8.  * Example:
  9.  * Default structure  : /%category%/%postname%/
  10.  * Custom structure   : /podcast/%jnwes-series%/%postname%/
  11.  */
  12. add_filter(
  13.     'pre_post_link',
  14.     function ( $permalink, $post, $leavename ) {
  15.         if ( 'post' !== $post->post_type ) {
  16.             return $permalink;
  17.         }
  18.  
  19.         $series = wp_get_post_terms( $post->ID, 'jnews-series' );
  20.         $series = is_wp_error( $series ) ? '' : $series;
  21.         $series = is_array( $series ) ? ( ! empty( $series ) ? $series[0] : $series ) : $series;
  22.  
  23.         if ( ! empty( $series ) && ! is_wp_error( $series ) ) {
  24.             return '/podcast/%jnwes-series%/%postname%/';
  25.         }
  26.  
  27.         return $permalink;
  28.     },
  29.     10,
  30.     3
  31. );
  32.  
  33. /**
  34.  * Replace the custom permalink tag with the actual taxonomy term slug.
  35.  *
  36.  * After `pre_post_link` changes the permalink structure, this hook replaces
  37.  * `%jnwes-series%` with the real slug from the assigned `jnews-series` term.
  38.  *
  39.  * If no valid term is found, it falls back to `uncategorized`.
  40.  */
  41. add_filter(
  42.     'post_link',
  43.     function ( $permalink, $post, $leavename ) {
  44.         if ( 'post' !== $post->post_type ) {
  45.             return $permalink;
  46.         }
  47.  
  48.         if ( false === strpos( $permalink, 'podcast/%jnwes-series%' ) ) {
  49.             return $permalink;
  50.         }
  51.  
  52.         $series = wp_get_post_terms( $post->ID, 'jnews-series' );
  53.         $series = is_wp_error( $series ) ? '' : $series;
  54.         $series = is_array( $series ) ? ( ! empty( $series ) ? $series[0] : $series ) : $series;
  55.  
  56.         if ( empty( $series ) || is_wp_error( $series ) ) {
  57.             return str_replace( 'podcast/%jnwes-series%', 'uncategorized', $permalink );
  58.         }
  59.  
  60.         return str_replace( '%jnwes-series%', $series->slug, $permalink );
  61.     },
  62.     10,
  63.     3
  64. );
  65.  
  66. /**
  67.  * Register custom query variables used by the rewrite rule.
  68.  *
  69.  * `jnews_series_slug` is used to store the series term slug taken from the URL.
  70.  * `jnews_series_post_slug` is registered here for possible future use, even though
  71.  * it is not used in the current rewrite target.
  72.  */
  73. add_filter(
  74.     'query_vars',
  75.     function ( $vars ) {
  76.         $vars[] = 'jnews_series_slug';
  77.         $vars[] = 'jnews_series_post_slug';
  78.         return $vars;
  79.     }
  80. );
  81.  
  82. /**
  83.  * Add a custom rewrite rule for podcast series single posts.
  84.  *
  85.  * This rule maps URLs like:
  86.  * /podcast/series-slug/post-slug/
  87.  *
  88.  * Into query vars that WordPress can understand:
  89.  * - post_type=post
  90.  * - name=post-slug
  91.  * - jnews_series_slug=series-slug
  92.  *
  93.  * The rule is added at the top so it takes priority over more generic rules.
  94.  */
  95. add_action(
  96.     'init',
  97.     function () {
  98.         add_rewrite_rule(
  99.             '^podcast/([^/]+)/([^/]+)/?$',
  100.             'index.php?post_type=post&name=$matches[2]&jnews_series_slug=$matches[1]',
  101.             'top'
  102.         );
  103.     }
  104. );
  105.  
  106. /**
  107.  * Modify the main query so WordPress only loads posts that belong to
  108.  * the requested `jnews-series` term from the URL.
  109.  *
  110.  * Without this hook, WordPress may find a post by slug alone, even if the post
  111.  * does not belong to the requested series term.
  112.  *
  113.  * This ensures the URL:
  114.  * /podcast/series-slug/post-slug/
  115.  *
  116.  * Only resolves if `post-slug` is actually assigned to `series-slug`.
  117.  */
  118. add_action(
  119.     'pre_get_posts',
  120.     function ( $query ) {
  121.         if ( is_admin() || ! $query->is_main_query() ) {
  122.             return;
  123.         }
  124.  
  125.         $series_slug = $query->get( 'jnews_series_slug' );
  126.         $post_slug   = $query->get( 'name' );
  127.  
  128.         if ( empty( $series_slug ) || empty( $post_slug ) ) {
  129.             return;
  130.         }
  131.  
  132.         $query->set( 'post_type', 'post' );
  133.         $query->set(
  134.             'tax_query',
  135.             array(
  136.                 array(
  137.                     'taxonomy' => 'jnews-series',
  138.                     'field'    => 'slug',
  139.                     'terms'    => $series_slug,
  140.                 ),
  141.             )
  142.         );
  143.     }
  144. );
  145.  
  146. /**
  147.  * Redirect non-canonical series URLs to the correct permalink.
  148.  *
  149.  * This hook runs before the template is loaded and compares the current URL
  150.  * against the canonical permalink generated by WordPress.
  151.  *
  152.  * If the requested URL does not match the correct permalink, a 301 redirect
  153.  * is performed to avoid duplicate URLs and keep the permalink consistent.
  154.  */
  155. add_action(
  156.     'template_redirect',
  157.     function () {
  158.         if ( ! is_single() ) {
  159.             return;
  160.         }
  161.  
  162.         $post = get_queried_object();
  163.  
  164.         if ( ! $post instanceof WP_Post || 'post' !== $post->post_type ) {
  165.             return;
  166.         }
  167.  
  168.         $requested_series = get_query_var( 'jnews_series_slug' );
  169.  
  170.         if ( empty( $requested_series ) ) {
  171.             return;
  172.         }
  173.  
  174.         $canonical = get_permalink( $post );
  175.         $current   = home_url( trailingslashit( $GLOBALS['wp']->request ) );
  176.  
  177.         if ( untrailingslashit( $canonical ) !== untrailingslashit( $current ) ) {
  178.             wp_safe_redirect( $canonical, 301 );
  179.             exit;
  180.         }
  181.     }
  182. );
  183.  
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
Add Comment
Please, Sign In to add comment