How to seamlessly redirect between different archive and singular slugs?

I have a CPT with slug function (singular) and archive slug functions (plural).

Permalink structure for:

Read More
  • singular is /function/[postname]/
  • archive is /functions/[page-x/].

(can poke live at QueryPosts )

This works and looks fine, however I have logged some cases when people manipulate URL directly, like:

  • /functions/[postname] (typing name after archive page)
  • /function/ (erasing function name)

While this is not complicated to correct with some code explicitly, there would be more post types and it seems like data and logic are quite sufficient for generic solution (really I kinda expected WP to handle it, it does quite a lot to correct link issues).

However I have deep WP rewrite traumas and could use some pointers. 🙂

To sum it up:

How-To catch and redirect URLs that are correct, other than wrongly using archive slug instead of singular and vice versa?

Archive setup is registered like this:

'has_archive' => 'functions',
'rewrite'     => array(
    'feeds'      => false,
    'with_front' => false,
),

Related posts

Leave a Reply

2 comments

  1. Ok, I was probably overthinking it. In line with suggested algorithm coded out my handler to this so far:

    /**
     * Redirect manually edited URLs to proper pages.
     *
     * @param $template
     *
     * @return mixed
     */
    static function if_404_template( $template ) {
    
        $post_types     = get_post_types( array( 'has_archive' => true, ), 'objects' );
        $url_path       = @parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
        $url_path_parts = array_filter( explode( '/', $url_path ) );
    
        if ( ! empty( $url_path_parts ) ) {
            $last_url_path_part = end( $url_path_parts );
    
            foreach ( $post_types as $name => $post_type_object ) {
                if ( $name == $last_url_path_part ) {
                    wp_safe_redirect( get_post_type_archive_link( $name ), 301 );
                    die;
                }
    
                if ( count( $url_path_parts ) > 1 && in_array( $post_type_object->has_archive, $url_path_parts ) ) {
                    $post = get_page_by_title( $last_url_path_part, OBJECT, $name );
    
                    if ( ! empty( $post ) ) {
                        wp_safe_redirect( get_permalink( $post ), 301 );
                        die;
                    }
                }
            }
        }
    
        trigger_error( 'Uncaught 404 at ' . esc_html( $_SERVER['REQUEST_URI'] ) );
    
        return $template;
    }