feat(routing): region/city/product URLs with 301s from old paths
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ final class VC_Plugin {
|
||||
VC_Taxonomies::init();
|
||||
VC_Settings::init();
|
||||
VC_Structure_Admin::init();
|
||||
VC_Routing::init();
|
||||
VC_Cron::init();
|
||||
VC_Display::init();
|
||||
VC_Sections::init();
|
||||
@@ -37,6 +38,7 @@ final class VC_Plugin {
|
||||
'includes/class-vc-cron.php',
|
||||
'includes/class-vc-settings.php',
|
||||
'includes/class-vc-structure-admin.php',
|
||||
'includes/class-vc-routing.php',
|
||||
'includes/class-vc-display.php',
|
||||
'includes/class-vc-sections.php',
|
||||
);
|
||||
|
||||
139
viator-collector/includes/class-vc-routing.php
Normal file
139
viator-collector/includes/class-vc-routing.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Place-based URLs from the Viator Structure: /{region}/, /{city}/ and
|
||||
* /{city}/{tour}/. Old /tour-destination/… and /tours/… addresses 301 to
|
||||
* the new ones, and a tour opened under the wrong city 301s to its one
|
||||
* canonical city.
|
||||
*/
|
||||
class VC_Routing {
|
||||
private static $product_city = array();
|
||||
|
||||
public static function init() {
|
||||
// After the CPT/taxonomies register (default priority 10).
|
||||
add_action( 'init', array( __CLASS__, 'add_rules' ), 20 );
|
||||
add_filter( 'term_link', array( __CLASS__, 'term_link' ), 10, 3 );
|
||||
add_filter( 'post_type_link', array( __CLASS__, 'post_type_link' ), 10, 2 );
|
||||
// Ahead of core's redirect_canonical (priority 10).
|
||||
add_action( 'template_redirect', array( __CLASS__, 'redirect' ), 5 );
|
||||
add_action( 'wp_head', array( __CLASS__, 'term_canonical' ), 1 );
|
||||
}
|
||||
|
||||
public static function add_rules() {
|
||||
foreach ( VC_Structure::rewrite_rules( VC_Structure::get() ) as $regex => $query ) {
|
||||
add_rewrite_rule( $regex, $query, 'top' );
|
||||
}
|
||||
if ( get_option( 'vc_flush_rewrite' ) ) {
|
||||
delete_option( 'vc_flush_rewrite' );
|
||||
flush_rewrite_rules( false );
|
||||
}
|
||||
}
|
||||
|
||||
public static function is_configured_term( $term ) {
|
||||
if ( ! $term instanceof WP_Term || 'viator_destination' !== $term->taxonomy ) {
|
||||
return false;
|
||||
}
|
||||
if ( get_term_meta( $term->term_id, '_vc_region', true ) ) {
|
||||
return true;
|
||||
}
|
||||
$viator_id = (string) get_term_meta( $term->term_id, '_vc_viator_destination_id', true );
|
||||
if ( '' === $viator_id ) {
|
||||
return false;
|
||||
}
|
||||
$cities = VC_Structure::cities( VC_Structure::get() );
|
||||
return isset( $cities[ $viator_id ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Slug of the city a tour's URL lives under, or '' (it stays at /tours/).
|
||||
*/
|
||||
public static function product_city( $post_id ) {
|
||||
$post_id = (int) $post_id;
|
||||
if ( isset( self::$product_city[ $post_id ] ) ) {
|
||||
return self::$product_city[ $post_id ];
|
||||
}
|
||||
$dest_ids = array();
|
||||
$destinations = json_decode( (string) get_post_meta( $post_id, '_vc_destinations', true ), true );
|
||||
foreach ( is_array( $destinations ) ? $destinations : array() as $destination ) {
|
||||
$ref = is_array( $destination ) ? ( $destination['ref'] ?? ( $destination['destinationId'] ?? '' ) ) : $destination;
|
||||
if ( '' !== (string) $ref ) {
|
||||
$dest_ids[] = (string) $ref;
|
||||
}
|
||||
}
|
||||
$primary = (string) get_post_meta( $post_id, '_vc_primary_destination', true );
|
||||
|
||||
self::$product_city[ $post_id ] = VC_Structure::canonical_city( VC_Structure::get(), $dest_ids, $primary );
|
||||
return self::$product_city[ $post_id ];
|
||||
}
|
||||
|
||||
public static function term_link( $url, $term, $taxonomy ) {
|
||||
if ( 'viator_destination' === $taxonomy && self::is_configured_term( $term ) ) {
|
||||
return home_url( '/' . $term->slug . '/' );
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
public static function post_type_link( $url, $post ) {
|
||||
if ( 'viator_tour' !== $post->post_type || '' === $post->post_name ) {
|
||||
return $url;
|
||||
}
|
||||
$city = self::product_city( $post->ID );
|
||||
if ( '' === $city ) {
|
||||
return $url;
|
||||
}
|
||||
return home_url( '/' . $city . '/' . $post->post_name . '/' );
|
||||
}
|
||||
|
||||
public static function redirect() {
|
||||
if ( is_preview() ) {
|
||||
return;
|
||||
}
|
||||
$canonical = '';
|
||||
if ( is_singular( 'viator_tour' ) ) {
|
||||
$post = get_queried_object();
|
||||
if ( $post && 'publish' === $post->post_status ) {
|
||||
$canonical = get_permalink( $post );
|
||||
}
|
||||
} elseif ( is_tax( 'viator_destination' ) ) {
|
||||
$term = get_queried_object();
|
||||
if ( self::is_configured_term( $term ) ) {
|
||||
$canonical = get_term_link( $term );
|
||||
}
|
||||
}
|
||||
if ( ! $canonical || is_wp_error( $canonical ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$request = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
$parts = explode( '?', $request, 2 );
|
||||
$current = untrailingslashit( rawurldecode( $parts[0] ) );
|
||||
$target = untrailingslashit( rawurldecode( (string) wp_parse_url( $canonical, PHP_URL_PATH ) ) );
|
||||
if ( $current === $target ) {
|
||||
return;
|
||||
}
|
||||
if ( isset( $parts[1] ) && '' !== $parts[1] ) {
|
||||
$canonical .= '?' . $parts[1];
|
||||
}
|
||||
wp_safe_redirect( $canonical, 301 );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Core prints rel=canonical for single tours only; add it for region and
|
||||
* city archives unless an SEO plugin already handles canonicals.
|
||||
*/
|
||||
public static function term_canonical() {
|
||||
if ( defined( 'WPSEO_VERSION' ) || defined( 'RANK_MATH_VERSION' ) || ! is_tax( 'viator_destination' ) ) {
|
||||
return;
|
||||
}
|
||||
$term = get_queried_object();
|
||||
if ( ! self::is_configured_term( $term ) ) {
|
||||
return;
|
||||
}
|
||||
$link = get_term_link( $term );
|
||||
if ( ! is_wp_error( $link ) ) {
|
||||
echo '<link rel="canonical" href="' . esc_url( $link ) . '">' . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user