WordPress как на ладони
wordpress jino

ЧПУ для трех таксономий и типа записи одновременно

Очередная заметка с кодом который бесполезен для 99% пользователей WordPress. Но для 1% это будет находкой, пожалуй. Речь о ЧПУ, решение сложной задачи. Объяснений что и как в этой заметке нет, потому что это как объяснять анекдот - либо ты понимаешь, либо нет...

Тот кто ко мне обратился с этой задачей, прежде попробовал несколько популярных плагинов, которые справиться с задачей не смогли: Gd custom tools (платный), Custom Post Type Permalinks (бесплатный). Увидев, что нужно сделать, я не удивился - сложно...

Задача

Создать тип записи и три таксы для него:

  • Новый тип записи: realty - объявления
  • Новая многоуровневая таксономия: country - страна > город (Испания > Валенсия)
  • Новая многоуровневая таксономия: type_realty - тип недвижимости (дома)
  • Новая одноуровневая таксономия: type_deal. Содержит только 2 термина: sale и rent (продажа, аренда)

Создать произвольную структуру ссылок (ЧПУ) для такс и записи.

Для таксономий сделать ссылки вида:

  • dom/ estate / %country% /

  • dom/ estate / %country% / %type_deal% /

  • dom/ estate / %country% / %type_deal% / %type_realty% /

А для объявлений:

  • dom/ estate / %country% / %type_deal% / %type_realty% / id-%post_id% /

Суть, сделать ЧПУ таксономий и записи полностью похожими. Чтобы можно было вручную убирать из URL последние элементы и попадать на уровень выше.

Примеры того что должно получиться в URL

Для всех трех таксономий:

  • dom/estate/spain/ - Недвижимость в Испании

  • dom/estate/spain/sale/ - Купить недвижимость в Испании

  • dom/estate/spain/sale/houses/ - Купить дом в Испании

  • dom/estate/spain/valensiya/sale/houses/ - Купить дом в Испании > Валенсия

  • dom/estate/spain/valensiya/sale-rent/ - Продажа и аренда всей недвижимости в Валенсии. Может выглядеть как dom/estate/spain/valensiya/

  • dom/estate/spain/valensiya/sale-rent/kommercheskaya/magazin/ - Продажа и аренда магазинов в Валенсии

Для каждой ссылки таксономии должна работать пагинация .../page/2/, .../page/3/

Для записи

все также как и у термина, только в конце еще ID этой самой записи:

  • dom/estate/spain/valensiya/sale/houses/id-123/. 123 - ID записи

Решение

Создаем файл в теме lib/register-ads-post-tax.php и подключаем его в functions.php темы:

require_once TEMPLATEPATH .'/lib/register-ads-post-tax.php';

Код файла:

<?php

