Last active
January 10, 2024 03:25
-
-
Save doiftrue/6b3f03e15a424aba2ea7829abfac1af9 to your computer and use it in GitHub Desktop.
[wp-kama embed] WordPress: Friendly URLs for a Dual Multi-level Taxonomy >>> https://wp-kama.com/1326
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
new Same_Prefix_Post_Taxes_Rewrite(); | |
/** | |
* How it works. | |
* | |
* We have: | |
* - ``company`` — Flat post type. | |
* - ``catalog`` — Hierarchical taxonomy. | |
* - ``location`` — Hierarchical taxonomy. | |
* | |
* We need permalinks: | |
* ``/company/`` prefix for all! Combined queryes for the two taxes. Working pagination! | |
* Examples: | |
* - ``/company/bmw7/`` — post. | |
* - ``/company/almaty/`` — location term archive (top level). | |
* - ``/company/almaty/auezo/`` — location term archive (level 2). | |
* - ``/company/almaty/auezo/three/`` — location term archive (level 3). | |
* - ``/company/avto/`` — catalog term archive (top level). | |
* - ``/company/avto/light/`` — catalog term archive (level 2). | |
* - ``/company/avto/light/lyuks/`` — catalog term archive (level 3). | |
* - ``/company/avto/almaty/`` — catalog + location terms archive (top levels). | |
* - ``/company/avto/almaty/auezo/`` — catalog + location terms archive (level 1 + level 2). | |
* - ``/company/avto/light/almaty/auezo/three/`` — catalog + location terms archive (level 2 + level 3). | |
* | |
* All of these must support pagination. Ex: | |
* - ``/company/almaty/page/2/`` — location term pagination page 2. | |
* - ``/company/avto/light/almaty/auezo/three/page/2/`` — catalog + location terms page 2. | |
* - etc. | |
* | |
* For posts we need additional permalinks. Ex: | |
* - ``site.kz/company/bmw7/reviews/`` — post. | |
* - ``site.kz/company/bmw7/gallery/`` — post. | |
* - ``site.kz/company/bmw7/edit/`` — post. | |
* - ``site.kz/company/bmw7/price/`` — post. | |
* | |
* @version 1.4 | |
* | |
* @author Kama (wp-kama.com) | |
*/ | |
class Same_Prefix_Post_Taxes_Rewrite { | |
static $post_type = 'company'; | |
static $catalog_tax = 'catalog'; | |
static $location_tax = 'location'; | |
static $post_subpages = [ | |
'edit' => 'Редактирование', | |
'price' => 'Цена', | |
'gallery' => 'Галлерея', | |
'reviews' => 'Обзоры', | |
]; | |
static $request = []; | |
function __construct(){ | |
add_action( 'init', [ __CLASS__, 'register_post_types' ] ); | |
add_action( 'request', [ __CLASS__, 'fix_request' ] ); | |
add_filter( self::$post_type . '_rewrite_rules', [ __CLASS__, 'post_rewrite_rules' ] ); | |
add_filter( 'saved_' . self::$location_tax, [ __CLASS__, '_location_top_slugs_refresh_cache' ] ); | |
add_filter( 'delete_' . self::$location_tax, [ __CLASS__, '_location_top_slugs_refresh_cache' ] ); | |
add_filter( 'pre_term_link', [ __CLASS__, 'make_term_link' ], 10, 2 ); | |
add_filter( 'query_vars', static function( $vars ){ | |
$vars[] = 'catalog_location'; | |
$vars[] = 'post_subpage'; | |
return $vars; | |
} ); | |
add_filter( 'get_canonical_url', [ __CLASS__, 'subpages_canonical' ], 10, 2 ); | |
add_filter( 'single'.'_template_hierarchy', [ __CLASS__, 'subpages_template_hierarchy' ] ); | |
isset( $_GET['rwdebug'] ) && add_action( 'wp', [ __CLASS__, '_debug' ] ); // debug | |
} | |
static function is_multi_tax_query(){ | |
return count( self::$request ) > 1; | |
} | |
static function register_post_types(){ | |
register_taxonomy( self::$catalog_tax, [ self::$post_type ], [ | |
'label' => '', // определяется параметром $labels->name | |
'labels' => [ | |
'name' => 'Каталог', | |
'singular_name' => 'Каталог', | |
], | |
'public' => true, | |
'hierarchical' => true, | |
'rewrite' => false, //[ 'slug'=>self::$post_type, 'hierarchical'=>true, 'with_front'=>false, 'feed'=>false ], | |
'show_admin_column' => false, | |
] ); | |
register_taxonomy( self::$location_tax, [ self::$post_type ], [ | |
'label' => '', // определяется параметром $labels->name | |
'labels' => [ | |
'name' => 'Локация', | |
'singular_name' => 'Локация', | |
], | |
'public' => true, | |
'hierarchical' => true, | |
'rewrite' => false, //[ 'slug'=>self::$post_type, 'hierarchical'=>true, 'with_front'=>false, 'feed'=>false ], | |
'show_admin_column' => false, | |
] ); | |
register_post_type( self::$post_type, [ | |
'label' => null, | |
'labels' => [ | |
'name' => 'Компании', // основное название для типа записи | |
'singular_name' => 'Компания', // название для одной записи этого типа | |
], | |
'public' => true, | |
'menu_position' => null, | |
'menu_icon' => null, | |
'hierarchical' => false, | |
'supports' => [ 'title', 'editor' ], | |
// 'title','editor','author','thumbnail','excerpt','trackbacks','custom-fields','comments','revisions','page-attributes','post-formats' | |
'taxonomies' => [ self::$catalog_tax, self::$location_tax ], | |
'has_archive' => true, | |
'rewrite' => [ 'with_front'=>false, 'feeds'=>false, 'endpoints'=>false ], | |
'query_var' => true, | |
] ); | |
} | |
static function fix_request( $vars ){ | |
// post request like catalog|location | |
if( ! empty( $vars['name'] ) && self::$post_type === $vars['post_type'] ){ | |
$name = $vars['name']; | |
$location_top = self::_location_top_slugs(); | |
$unset_vars__fn = static function( & $vars ){ | |
unset( $vars['post_type'], $vars['name'], $vars[ self::$post_type ] ); // clear | |
}; | |
// location | |
if( false !== strpos( $location_top, "|$name|" ) ){ | |
$unset_vars__fn( $vars ); // clear | |
$vars[ self::$location_tax ] = $name; | |
} | |
// catalog | |
elseif( $term = get_term_by( 'slug', $name, self::$catalog_tax ) ){ | |
$unset_vars__fn( $vars ); // clear | |
$vars[ self::$catalog_tax ] = $name; | |
} | |
} | |
// special catalog_location request | |
elseif( ! empty( $vars['catalog_location'] ) ){ | |
$parts = explode( '/', $vars['catalog_location'] ); | |
// maybe company post with post_subpage | |
if( | |
count( $parts ) === 2 | |
&& isset( self::$post_subpages[ $parts[1] ] ) | |
&& $post = get_page_by_path( $parts[0], OBJECT, [ self::$post_type ] ) | |
){ | |
$vars[ self::$post_type ] = $parts[0]; | |
$vars['post_type'] = self::$post_type; | |
$vars['name'] = $parts[0]; | |
$vars['post_subpage'] = $parts[1]; | |
} | |
// catalog_location request | |
else { | |
$location_top = self::_location_top_slugs(); | |
$catalogs = $locations = []; | |
$lnk = & $catalogs; | |
foreach( $parts as $part ){ | |
if( false !== strpos( $location_top, "|$part|" ) ) | |
$lnk = & $locations; | |
$lnk[] = $part; | |
} | |
unset( $lnk ); | |
$catalog_slug = end( $catalogs ); | |
$location_slug = end( $locations ); | |
// for check reliability of the URL to redirect to right one | |
$build_catalog = $catalog_slug ? self::_build_tax_uri( $catalog_slug, self::$catalog_tax ) : ''; | |
$build_location = $location_slug ? self::_build_tax_uri( $location_slug, self::$location_tax ) : ''; | |
// two taxs | |
if( $catalog_slug && $location_slug ){ | |
// 301 redirect to correct URL | |
if( | |
( implode( '/', $catalogs ) !== $build_catalog ) | |
|| | |
( implode( '/', $locations ) !== $build_location ) | |
){ | |
wp_redirect( user_trailingslashit( '/'. self::$post_type ."/$build_catalog/$build_location" ), 301 ); | |
exit; | |
} | |
self::$request['catalog'] = get_term_by( 'slug', $catalog_slug, self::$catalog_tax ); | |
self::$request['location'] = get_term_by( 'slug', $location_slug, self::$location_tax ); | |
$vars[ self::$catalog_tax ] = $catalog_slug; | |
$vars[ self::$location_tax ] = $location_slug; | |
} | |
// catalog | |
elseif( $catalog_slug ){ | |
// 301 redirect to correct URL | |
if( implode( '/', $catalogs ) !== $build_catalog ){ | |
$url = get_term_link( $catalog_slug, self::$catalog_tax ); | |
if( ! is_wp_error( $url ) ){ | |
wp_redirect( $url, 301 ); | |
exit; | |
} | |
} | |
self::$request['catalog'] = get_term_by( 'slug', $catalog_slug, self::$catalog_tax ); | |
$vars[ self::$catalog_tax ] = $catalog_slug; | |
} | |
// location | |
elseif( $location_slug ){ | |
// 301 redirect to correct URL | |
if( implode( '/', $locations ) !== $build_location ){ | |
wp_redirect( get_term_link( $location_slug, self::$location_tax ), 301 ); | |
exit; | |
} | |
self::$request['location'] = get_term_by( 'slug', $location_slug, self::$location_tax ); | |
$vars[ self::$location_tax ] = $location_slug; | |
} | |
} | |
unset( $vars['catalog_location'] ); | |
} | |
return $vars; | |
} | |
static function post_rewrite_rules( $rules ){ | |
$_first_part = self::$post_type . "/(.+?)"; | |
$_page_part = 'page/?([0-9]{1,})'; | |
$more_riles = [ | |
"$_first_part/$_page_part/?$" => 'index.php?catalog_location=$matches[1]&paged=$matches[2]', | |
"$_first_part/?$" => 'index.php?catalog_location=$matches[1]', | |
]; | |
// delete conflict attachment rules | |
foreach( $rules as $regex => $rule ){ | |
if( false === strpos( $regex, '/attachment/' ) && false !== strpos( $rule, 'attachment=' ) ) | |
unset( $rules[ $regex ] ); | |
} | |
$rules += $more_riles; | |
return $rules; | |
} | |
static function make_term_link( $url, $term ){ | |
if( $term->taxonomy === self::$catalog_tax || $term->taxonomy === self::$location_tax ){ | |
return user_trailingslashit( '/'. self::$post_type .'/'. self::_build_tax_uri( $term ) ); | |
} | |
return $url; | |
} | |
/** | |
* Gets parent terms (including current). | |
* | |
* @param WP_Term|string $term | |
* @param string $taxonomy IF term passed as string. | |
* | |
* @return WP_Term[]|array | |
*/ | |
static function parent_terms( $term, $taxonomy = null ){ | |
if( is_string( $term ) ) | |
$term = get_term_by( 'slug', $term, $taxonomy ); | |
if( ! $term ) | |
return []; | |
$path = [ $term ]; | |
while( $term->parent ){ | |
$term = get_term( $term->parent ); | |
$path[] = $term; | |
} | |
return array_reverse( $path ); | |
} | |
/** | |
* Build URL part of term of specified taxonomy. EX: 'parent/child/childchild' | |
* | |
* @param WP_Term|string $term | |
* @param string $taxonomy IF term passed as string. | |
* | |
* @return string | |
*/ | |
static function _build_tax_uri( $term, $taxonomy = null ){ | |
$slugs = wp_list_pluck( self::parent_terms( $term, $taxonomy ), 'slug' ); | |
return implode( '/', $slugs ); | |
} | |
/** | |
* Get top level location taxonomy slugs as array. Cache the result. | |
* | |
* @param bool $refresh | |
*/ | |
static function _location_top_slugs( $refresh = false ){ | |
$trans_name = 'location_top_slugs'; | |
$top_slugs = get_transient( $trans_name ); | |
if( $refresh || ! $top_slugs ){ | |
$top_slugs = get_terms( [ | |
'taxonomy' => self::$location_tax, | |
'parent' => 0, | |
'hide_empty' => false, | |
] ); | |
$top_slugs = '|'. implode( '|', wp_list_pluck( $top_slugs, 'slug' ) ) .'|'; | |
set_transient( $trans_name, $top_slugs, HOUR_IN_SECONDS ); | |
} | |
return $top_slugs; | |
} | |
static function _location_top_slugs_refresh_cache(){ | |
self::_location_top_slugs( 'refresh' ); | |
} | |
static function subpages_template_hierarchy( $templates ){ | |
if( $subpage = get_query_var('post_subpage') ){ | |
$subpage_file = 'single-'. self::$post_type ."-$subpage.php"; | |
array_unshift( $templates, $subpage_file ); | |
} | |
return $templates; | |
} | |
static function subpages_canonical( $canonical_url, $post ){ | |
if( $subpage = get_query_var('post_subpage') ){ | |
$canonical_url = user_trailingslashit( rtrim( get_permalink( $post ), '/' ) ."/$subpage" ); | |
} | |
return $canonical_url; | |
} | |
static function _debug(){ | |
print_r( get_queried_object() ); | |
print_r( $GLOBALS['wp'] ); | |
print_r( $GLOBALS['wp_query'] ); | |
print_r( $GLOBALS['wp_rewrite'] ); | |
exit; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment