WordPress как на ладони
Недорогой хостинг для сайтов на WordPress: wordpress.jino.ru

Удаление всех родителей, кроме самого верхнего из ЧПУ страниц

Нужно у структуры ЧПУ (страниц) убрать всех родителей, кроме самого верхнего. Например site.ru/rod1/rod2/rod3/pagename - надо сделать site.ru/rod1/pagename.

Решение

/**
 * Delete all parents from static page permalink, except top parent. 
 *
 * What we need to achieve: 
 *     /parent/child1/child2/child3/   >>>   /parent/child3/
 *     /parent/child1/child2/          >>>   /parent/child2/
 *     /parent/child1/                 >>>   /parent/child1/
 *     /parent/                        >>>   /parent/
 *
 * For usage just init the class, like so: 
 *     Delete_Page_URI_Parents::init();
 * 
 * @author Kama
 *
 * @version 1.0
 */
final class Delete_Page_URI_Parents {

	static $origin_REQUEST_URI = '';

	static $skip_link_change = false;

	static function init(){

		// изменим пермалинк
		add_filter( 'page_link',        [ __CLASS__, 'page_permalink' ] );

		// изменяем $_SERVER['REQUEST_URI'] на фильтре `do_parse_request`
		// и возвращаем пердыдущее значение обратно на фильтре `request`
		// между ними ВП определяет какая сейчас страница.
		add_filter( 'do_parse_request', [ __CLASS__, 'replace_uri' ] );
		add_filter( 'request',          [ __CLASS__, 'replace_uri_back' ] );
	}

	static function page_permalink( $link ){

		if( self::$skip_link_change )
			return $link;

		$endslash = '/' === $link{-1} ? '/' : '';
		$parts = explode( '/', rtrim( $link, '/' ) );

		// our case
		if( count( $parts ) > 5 ){
			$last  = end( $parts );
			$parts = array_slice( $parts, 0, 4 );

			$link = implode( '/', $parts ) ."/$last$endslash";
		}

		return $link;
	}

	static function replace_uri( $foo ){

		global $wpdb, $wp_rewrite;

		list( $req_uri ) = explode( '?', $_SERVER['REQUEST_URI'] );
		$req_uri = trim( $req_uri, '/' );

		// there is no slashes in uri - not our case
		if( ! substr_count( $req_uri, '/' ) )
			return $foo;

		// Let's do it!

		list( $req_uri_prefix ) = explode( '/', $req_uri );

		// Check all rewrite rules
		foreach( get_option( 'rewrite_rules' ) as $match => $query ){

			list( $match_prefix ) = explode( '/', $match );

			if(
				// URL starts with known prefix: category/foo/bar
				( ltrim( $match_prefix, '^' ) === $req_uri_prefix ) ||
				// URL starts with nember: 2019/08/10
				is_numeric( $req_uri_prefix )
			){
				return $foo;
			}

			// Try to find page rewrite rule
			// and take page path from current URL using found rewrite rule
			if(
				strpos( $query, 'pagename=' ) && // for performance
				(
					preg_match( "#^$match#", $req_uri, $matches ) ||
					preg_match( "#^$match#", urldecode( $req_uri ), $matches )
				) &&
				(
					$wp_rewrite->use_verbose_page_rules &&
					preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch )
				)
			){

				$page_path = $matches[ $varmatch[1] ];
				$page_name = basename( $page_path );

				// we need at least 1 slash: parent/child2
				if( ! substr_count( $page_path, '/' ) )
					return $foo;

				//die( print_r( $req_uri_prefix ) );
				//die( print_r( get_option( 'rewrite_rules' ) ) );
				//die( print_r( $wp_rewrite ) );

				$page = $wpdb->get_row(
					"SELECT * FROM $wpdb->posts WHERE post_name = '". esc_sql( $page_name ) ."' AND post_type = 'page' AND post_status = 'publish' LIMIT 1"
				);

				// page found
				if( $page ){

					self::$origin_REQUEST_URI = $_SERVER['REQUEST_URI'];

					$page = get_post( $page );

					self::$skip_link_change = 1;
					$real_page_uri = wp_make_link_relative( get_permalink($page) );
					self::$skip_link_change = 0;

					// make a substitution
					$_SERVER['REQUEST_URI'] = $real_page_uri;

					// SEO Redirect
					// to correct URL, when full path uses in page URL
					if(
						substr_count( trim($real_page_uri, '/'), '/' ) > 1 &&
						false !== strpos( self::$origin_REQUEST_URI, $real_page_uri )
					){
						wp_redirect( get_permalink($page), 301 );
						exit;
					}
				}

				break; // rewrite rule is found, further looking and checks makes no sense
			}

		}

		return $foo; // for filter
	}

	static function replace_uri_back( $foo ){

		if( self::$origin_REQUEST_URI )
			$_SERVER['REQUEST_URI'] = self::$origin_REQUEST_URI;

		return $foo; // for filter
	}

}

Delete_Page_URI_Parents::init();
Комментариев нет