## регистрация типов записей и таксономий
add_action('init', 'estate_type_register');
function estate_type_register(){

	// post_type realty
	register_post_type('realty', array(
		'description'         => 'Объявления о продаже недвижимости за рубежом.',
		'labels' => array(
			'name'               => 'Объявления',
			'singular_name'      => 'Объявление',
			'add_new'            => 'Добавить новое',
			'add_new_item'       => 'Добавить объявление',
			'edit_item'          => 'Изменить объявление',
			'new_item'           => 'Новое объявление',
			'view_item'          => 'Посмотреть объявления',
			'search_items'       => 'Поиск объявлений',
			'not_found'          => 'Не найдено ни одного объявления',
			'not_found_in_trash' => 'Объявлений в корзине не найдено',
			'parent_item_colon'  => 'Parent Объявление:',
			'all_items'          => 'Все объявления',
			'menu_name'          => 'Объявления'
		),
		'taxonomies'          => array( 'country', 'type_realty', 'type_deal' ),
		'public'              => true,
		'query_var'           => true,
		'rewrite'             => false,
		//'rewrite'             => true,
		//'rewrite'             => array( 'slug'=>'estate/%country%/%post_id%', 'with_front'=>false, 'feeds'=>false, 'pages'=>false, 'feed'=>false, 'paged'=>false ),
		'show_in_menu'        => true,
		'has_archive'         => false,
		'supports'            => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'revisions' ),
		'show_ui'             => true,
		'show_in_nav_menus'   => false,
	));

	## Изменяет ссылку на термины таксономий - 'type_deal' и 'type_realty', которая получается функцией get_term_link()
	## К какой стране относится берется из URL текущей страницы
	add_filter('term_link', 'deal_realty_tax_link_fix', 10, 3);
	function deal_realty_tax_link_fix( $link, $term, $taxonomy ){
		if( ! in_array( $taxonomy, array('type_deal','type_realty') ) ) return $link;

		$uri = $_SERVER['REQUEST_URI'];

		if( $taxonomy == 'type_deal' ){
			$link = preg_replace('~^(.*?/)(?:'. implode('|', __type_deal_elements()) .')/.*~', '\1', $uri ) . $term->slug;
		}

		if( $taxonomy == 'type_realty' ){
			$link = preg_replace('~^(.*?/(?:'. implode('|', __type_deal_elements()) .')/).*~', '\1', $uri ) . __build_tax_uri( $term );
		}

		return esc_url( user_trailingslashit( home_url($link) ) );
	}

	// фокусы со структурой ссылок
	if(1){
		//add_action('wp', 'xxxxxxx'); // debug
		function xxxxxxx($wp){
			print_r( get_queried_object() );
			print_r( $wp );
			print_r( $GLOBALS['wp_rewrite'] );
			exit;
		}

		// регистрируем структуру через add_permastruct, чтобы работала структура ЧПУ для типа записи
		add_permastruct('realty', 'estate/%country%/%type_deal%/%type_realty%/id-%post_id%', array(
			'with_front'  => 0,
			'paged'       => 0,
			'feed'        => 0,
			'walk_dirs'   => 0,
		));

		/*
		add_filter( 'query_vars', function( $vars ){
			$vars[] = 'type_deal';
			$vars[] = 'type_realty';
			return $vars;
		} );
		*/

		// поправим параметры запроса - удалим 'country' и добавим 'post_type=realty', потому что '?p=208' не работает, а '?p=208&post_type=realty' работает
		//$this->query_vars = apply_filters( 'request', $this->query_vars );
		add_action('request', 'remove_unwanted_query_vars' );
		function remove_unwanted_query_vars( $vars ){

			if( !empty($vars['country']) ){
				$_country = explode('/', $vars['country']);
				$vars['country'] = end( $_country ); // последний элемент
			}

			if( !empty($vars['type_realty']) ){
				$_type_realty = explode('/', $vars['type_realty']);
				$vars['type_realty'] = end( $_type_realty ); // последний элемент
			}

			// запрос типа записи 'realty' - параметры: country, type_realty, type_deal
			if( isset($vars['p'], $vars['type_realty']) && $vars['post_type'] === 'realty' ){
				$deal = __type_deal_elements();

				if( $vars['type_deal'] == end($deal)/*'sale-rent'*/ )
					unset( $vars['type_deal'] );
			}

			return $vars;
		}

		// добавим правила перезаписи
		//add_rewrite_rule('^estate/.+?/(?:sale|rent|sale-rent)/.+?/([0-9]+)/?', 'index.php?p=$matches[1]&post_type=realty', 'top' );

		// удалим все созданные правила перезаписи и создам новые...
		// $rules = apply_filters( $permastructname . '_rewrite_rules', $rules );
		// do_action_ref_array( 'generate_rewrite_rules', array( &$this ) );
		add_filter('realty'.'_rewrite_rules', 'delete_realty_rewrite_rules' );
		function delete_realty_rewrite_rules( $rules ){
			/* array(
				[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/?$] => index.php?attachment=$matches[1]
				[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
				[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
				[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
				[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
				[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
				[estate/(.+?)/%type_deal%/%type_realty%/([0-9]+)/embed/?$] => index.php?country=$matches[1]&%type_deal%$matches[2]&%type_realty%$matches[3]&p=$matches[4]&embed=true
				[estate/(.+?)/%type_deal%/%type_realty%/([0-9]+)/trackback/?$] => index.php?country=$matches[1]&%type_deal%$matches[2]&%type_realty%$matches[3]&p=$matches[4]&tb=1
				[estate/(.+?)/%type_deal%/%type_realty%/([0-9]+)(?:/([0-9]+))?/?$] => index.php?country=$matches[1]&%type_deal%$matches[2]&%type_realty%$matches[3]&p=$matches[4]&page=$matches[5]
				[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/?$] => index.php?attachment=$matches[1]
				[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
				[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
				[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
				[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
				[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
			) */

			// должен быть до правил стран
			$rules = array(
				'estate/(.+?)/('. implode('|', __type_deal_elements() ) /*sale|rent|sale-rent*/ .')/(.+?)/id-([0-9]+)/?$' => 'index.php?post_type=realty&p=$matches[4]&country=$matches[1]&type_deal=$matches[2]&type_realty=$matches[3]',
			);
			return $rules;
		}

		## добавим правила перезаписи для 'country', 'type_deal', 'type_realty'
		add_filter('country'.'_rewrite_rules', 'add_more_country_rewrite_rules' );
		function add_more_country_rewrite_rules( $rules ){
			/*Array(
				[estate/(.+?)/page/?([0-9]{1,})/?$] => index.php?country=$matches[1]&paged=$matches[2]
				[estate/(.+?)/?$] => index.php?country=$matches[1]
			)*/

			// должен быть после правил типа запии 'realty'
			$_first_part = 'estate/(.+?)/('. implode('|', __type_deal_elements() ) /*sale|rent|sale-rent*/ .')';
			$_pade_part = 'page/?([0-9]{1,})';
			$more_riles = array(
				"$_first_part/(.+?)/$_pade_part/?$" => 'index.php?country=$matches[1]&type_deal=$matches[2]&type_realty=$matches[3]&paged=$matches[4]',
				"$_first_part/$_pade_part/?$"       => 'index.php?country=$matches[1]&type_deal=$matches[2]&paged=$matches[3]',
				"$_first_part/(.+?)/?$"             => 'index.php?country=$matches[1]&type_deal=$matches[2]&type_realty=$matches[3]',
				"$_first_part/?$"                   => 'index.php?country=$matches[1]&type_deal=$matches[2]',
			);

			$rules = array_merge( $more_riles, $rules );

			//die( print_r($rules) );
			return $rules;
		}

		// array( sale, rent, sale-rent )
		function __type_deal_elements(){
			$deal_terms = get_terms(array( 'taxonomy'=>'type_deal','hide_empty'=>0, 'fields'=>'id=>slug' ));
			$deal_terms[] = implode('-', $deal_terms); // sale-rent
			return $deal_terms;
		}

		/*
		add_action('generate_rewrite_rules', 'delete_ads_rewrite_rules' );
		function delete_ads_rewrite_rules( $that ){

			[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/?$] => index.php?attachment=$matches[1]
			[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
			[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
			[estate/.+?/%type_deal%/%type_realty%/[0-9]+/attachment/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true
			[estate/(.+?)/%type_deal%/%type_realty%/([0-9]+)/embed/?$] => index.php?country=$matches[1]&%type_deal%$matches[2]&%type_realty%$matches[3]&p=$matches[4]&embed=true
			[estate/(.+?)/%type_deal%/%type_realty%/([0-9]+)/trackback/?$] => index.php?country=$matches[1]&%type_deal%$matches[2]&%type_realty%$matches[3]&p=$matches[4]&tb=1
			[estate/(.+?)/%type_deal%/%type_realty%/([0-9]+)(?:/([0-9]+))?/?$] => index.php?country=$matches[1]&%type_deal%$matches[2]&%type_realty%$matches[3]&p=$matches[4]&page=$matches[5]
			[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/?$] => index.php?attachment=$matches[1]
			[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/trackback/?$] => index.php?attachment=$matches[1]&tb=1
			[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$] => index.php?attachment=$matches[1]&feed=$matches[2]
			[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/comment-page-([0-9]{1,})/?$] => index.php?attachment=$matches[1]&cpage=$matches[2]
			[estate/.+?/%type_deal%/%type_realty%/[0-9]+/([^/]+)/embed/?$] => index.php?attachment=$matches[1]&embed=true

			foreach( $that->extra_rules_top as $rule => $_ ) if( preg_match('~^estate/.+type_deal~', $rule) ) unset($that->extra_rules_top[$rule]);
			foreach( $that->rules           as $rule => $_ ) if( preg_match('~^estate/.+type_deal~', $rule) ) unset($that->rules[$rule]);
		}
		*/

		// добавим post_type в параметры запроса, потому что просто '?p=208' почему-то не работает, а работает '?p=208&post_type=realty'
		//do_action_ref_array( 'parse_request', array( &$this ) );
		/*
		add_action('parse_request', function($wp){
			if( strpos($wp->matched_query, 'foo_') ) $wp->query_vars['post_type'] = 'realty';
		} );

		// дополнительный тег перезаписи, чтобы не учитывать
		add_rewrite_tag('%fake_country%', '(.+?)', '&adsfoovar_='); // adsfoovar_ - несуществующий параметр запроса, он будет удален... он не нужен...

		// заменим %realty% (%post_name%) на ID
		//global $wp_rewrite;
		//$wp_rewrite->extra_permastructs['realty'] = str_replace('%realty%', '%post_id%.html', $wp_rewrite->extra_permastructs['realty']);

		// Удалим слэш на конце, если нужно. Если слэш есть в структуре ЧПУ, то он будет добавляться и к постоянным страницам.
		//add_filter('user_trailingslashit', 'no_ads_trailslash', 170, 2);
		function no_ads_trailslash( $string, $type ){
			global $wp_rewrite;

			if( $type == 'realty' && $wp_rewrite->use_trailing_slashes && $wp_rewrite->using_permalinks() )
				return untrailingslashit($string);

		   return $string;
		}
		*/

		## Отфильтруем ЧПУ произвольного типа
		add_filter('post_type_link', 'realty_permalink', 1, 2);
		function realty_permalink( $permalink, $post ){
			if( false === strpos($permalink, '%country%') ) return $permalink;

			//global $wp_rewrite;
			//$perm = $wp_rewrite->get_extra_permastruct('realty');

			// Получаем элементы таксы 'country'
			$country_path = __build_tax_uri( get_the_terms( $post, 'country') ); // много уровней - asd/asd

			// type_realty_path
			$type_realty_path = __build_tax_uri( get_the_terms( $post, 'type_realty') ); // много уровней - asd/asd

			$type_deal_path = __build_tax_uri( get_the_terms( $post, 'type_deal') ); // один уровень - asd

			return strtr( $permalink, array(
				'%type_deal%'   => $type_deal_path,
				'%type_realty%' => $type_realty_path,
				'%country%'     => $country_path,
				'%post_id%'     => $post->ID,
			) );
		}

		## Получает цепочку из ярлыков указанного термина - вврехи пока у термина не будет родителей - 'parent/child/child'
		function __build_tax_uri( $terms ){
			if( is_wp_error($terms) || empty($terms) || ! ( is_object(reset($terms)) || is_object($terms) ) )
				return 'no_terms'; // элемента таксы нет, а должен быть...

			$term = is_object(reset($terms)) ? reset($terms) : $terms;
			$path = array( $term->slug );
			while( $term->parent ){
				$term = get_term( $term->parent );
				$path[] = $term->slug;
			}

			return implode('/', array_reverse($path) );
		}

		## 404 страница если в URL указаны неправильные названия таксономий.
		## По факту, WP затем 301 редиректит на правильный URL...
		add_filter('pre_handle_404', 'realty_404_test' );
		function realty_404_test( $false ){
			if( ! get_queried_object() ) return $false; // ничего не делает...

			$_404 = false;

			// запись объявления
			if( is_singular('realty') ){
				$post = get_queried_object();

				// type_deal
				if( ! $_404 && ( $term_name = get_query_var('type_deal') ) && ! has_term( explode('-', $term_name), 'type_deal', $post ) )
					$_404 = 1;

				// type_realty
				if( ! $_404 && ( $term_name = get_query_var('type_realty') ) && ! has_term( $term_name, 'type_realty', $post ) )
					$_404 = 1;

				// country
				if( ! $_404 && ( $term_name = get_query_var('country') ) && ! has_term( $term_name, 'country', $post ) )
					$_404 = 1;

			}

			// для такс
			if( is_tax(['type_deal','type_realty','country']) ){
				// type_deal
				if( ! $_404 && ( $term_name = get_query_var('type_deal') ) && ! get_term_by('slug', $term_name, 'type_deal') )
					$_404 = 1;

				// type_realty
				if( ! $_404 && ( $term_name = get_query_var('type_realty') ) && ! get_term_by('slug', $term_name, 'type_realty') )
					$_404 = 1;

				// country
				if( ! $_404 && ( $term_name = get_query_var('country') ) && ! get_term_by('slug', $term_name, 'country') )
					$_404 = 1;

			}

			if( $_404 ){
				global $wp_query;

				$wp_query->set_404();
				status_header( 404 );
				nocache_headers();

				return 1; // обрываем следующие проверки
			}

			return $false; // ничего не делает...
		}

	}

	## Такса - страна город регион
	register_taxonomy('country', array('realty'), array(
		'labels' => array(
			'name'                       => 'Страны',
			'singular_name'              => 'Страна',
			'search_items'               => 'Search Страны',
			'popular_items'              => 'Popular Страны',
			'all_items'                  => 'All Страны',
			'parent_item'                => 'Parent Страна',
			'parent_item_colon'          => 'Parent Страна:',
			'edit_item'                  => 'Edit Страна',
			'view_item'                  => 'View Страна',
			'update_item'                => 'Update Страна',
			'add_new_item'               => 'Add New Страна',
			'new_item_name'              => 'New Страна Name',
			'separate_items_with_commas' => 'Separate Страны with commas',
			'add_or_remove_items'        => 'Add or remove Страны',
			'choose_from_most_used'      => 'Choose from the most used Страны',
			'not_found'                  => 'No Страны found.',
			'menu_name'                  => 'Страны'
		),
		'hierarchical'       => true,
		'rewrite'            => array('slug'=>'estate', 'hierarchical'=>true, 'with_front'=>0, 'feed'=>0 ),
		'query_var'          => true,
		'public'             => true,
		'show_ui'            => true,
		'show_tagcloud'      => true,
		'show_admin_column'  => false,
		'show_in_nav_menus'  => true,
	));

	## Такса - тип недвижимости
	register_taxonomy( 'type_realty', array('realty'), array(
		'labels' => array(
			'name'                       => 'Тип недвижимости',
			'singular_name'              => 'Тип недвижимости',
			'search_items'               => 'Search Тип недвижимости',
			'popular_items'              => 'Popular Тип недвижимости',
			'all_items'                  => 'All Тип недвижимости',
			'parent_item'                => 'Parent Тип недвижимости',
			'parent_item_colon'          => 'Parent Тип недвижимости:',
			'edit_item'                  => 'Edit Тип недвижимости',
			'view_item'                  => 'View Тип недвижимости',
			'update_item'                => 'Update Тип недвижимости',
			'add_new_item'               => 'Add New Тип недвижимости',
			'new_item_name'              => 'New Тип недвижимости Name',
			'separate_items_with_commas' => 'Separate Тип недвижимости with commas',
			'add_or_remove_items'        => 'Add or remove Тип недвижимости',
			'choose_from_most_used'      => 'Choose from the most used Тип недвижимости',
			'not_found'                  => 'No Тип недвижимости found.',
			'menu_name'                  => 'Тип недвижимости'
		),
		'public'             => false,
		'rewrite'            => false,
		'hierarchical'       => true,
		'query_var'          => true, // query_var, чтобы запрос на термин работал...
		'publicly_queryable' => true, // query_var, чтобы запрос на термин работал...
		'show_ui'            => true,
		'show_in_quick_edit' => true,
		'show_tagcloud'      => false,
		'show_admin_column'  => false,
		'show_in_nav_menus'  => false,
	));

	## Такса - тип сделки  -  два типа - 'sale' и 'rent'
	register_taxonomy('type_deal', array('realty'), array(
		'labels' => array(
			'name'                       => 'Тип сделки',
			'singular_name'              => 'Тип сделки',
			'search_items'               => 'Search Тип сделки',
			'popular_items'              => 'Popular Тип сделки',
			'all_items'                  => 'All Тип сделки',
			'parent_item'                => 'Parent Тип сделки',
			'parent_item_colon'          => 'Parent Тип сделки:',
			'edit_item'                  => 'Edit Тип сделки',
			'view_item'                  => 'View Тип сделки',
			'update_item'                => 'Update Тип сделки',
			'add_new_item'               => 'Add New Тип сделки',
			'new_item_name'              => 'New Тип сделки Name',
			'separate_items_with_commas' => 'Separate Тип сделки with commas',
			'add_or_remove_items'        => 'Add or remove Тип сделки',
			'choose_from_most_used'      => 'Choose from the most used Тип сделки',
			'not_found'                  => 'No Тип сделки found.',
			'menu_name'                  => 'Тип сделки'
		),
		'public'             => false,
		'hierarchical'       => true, // для удобности выбора
		'query_var'          => true, // query_var, чтобы запрос на термин работал...
		'publicly_queryable' => true, // query_var, чтобы запрос на термин работал...
		'rewrite'            => false,
		'show_ui'            => true,
		'show_in_quick_edit' => true,
		'show_tagcloud'      => false,
		'show_in_nav_menus'  => false,
		'show_admin_column'  => false,
	));

}

##  отменим показ выбранного термина наверху в checkbox списке терминов
add_filter( 'wp_terms_checklist_args', 'set_checked_ontop_default', 10 );
function set_checked_ontop_default( $args ){
	// изменим параметр по умолчанию на false
	if( ! isset($args['checked_ontop']) ) $args['checked_ontop'] = false;

	return $args;
}

## Меняем страницу отдельной страны
add_filter('template_include', 'top_country_tpl_file');
function top_country_tpl_file( $template ) {

	// термин страницы верхнего уровня
	if( is_tax('country') ){
		if( ! get_query_var('type_realty') && ! get_query_var('type_deal') && ($term = get_queried_object()) && ! $term->parent ){
			$tpl_file = get_stylesheet_directory() . '/taxonomy-country-toplevel.php';
			if( file_exists($tpl_file) ) $template = $tpl_file;
		}
	}

	return $template;
}

// дополнительные хуки для станицы создания объявления
// изменение блока метабокса
if( is_admin() ){

	// стили в админке связанные с типом поста quest
	add_action('admin_print_footer_scripts', 'country_hook_admin_footer_scripts', 99);
	function country_hook_admin_footer_scripts(){
		$sc = get_current_screen();
		if( $sc->base != 'post' || ! in_array($sc->post_type, ['realty']) )
			return;
		?>
		<style>
			.postbox div.tabs-panel{ max-height:700px; border:0; }
			.category-tabs{ display:none; }
			#countrydiv .inside{ margin:0; padding:0; }
			#countrydiv #country-adder{ padding: 0 1em; }
			#country-all ul.children{ display:none; }
		</style>

		<script>
		jQuery(document).ready(function($){

			var $main = $('#country-all'),
				$checklist = $main.find('input[type="checkbox"]');

			$checklist.prop('type','radio');

			// стрелки ▾ ▸
			$main.find('label').each(function(){
				var $li = $(this).closest('li')
				if( $li.find('> .children').length )
					$(this).append('<span class="has_ch" style="opacity:.5;">▸</span>');
			});

			// развернуть/свернуть
			$main.on('click', 'input', function(){
				var $li = $(this).closest('li'),
					$child = $li.find('> .children');

				if( $child.length ){
					if( $child.is(':visible') ){
						$child.slideUp(200);
						$child.prev().find('.has_ch').text('▸');
					}
					else {
						$child.slideDown(200);
						$child.prev().find('.has_ch').text('▾');
					}
				}
			});

			// покажем выбранный внутренний
			var $checked = $checklist.filter(':checked')
			if( $checked.length )
				$checked.parents('ul.children').show()

		});
		</script>
		<?php
	}

}

Объяснять код не буду, долго, сложно, смысла особо нет, потому лень, извините...

ЧПУ для трех таксономий и типа записи одновременно 8 комментариев
  • campusboy1946 cайт: www.youtube.com/c/wpplus @

    Отвал башки. Запишите кто-нибудь урок с подробным объяснением, буду признателен!

    Ответить2 месяца назад #
  • Александр cайт: tocrypto.ru

    Данный пост подтверждает, что на wordpress можно творить реально всё, что угодно! Когда-то сталкивался с необходимостью сделать подобную структуру, провозился несколько дней, так и не получилось, хотя изначально казалось, что просто. Спасибо, пригодится)

    Ответитьмесяц назад #
  • Сергей cайт: moymotoblok.ru @

    Большое спасибо, код очень помог в разработке своей структуры. Остался нерешенным только один момент: можно ли как-то задавать title и описание к генерируемым страницам?

    Например:
    /estate/spain/sale/houses/
    title: Купить дом в Испании
    Описание: Список домов, которые вы можете купить в Испании

    Или только хардкодить прямо в шаблоне темы шаблоны с подстановкой переменных? Вроде

    $type_deal $type_realty в $country
    Ответитьмесяц назад #
    • Kama4662

      Плохо помню, но я это тоже делал. Там по шаблону был заголовок: Если sale то продать, если houses - то дом, а для стран и городов парсил все варианты падежей в метаполе термина (города, страны) и потом от туда я брал нужный падеж и подставлял, где нужно - это не только заголовок. В теме самой много где падежи нужны были...

      Для описания аналогично функцией можно сделать по шаблону...

      Ответитьмесяц назад #
    • anton cайт: compbcn.es

      Конечно можно. Я недавно делал похожее, но с использованием плагина Yoast.
      Кодом не поделюсь но вот:

      Вытаскиваем данные что бы формировать Тайтл + Описание

      Создаем базовое описание и через ифы добавляем что то в зависимости от наличия или отсутствия вдернутых значений.

      К примеру

      $main_str = " в Испании"
      
      $type_deal = get_type_deal($post_id);
      $type_realty = get_type_realty($post_id);
      $country
      
      if ($type_deal == 'forsale' && $type_realty == 'house'){
      	$main_str = 'Купить дом'.$main_str;
      }elseif($type_deal == 'forrent' && $type_realty == 'house'){
      	$main_str = 'Арендовать дом'.$main_str;
      }elseif($type_deal == 'forsale' && $type_realty == 'hotel'){
      	$main_str = 'Купить отель'.$main_str;
      ...
      
      //Цепляемся к фильтрам Yoast (https://yoast.com/wordpress/plugins/seo/api/)
      // и меняем тайтлы или описание или что угодно на выбор
      
      add_filter( 'wpseo_title', 'my_func_change_title', 10, 1 );
      
      1
      Ответитьмесяц назад #
      • Сергей cайт: moymotoblok.ru @

        Спасибо, но хардкодить шаблоны - это просто. А хотелось бы кастомизировать каждую отдельную страницу, чтобы описания были не по шаблону, а специально прописанным текстом.

        Что ж, нет так нет, придется думать. Или таки шаблоны.

        Ответить27 дней назад #
        • campusboy1946 cайт: www.youtube.com/c/wpplus @

          Создать отдельную таблицу в базе со всеми совпадениями и текстами. И можно будет любой писать smile

          1
          Ответить27 дней назад #
  • Оно работает! Мне по всей видимости не позволяет скудность моего абстрактного мышления понять, как вообще систематика т.е таксономия может быть дочерней термину другой систематики… Т.е по сути тут метки являются дочерними элементами категорий…

    Может кто то дать пример по проще, что бы кода было как можно меньше но суть осталась) К примеру со стандартными записями, категориями и метками.

    Интересует только что бы термины одной таксономии являлись дочерними терминами другой таксономии:

    dom / %category% / %tag% /

    Ответитьмесяц назад #

Здравствуйте, !

Ваш комментарий