😅Tạo phân trang, paginate cho Custom WordPress Loop Full (ok)

https://websitecuatui.net/custom-wordpress-loop-pagination/

Cách 1. Pagination taxonomy custom

Default paginate taxonomy https://test.com/category/uncategorized/page/2/

Nếu chưa viết lại đường dẫn thì nó bị lỗi không tìm thấy 😂

Dùng rewrite_rules_array viết lại đường dẫn

C:\xampp743\htdocs\testcom\wp-content\themes\twentytwentyone\functions.php

add_action('init', 'foo_articles');
function foo_articles()
{
	$labels = array(
		'name'               => _x('Foo Articles', 'post type general name', 'your-plugin-textdomain'),
		'singular_name'      => _x('Foo Articles', 'post type singular name', 'your-plugin-textdomain'),
		'menu_name'          => _x('Foo Articles', 'admin menu', 'your-plugin-textdomain'),
		'name_admin_bar'     => _x('My Slug', 'add new on admin bar', 'your-plugin-textdomain'),
		'add_new'            => _x('Add New', 'book', 'your-plugin-textdomain'),
		'add_new_item'       => __('Add New Book', 'your-plugin-textdomain'),
		'new_item'           => __('New Book', 'your-plugin-textdomain'),
		'edit_item'          => __('Edit Book', 'your-plugin-textdomain'),
		'view_item'          => __('View Book', 'your-plugin-textdomain'),
		'all_items'          => __('All Books', 'your-plugin-textdomain'),
		'search_items'       => __('Search Books', 'your-plugin-textdomain'),
		'parent_item_colon'  => __('Parent Books:', 'your-plugin-textdomain'),
		'not_found'          => __('No books found.', 'your-plugin-textdomain'),
		'not_found_in_trash' => __('No books found in Trash.', 'your-plugin-textdomain'),
	);
	$args = array(
		'labels'             => $labels,
		'description'        => __('Description.', 'your-plugin-textdomain'),
		'public'             => true,
		'publicly_queryable' => true,
		'show_ui'            => true,
		'show_in_menu'       => true,
		'query_var'          => true,
		'has_archive'        => true,
		'rewrite'            => array('slug' => 'my_slug', 'with_front' => true),
		'hierarchical'       => true,
		'menu_position'      => null,
		'show_in_rest'       => true,
		'rest_base'          => 'my_slug',
		'supports'           => array('title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments', 'page-attributes'),
	);
	register_post_type('my_slug', $args);
	flush_rewrite_rules(true);
}
add_action('init', 'my_book_taxonomy');
function my_book_taxonomy()
{
	$labels = array(
		'name'              => _x('Foo Taxonomy', 'taxonomy general name'),
		'singular_name'     => _x('Foo Taxonomy', 'taxonomy singular name'),
		'search_items'      => __('Search Genres'),
		'all_items'         => __('All Genres'),
		'parent_item'       => __('Parent Genre'),
		'parent_item_colon' => __('Parent Genre:'),
		'edit_item'         => __('Edit Genre'),
		'update_item'       => __('Update Genre'),
		'add_new_item'      => __('Add New Genre'),
		'new_item_name'     => __('New Genre Name'),
		'menu_name'         => __('Foo Taxonomy'),
	);
	$args = array(
		'hierarchical'      => true,
		'labels'            => $labels,
		'show_ui'           => true,
		'show_admin_column' => true,
		'query_var'         => true,
		'rewrite'           => array('slug' => 'my_slug', 'with_front' => TRUE),
		'show_in_rest'      => true,
		'rest_base'         => 'foo_taxonomy',
	);
	register_taxonomy('foo_taxonomy', array('my_slug'), $args);
	flush_rewrite_rules(true);
}
function add_rewrite_rules( $rules ) {
  $new = array();
  $new['my_slug/([^/]+)/(.+)/?$'] = 'index.php?knowledge_base=$matches[2]';
  $new['my_slug/(.+)/?$'] = 'index.php?my_slug_topics=$matches[1]';
  return array_merge( $new, $rules ); // Ensure our rules come first
}
add_filter( 'rewrite_rules_array', 'add_rewrite_rules' );

Cách 2. Pagination taxonomy custom

Hoặc thay rewrite_rules_array bởi generate_rewrite_rules

function column_custom_rewrite_basic( $wp_rewrite ) {
	unset($wp_rewrite->rules['my_slug/([^/]+)/page/?([0-9]{1,})/?$']);
	$feed_rules = array(
			'my_slug/page/?([0-9]{1,})/?$' => $wp_rewrite->index . '?post_type=my_slug&paged=' . $wp_rewrite->preg_index( 1 ),
			'my_slug/([^/]+)/page/?([0-9]{1,})/?$' => $wp_rewrite->index . '?tax_my_slug_category=' . $wp_rewrite->preg_index( 1 ) . '&paged=' . $wp_rewrite->preg_index( 2 ),
			'my_slug/(.+)/(.+)/?$' => $wp_rewrite->index . '?post_type=my_slug&tax_my_slug_category=' . $wp_rewrite->preg_index( 1 ) . '&name=' . $wp_rewrite->preg_index( 2 ),
			'my_slug/(.+)?/?$' => $wp_rewrite->index . '?post_type=my_slug&tax_my_slug_category=' . $wp_rewrite->preg_index( 1 ),
	);
	$wp_rewrite->rules = $feed_rules + $wp_rewrite->rules;
	return $wp_rewrite->rules;
}
add_filter('generate_rewrite_rules', 'column_custom_rewrite_basic');

function taxonomy_rewrite_fix($wp_rewrite) {
	$r = array();
	foreach ($wp_rewrite->rules as $k => $v) {
		$r[$k] = str_replace('game=$matches[1]&paged=', 'game=$matches[1]&page=', $v);
	}
	$wp_rewrite->rules = $r;
}
add_filter('generate_rewrite_rules', 'taxonomy_rewrite_fix');
<?php
define('UPLOADS', 'media/upload');
function displayadsv2() {
	$news = get_option('oknews');
	echo '
    <div class="adsgg active ">
    <div class="label">Advertisement</div><div class="adss">' . html_entity_decode($news['ok11ads']) . '</div>
    </div>
    ';
}

add_filter('wpseo_title', 'add_to_page_titles');
function add_to_page_titles($title) {
	$nv = $_SERVER['REQUEST_URI'];
	if (strpos('--' . $nv, 'get-game') > 0) {
		$title = 'Download ' . $title;
	}

	return $title;
}

function specific_enqueue($hook_suffix) {
	if ('post.php' == $hook_suffix || 'post-new.php' == $hook_suffix) {
		wp_enqueue_script('madmin_js', '/media/css/madmin.js', array('jquery'));
	}
}
add_action('admin_enqueue_scripts', 'specific_enqueue');

// include('func-m.php');
include 'func-options.php';
include 'func-analytics.php';
if (is_user_logged_in()) {
	add_filter('show_admin_bar', '__return_true');
}

add_shortcode('ads3361', 'ads3361');
add_shortcode('ads3362', 'ads3362');

function getcatflinkff($tag = 'game', $rep = '', $count = '20') {
	global $wpdb;
	$table = $wpdb->prefix;
	$sql = "SELECT name,slug FROM " . $wpdb->terms . " t INNER JOIN " . $wpdb->term_taxonomy . " tt ON t.term_id = tt.term_id
    WHERE taxonomy='$tag' ORDER BY count DESC LIMIT 0,$count
    ";
	$dat = '';
	$rs = $wpdb->get_results($sql);
	foreach ($rs as $r) {

		$dat .= '<li class="vlinkkkcat"><a title="' . $r->name . '" href="https://hocdekhangdinhminh.com/game/' . $r->slug . '">' . str_replace($rep, '', $r->name) . '</a></li>';
	}
	echo $dat;
}

function ads3361($args, $content) {
	ob_start();
	$bglp1 = get_option('oknews');

	echo '<div class="advertise-container">' . html_entity_decode($bglp1['okads61']) . '</div>';

	$content = ob_get_contents();
	ob_end_clean();
	return $content;
}
function ads3362($args, $content) {
	ob_start();
	$bglp1 = get_option('oknews');

	echo '<div class="advertise-container">' . html_entity_decode($bglp1['okads62']) . '</div>';

	$content = ob_get_contents();
	ob_end_clean();
	return $content;
}

function noidungdangbaig($cid = '') {
	?>
<!DOCTYPE html>
<html lang="en">
<head>

<?php wp_head();?>
<script src='/wp-includes/js/jquery/jquery.min.js' id='jquery-min-js'></script>
<script src='/wp-includes/js/tinymce/wp-tinymce.js?ver=49110-20201110' id='wp-tinymce-js'></script>

<link rel='stylesheet' href='/wp-admin/load-styles.php?c=1&amp;dir=ltr&amp;load%5Bchunk_0%5D=dashicons,admin-bar,common,forms,admin-menu,dashboard,list-tables,edit,revisions,media,themes,about,nav-menus,wp-pointer,widgets&amp;load%5Bchunk_1%5D=,site-icon,l10n,buttons,wp-auth-check&amp;ver=6.1.1' type="text/css" media='all' />
<link href="//cms.mepop.net/media/admin/css/icons/icomoon/styles.css" rel="stylesheet" type="text/css">
<link href="//cms.mepop.net/media/admin/css/bootstrap.css" rel="stylesheet" type="text/css">
<link href="//cms.mepop.net/media/admin/css/core.css" rel="stylesheet" type="text/css">
<link href="//cms.mepop.net/media/admin/css/components.css" rel="stylesheet" type="text/css">
<link href="//cms.mepop.net/media/admin/css/colors.css" rel="stylesheet" type="text/css">

	<script type="text/javascript" src="//cms.mepop.net/media/admin/js/core/libraries/jquery.min.js"></script>
	<script type="text/javascript" src="//cms.mepop.net/media/admin/js/core/libraries/bootstrap.min.js"></script>


<script type="text/javascript" src="//cms.mepop.net/media/admin/cms/spectrum.js"></script>

<script type="text/javascript" src="//cms.mepop.net/media/admin/cms/picker_color.js"></script>
</head>
<body class="top-container">
<?php

	echo do_shortcode('[sortableform]');
	if ($_POST['submithome']) {

		if (!function_exists('wp_generate_attachment_metadata')) {
			require_once ABSPATH . "wp-admin" . '/includes/image.php';
			require_once ABSPATH . "wp-admin" . '/includes/file.php';
			require_once ABSPATH . "wp-admin" . '/includes/media.php';
		}

		$myNewOptions1 = array(
			'ok11' => reoption($_POST['ok11']),
			'okmau1' => reoption($_POST['okmau1']),
			'okmau2' => reoption($_POST['okmau2']),
			'logolink1' => reoption($_POST['logolink1']),

		);
		update_option('oknews', $myNewOptions1);
	}
	$bglp1 = get_option('oknews');
	?>

<form class="vpadding" action='' method=post>
<?php

	echo "
<div class='dbdb'>HEADER Mã theo dõi <\/head>: </div>
<textarea placeholder='Nhập nội dung HTML' class='tarfull' name='ok11'>" . $bglp1['ok11'] . "</textarea><br />
<br /><br />




";

	?>
<div class="panel panel-body border-top-danger">

<div class="vcolorx">
<label>Màu header</label>
<div class="vcolor">
<input type="text"  name='okmau1' class="form-control colorpicker-show-input" data-preferred-format="name" value="<?php echo $bglp1['okmau1']; ?>">
<div class="mtvx " id="move-result"><span class="text-semibold"><?php echo $bglp1['okmau1']; ?></span></div>
</div>
</div>
<div class="clearfix"></div>
<div class="vcolorx">
<label>Màu nội dung</label>
<div class="vcolor">
<input type="text" name='okmau2' class="form-control colorpicker-show-input" data-preferred-format="name" value="<?php echo $bglp1['okmau2']; ?>">
<div class="mtvx " id="move-result"><span class="text-semibold"><?php echo $bglp1['okmau2']; ?></span></div>
</div>
</div>

</div>

<div class="panel panel-body border-top-danger">
<div class="vcolorx">
<label>Logo</label>
<div class="vcolor">
<?php
echo "<input size=50 type=text name=logolink1 value='" . $bglp1['logolink1'] . "'> Link logo<br /> ";
	?>
</div>
</div>

</div>



<input type=submit name=submithome value=Submit  class="button button-primary button-large" />
<br /><br />

</form>


<style>
.listtintieudiem {
  height: 300px;
  overflow: auto;
  width: 70%;
}.tarfull {
  height: 100px;
  width: 98%;
}.dbdb{
    float: left;
    width: 100%;
    padding:10px 0
}form.vpadding {
    margin: 0 10px;
}.vcolor .sp-replacer.sp-light {
    float: left;
    margin-right: 7px;
}div#move-result span {
    margin-top: 5px;
    display: inline-block;
}.vcolorx label {
    font-weight: bold;
    padding-top: 9px;
    margin-bottom: 2px;
}
</style>
<?php

	?>

<?php wp_footer();?>
</body>
</html>
   <?php
}

function noidungdangbai($id = '') {

	$title = get_the_title($id);
	$post = get_post($id, OBJECT, 'edit');
	$linkgame = get_post_meta($id, 'linkgame', true);
	$postfea = get_post_meta($id, 'postfea', true);
	$postfeas = json_decode($postfea);
	//echo '<pre>';print_r($postfeas);
	$abcfea = ($postfeas->fea == 1) ? " checked" : "";
	$abcnew = ($postfeas->new == 1) ? " checked" : "";
	?>
<!DOCTYPE html>
<html lang="en">
<head>

<?php wp_head();?>
<script src='/wp-includes/js/jquery/jquery.min.js' id='jquery-min-js'></script>
<script src='/wp-includes/js/tinymce/wp-tinymce.js?ver=49110-20201110' id='wp-tinymce-js'></script>

<link rel='stylesheet' href='/wp-admin/load-styles.php?c=1&amp;dir=ltr&amp;load%5Bchunk_0%5D=dashicons,admin-bar,common,forms,admin-menu,dashboard,list-tables,edit,revisions,media,themes,about,nav-menus,wp-pointer,widgets&amp;load%5Bchunk_1%5D=,site-icon,l10n,buttons,wp-auth-check&amp;ver=6.1.1' type="text/css" media='all' />

</head>

<body class="top-container">
<?php sw_save_post_if_submitted();
	$thumb = get_the_post_thumbnail_url($id);
	?>

<style>
body {
    margin: 0px 10px;
}
</style>
<div class="wp-core-ui">
<div id="poststuff">
   <form id="new_post" name="new_post" method="post" enctype="multipart/form-data">
      <?php wp_nonce_field('swp-frontend-post');?>
    <input type="hidden" name="ID" value="<?php echo $id; ?>" />
    <input type="hidden" name="post_type" value="post" />
    <div id="post-body" class="metabox-holder columns-2">
        <div id="post-body-content" style="position: relative;">
<div id="titlediv">
    <label class="vvv-text" id="title-prompt-textv" for="title">Tên Game</label>
    <div id="titlewrap">
    <label class="screen-reader-text" id="title-prompt-text" for="title">Tên game</label>
    <input required="" type="text" id="title" value="<?php echo $title; ?>" tabindex="1" size="20" name="title" spellcheck="true" autocomplete="off" />
    </div>
</div>
<div class="titledivff">
    <div class="titlediv1">
        <label class="vvv-text" id="title-prompt-textv" for="linkgame">Đường dẫn game HTML5</label>
        <div id="titlewrap">
        <label class="screen-reader-text" id="title-prompt-text" for="linkgame">Link game</label>
<input type="text" id="linkgame" value="<?php echo $linkgame; ?>" tabindex="1" size="20" name="linkgame" spellcheck="true" autocomplete="off" style="width: 600px;text-align: left;" />
        </div>
    </div>



<ul id="categorychecklist" data-wp-lists="list:postfea" class="postfeachecklist form-no-clear">
   <li id="postfea-1"><label class="selectit"><input value="1" <?php echo $abcfea; ?> type="checkbox" name="postfea[fea]" id="in-postfea-1"> Featured</label></li>
   <li id="postfea-2"><label class="selectit"><input value="1" <?php echo $abcnew; ?> type="checkbox" name="postfea[new]" id="in-postfea-2"> New</label></li>
</ul>



</div>
<br />
<div id="postdivrich" class="postarea wp-editor-expand">

<?php
$content = $post->post_content;
	$editor_id = 'editpost';

	$editor_args = array(
		'textarea_name' => 'content',
		'media_buttons' => true,
		'editor_class' => 'my_editor',
		'textarea_rows' => 15,
		'wpautop' => false,
		'tinymce' => array(
			'content_css' => get_stylesheet_directory_uri() . '/editor-style_single.css',
		),
	);

	?>
<div id="inline_editor">
<?php wp_editor($content, $editor_id, $editor_args);?>
</div>
</div>
        </div>


    <div id="postbox-container-1" class="postbox-container">
        <div id="side-sortables" class="meta-box-sortables ui-sortable" style="">

<div id="postimagediv" class="postbox ">
<div class="inside">
<p class="hide-if-no-js">
<img width="266" height="127" src="<?php echo $thumb; ?>" /></p>
</div>
<input type="file" name="thumbnail" id="thumbnail"  />
</div>


            <p>
            </p>
            <p>
             <label for="content">Chọn chuyên mục</label><br />
<?php

	$category_detail = wp_get_post_terms($id, 'category', array("fields" => "all"));
	$dm = $category_detail[0];
	$cid = $dm->term_id;
	$defaults = array(
		'show_option_none' => 'Category',
		'tab_index' => '4',
		'taxonomy' => 'category',
		'hide_empty' => '0',
		'selected' => $cid,
	);

	?>
             <?php wp_dropdown_categories($defaults);?>
            </p>
            <p><input type="submit" class="button button-primary button-large" value="Đăng bài" tabindex="6" id="submit" name="submit" /></p>
        </div>
    </div>
    </div>
    </form>
    </div>
<script>

jQuery(document).ready(function(){
tinyMCE.init({
        mode : "specific_textareas",
        theme : "simple",
        /*plugins : "autolink, lists, spellchecker, style, layer, table, advhr, advimage, advlink, emotions, iespell, inlinepopups, insertdatetime, preview, media, searchreplace, print, contextmenu, paste, directionality, fullscreen, noneditable, visualchars, nonbreaking, xhtmlxtras, template",*/
        editor_selector :"tinymce-enabled"
    });
});

</script>
</div>
<style>
.titledivff {
    padding: 10px 6px;
    border: 1px solid #ccc;
    margin: 10px 0;
    background: #ebebeb;
}
</style>
<?php wp_footer();?>
</body>
</html>
   <?php
}

function sw_save_post_if_submitted() {
	// Check that the nonce was set and valid
	if (!wp_verify_nonce($_POST['_wpnonce'], 'swp-frontend-post')) {
		//echo 'Did not save because your form seemed to be invalid. Sorry';
		return;
	}
	$ID = (int) $_POST['ID'];
	// Do some minor form validation to make sure there is content
//    if ( strlen($_POST['title'] ) < 3 ) {
//        echo 'Please enter a title. Titles must be at least three characters long';
//        return ;
//    }
//    if ( strlen($_POST['content']) < 100  ) {
//        echo 'Please enter content more than 100 characters in length';
//        return;
//    }
	// Add the content of the form to $post as an array
	$postfea = json_encode($_POST['postfea']);
	$post_type = $_POST['post_type'];
	if ($ID > 0) {
		$post = array(
			'ID' => $ID,
			'post_title' => $_POST['title'],
			'post_content' => $_POST['content'],
			'post_category' => $_POST['cat'],
			'post_status' => 'publish',
			'post_type' => $post_type, // Could be: `page` or your CPT
		);

		wp_update_post($post);
		$post_id = $ID;
		update_post_meta($post_id, 'linkgame', $_POST['linkgame']);
		update_post_meta($post_id, 'postfea', $postfea);

	} else {
		$post = array(
			'post_title' => $_POST['title'],
			'post_content' => $_POST['content'],
			'post_category' => $_POST['cat'],
			'post_status' => 'publish',
			'post_type' => $post_type, // Could be: `page` or your CPT
		);

		$post_id = wp_insert_post($post);

		$link = 'https://cgame1.mepop.net/media/core.php?ma=TMC6VLjz2wZmZpBD&tpp=game&pid=' . $post_id;

		add_post_meta($post_id, 'linkgame', $_POST['linkgame']);
		update_post_meta($post_id, 'postfea', $postfea);

	}

	?>
<?php
if (!function_exists('wp_generate_attachment_metadata')) {
		require_once ABSPATH . "wp-admin" . '/includes/image.php';
		require_once ABSPATH . "wp-admin" . '/includes/file.php';
		require_once ABSPATH . "wp-admin" . '/includes/media.php';
	}
	$link = 'https://cgame1.mepop.net/media/core.php?ma=TMC6VLjz2wZmZpBD&tpp=game&pid=' . $post_id;
	$linkhtml = get_permalink($post_id);
	$thumbnailname = $_FILES['thumbnail']['name'];

	if (strlen($thumbnailname)) {
		foreach ($_FILES as $file => $array) {
			if ($_FILES[$file]['error'] !== UPLOAD_ERR_OK) {
				return "upload error : " . $_FILES[$file]['error'];
			}
			$attach_id = media_handle_upload($file, $post_id);
		}
		if ($attach_id > 0) {
			//and if you want to set that image as Post  then use:
			update_post_meta($post_id, '_thumbnail_id', $attach_id);

		}
		$attach_link = wp_get_attachment_url($attach_id);
		?>
<script type="text/javascript">
parent.postMessage({vdata: {
    vpid:"<?php echo $post_id; ?>",
    vtitle: "<?php echo $_POST['title']; ?>",
    linkhtml: "<?php echo $linkhtml; ?>",
    imgdaidien: "<?php echo $attach_link; ?>",
    post_type: "<?php echo $post_type; ?>",
    pcat: "<?php echo $_POST['cat']; ?>"
    }}, "*");
</script>
<?php
} else {

		$attach_link = get_the_post_thumbnail_url($post_id);
		?>
<script type="text/javascript">

parent.postMessage({vdata: {
    vpid:"<?php echo $post_id; ?>",
    vtitle: "<?php echo $_POST['title']; ?>",
    linkhtml: "<?php echo $linkhtml; ?>",
    imgdaidien: "<?php echo $attach_link; ?>",
    post_type: "<?php echo $post_type; ?>",
    pcat: "<?php echo $_POST['cat']; ?>"
    }}, "*");
</script>
<?php

	}
	//echo 'Saved your post successfully! :)';
}

add_shortcode('sortableform', 'sortableform');
function sortableform() {
	ob_start();

	?>
	<link rel="stylesheet" href="//code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
  <script src="//code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
  <script>
  $( function() {
    $( ".sortable" ).sortable({
    axis: 'y',
    update: function (event, ui) {
        var data = $(this).sortable('serialize'),
            dataid = $(this).attr('data-id');

        // POST to server using $.post or $.ajax
        $.ajax({
            data: data+"&bb="+dataid,
            type: 'POST',
            url: '/wp-admin/admin-ajax.php'
        });
    }});
  } );
  </script>
   <style>
.width1ff {float: left;width: 100%;padding: 12px}
.width1 {width: 33%;float: left}
spanp.vsua {float: right;background: yellow;position: absolute;right: 3px;top: 3px;}
  #sortable { list-style-type: none; margin: 0; padding: 0;  }
  #sortable li { position: relative;margin: 0 3px 3px 3px; padding: 0.4em; padding-left: 1.5em; font-size: 1.4em; height: 36px; cursor: crosshair;}
  #sortable li span { position: absolute; margin-left: -1.3em; }
.vsua{cursor: pointer;}
ab.athemg {
    color: #ffffff;
    font-weight: bold;
    text-transform: uppercase;
    background: #166dba;
    padding: 6px 18px;
    border-radius: 14px;
    margin-left: 10px;cursor: pointer;
}.athemg img {
    margin-right: 6px;
}
  </style>
<div class="width1ff">
<div class="width1">
<label>Left</label> <ab class="athemg" href="#"><img src="//cms.mepop.net/media/img/athem1.png"><span>Thêm</span></ab>
<ul id="sortable" class="sortable" data-id="vleft">
  <li id="item-1" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 1 <spanp class="vsua">[Edit]</spanp></li>
  <li id="item-2" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 2 <spanp class="vsua">[Edit]</spanp></li>
  <li id="item-3" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 3 <spanp class="vsua">[Edit]</spanp></li>
</ul>
</div>
<div class="width1">
<label>Center</label>
<ul id="sortable" class="sortable" data-id="vcenter">
  <li id="item-1" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 1 <spanp class="vsua">[Edit]</spanp></li>
  <li id="item-2" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 2 <spanp class="vsua">[Edit]</spanp></li>
  <li id="item-3" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 3 <spanp class="vsua">[Edit]</spanp></li>
</ul>
</div>
<div class="width1">
<label>Right</label>
<ul id="sortable" class="sortable" data-id="vright">
  <li id="item-1" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 1 <spanp class="vsua">[Edit]</spanp></li>
  <li id="item-2" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 2 <spanp class="vsua">[Edit]</spanp></li>
  <li id="item-3" class="ui-state-default"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span>Item 3 <spanp class="vsua">[Edit]</spanp></li>
</ul>
</div>
</div>
<?php

	$html = ob_get_contents();
	ob_end_clean();
	return $html;

}

if (!function_exists('ftruyen_comment')):

	function ftruyen_comment($comment, $args, $depth) {
		$GLOBALS['comment'] = $comment;
		switch ($comment->comment_type):
	case 'pingback':
	case 'trackback':
/*
<li class="post pingback">
<p><?php _e( 'Bài liên quan:', 'ftruyen' ); ?> <?php comment_author_link(); ?><?php edit_comment_link( __( '(Sá»­a)', 'ftruyen' ), ' ' ); ?></p>
 */
		break;
	default:
		?>
										<li <?php comment_class();?> id="li-comment-<?php comment_ID();?>">

									<article id="comment-<?php comment_ID();?>" class="comment">
									<div class="comment-row">
									<div class="w3-left comment-left"> <img class="w3-circle avatarcm" alt="avatar " src="/media/images/avarta.png"> <img src="/media/images/default.png?2" class="avatar user-khungvien"></div>
									<div class="comment-right"> <a class="comment-user"><?php printf(__('%s<span class="says">:</span>', 'ftruyen'), sprintf('<cite class="fn">%s</cite>', get_comment_author_link()));?></a>
									<span class="comment-date"><?php	printf(__('%1$s', 'ftruyen'), get_comment_date(), get_comment_time());?></span>

									<div class="comment-chat"><?php echo makeClickableLinks(str_replace("\n", '<br />', get_comment_text())); ?></div>
									<div class="reply">
									<?php comment_reply_link(array_merge($args, array('reply_text' => __('Reply <span>&darr;</span>', 'ftruyen'), 'depth' => $depth, 'max_depth' => $args['max_depth'])));?>
									</div><!-- .reply -->


									</div></div>
									</article>

									<div class="clear"></div>
											</article><!-- #comment-## -->
									    </li>
										<?php
	break;
		endswitch;
	}
endif; // ends check for ftruyen_comment()

add_filter('wpseo_remove_reply_to_com', '__return_false');

function makeClickableLinks($s) {
	return preg_replace('@(https?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@', '<a onclick=\'window.open("$1");return false;\' href="#">$1</a>', $s);
}

// function add_rewrite_rules($wp_rewrite) {
// 	$new_rules = array(
// 		'get-game-(.+?)/?$' => 'index.php?post_type=games&name=' . $wp_rewrite->preg_index(1),
// 	);
// 	$wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
// }
// add_action('generate_rewrite_rules', 'add_rewrite_rules');

function change_blog_links($post_link, $id = 0) {
	$post = get_post($id);
	if (is_object($post) && $post->post_type == 'games') {
		return home_url('/get-' . $post->post_name . '');
	}
	return $post_link;
}
add_filter('post_link', 'change_blog_links', 1, 3);

function new_games($post_id, $post, $update) {
	if ($post->post_type == 'games' && $post->post_status == 'publish') {
		$title = $post->post_title;
		$link = get_permalink($post->ID);
		$token = "6021809494:AAGradAJCx-1hayE1rW8VTog8JKnkHOzQqQ";
		$chat_id = "-1001930434597";

		$chat = $title . ": " . $link;

		$data_qtri = [
			'chat_id' => $chat_id,
			'text' => "Update Review game: \n" . $chat,
		];

		file_get_contents("https://api.telegram.org/bot$token/sendMessage?" . http_build_query($data_qtri));
	}
}

add_action('post_updated', 'post_update_function', 10, 3);

function post_update_function($post_ID, $post_after, $post_before) {
	// on each change, call this api to notify the content engine
	$event = null;
	// if article is auto draft
	if ($post_before->post_status === 'auto-draft' && $post_after->post_status === 'publish') {
		// sendchat($post_ID);
		// if article is draft then published
	} else if ($post_before->post_status === 'draft' && $post_after->post_status === 'publish') {
		// sendchat($post_ID);
		// if its updated article
	} else if ($post_before->post_status === 'publish' && $post_after->post_status === 'publish') {
		// sendchat($post_ID);
		$event = 'updated_article';
	} else {
		return;
	}
	if ($event === null) {
		return;
	}
}

function sendchat($id) {
	// $title = get_the_title($id);
	// $link = get_permalink($id);
	// $token = "6021809494:AAGradAJCx-1hayE1rW8VTog8JKnkHOzQqQ";
	// $chat_id = "-1001930434597";

	// $chat = "Review game " . $title . ": " . $link;

	// $data_qtri = [
	// 	'chat_id' => $chat_id,
	// 	'text' => "Update Review game: \n" . $chat,
	// ];

	// file_get_contents("https://api.telegram.org/bot$token/sendMessage?" . http_build_query($data_qtri));
}

add_action('init', 'my_games_cpt');
function my_games_cpt() {
	$labels = array(
		'name' => _x('Games', 'post type general name', 'your-plugin-textdomain'),
		'singular_name' => _x('games', 'post type singular name', 'your-plugin-textdomain'),
		'menu_name' => _x('Games', 'admin menu', 'your-plugin-textdomain'),
		'name_admin_bar' => _x('games', 'add new on admin bar', 'your-plugin-textdomain'),
		'add_new' => _x('Add New', 'games', 'your-plugin-textdomain'),
		'add_new_item' => __('Add New games', 'your-plugin-textdomain'),
		'new_item' => __('New games', 'your-plugin-textdomain'),
		'edit_item' => __('Edit games', 'your-plugin-textdomain'),
		'view_item' => __('View games', 'your-plugin-textdomain'),
		'all_items' => __('All Games', 'your-plugin-textdomain'),
		'search_items' => __('Search Games', 'your-plugin-textdomain'),
		'parent_item_colon' => __('Parent Games:', 'your-plugin-textdomain'),
		'not_found' => __('No Games found.', 'your-plugin-textdomain'),
		'not_found_in_trash' => __('No Games found in Trash.', 'your-plugin-textdomain'),
	);
	$args = array(
		'labels' => $labels,
		'description' => __('Description.', 'your-plugin-textdomain'),
		'public' => true,
		'publicly_queryable' => true,
		'show_ui' => true,
		'show_in_menu' => true,
		'query_var' => true,
		'rewrite' => array('slug' => 'games'),
		'capability_type' => 'post',
		'has_archive' => true,
		'hierarchical' => false,
		'menu_position' => null,
		'show_in_rest' => true,
		'rest_base' => 'games',
		'supports' => array('title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments'),
	);
	register_post_type('games', $args);
	flush_rewrite_rules(true);
}
// ===========
add_action('init', 'my_games_taxonomy', 30);
function my_games_taxonomy() {
	$labels = array(
		'name' => _x('game', 'taxonomy general name'),
		'singular_name' => _x('game', 'taxonomy singular name'),
		'search_items' => __('Search game'),
		'all_items' => __('All game'),
		'parent_item' => __('Parent game'),
		'parent_item_colon' => __('Parent game:'),
		'edit_item' => __('Edit game'),
		'update_item' => __('Update game'),
		'add_new_item' => __('Add New game'),
		'new_item_name' => __('New game Name'),
		'menu_name' => __('game'),
	);
	$args = array(
		'hierarchical' => true,
		'labels' => $labels,
		'show_ui' => true,
		'show_admin_column' => true,
		'query_var' => true,
		'show_in_rest' => true,
		'rest_base' => 'game',
		'rewrite' => array(
			'slug' => 'game',
			'with_front' => true,
			'hierarchical' => true,
		),
	);
	register_taxonomy('game', array('games'), $args);
	flush_rewrite_rules(true);
}
function taxonomy_rewrite_fix($wp_rewrite) {
	$r = array();
	foreach ($wp_rewrite->rules as $k => $v) {
		$r[$k] = str_replace('game=$matches[1]&paged=', 'game=$matches[1]&page=', $v);
	}
	$wp_rewrite->rules = $r;
}
add_filter('generate_rewrite_rules', 'taxonomy_rewrite_fix');
add_action('init', function () {
	add_rewrite_rule('page/([a-z0-9-]+)[/]?$', 'index.php?page=$matches[1]', 'top');
});
function custom_pagination($numpages = '', $pagerange = '', $paged='') {

  if (empty($pagerange)) {
    $pagerange = 2;
  }

  /**
   * This first part of our function is a fallback
   * for custom pagination inside a regular loop that
   * uses the global $paged and global $wp_query variables.
   * 
   * It's good because we can now override default pagination
   * in our theme, and use this function in default quries
   * and custom queries.
   */
  global $paged;
  if (empty($paged)) {
    $paged = 1;
  }
  if ($numpages == '') {
    global $wp_query;
    $numpages = $wp_query->max_num_pages;
    if(!$numpages) {
        $numpages = 1;
    }
  }

  /** 
   * We construct the pagination arguments to enter into our paginate_links
   * function. 
   */
  $pagination_args = array(
    'base'            => get_pagenum_link(1) . '%_%',
    'format'          => 'page/%#%',
    'total'           => $numpages,
    'current'         => $paged,
    'show_all'        => False,
    'end_size'        => 1,
    'mid_size'        => $pagerange,
    'prev_next'       => True,
    'prev_text'       => __('&laquo;'),
    'next_text'       => __('&raquo;'),
    'type'            => 'plain',
    'add_args'        => false,
    'add_fragment'    => ''
  );

  $paginate_links = paginate_links($pagination_args);

  if ($paginate_links) {
    echo "<nav class='custom-pagination'>";
      echo "<span class='page-numbers page-num'>Page " . $paged . " of " . $numpages . "</span> ";
      echo $paginate_links;
    echo "</nav>";
  }

}
if (!class_exists('WPSE_78121_Sublevel_Walker')) {
	class WPSE_78121_Sublevel_Walker extends Walker_Nav_Menu {
		function start_lvl(&$output, $depth = 0, $args = array()) {
			$indent = str_repeat("\t", $depth);
			$output .= "\n$indent<ul class='dropdown-list'>\n";
		}
		function end_lvl(&$output, $depth = 0, $args = array()) {
			$indent = str_repeat("\t", $depth);
			$output .= "$indent</ul>\n";
		}
	}
}
add_action('template_redirect', 'webroom_force_ssl');
function webroom_force_ssl() {
	if (!is_ssl()) {
		wp_redirect('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], 301);
		exit();
	}
}
add_filter( 'rank_math/frontend/canonical', function( $canonical ) {
  $current_url="https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
  if(false === strpos($current_url, '/page/')) return $canonical;
  $canonical = strtok($current_url, '?');
  return $canonical;
});

Doc thêm bài viết

Học cách tạo một vòng lặp WordPress tùy chỉnh với phân trang là rất tốt. Không phải lúc nào bạn cũng muốn sử dụng truy vấn mặc định trên trang của mình, vì vậy việc thiết lập truy vấn tùy chỉnh trên một trang là khá tiện dụng. Điều này đặc biệt hữu ích cho những thời điểm bạn muốn hiển thị tất cả các bài đăng trên trang đầu tĩnh và bạn muốn giới hạn các bài đăng đó ở một số thông số nhất định. Trong trường hợp của chúng tôi, chúng tôi sẽ giả định các thông số sau:

  • Type of post: post

  • # of posts we want to display on our page: 5

  • Category that our post resides in: Tutorials

  • Pagination: Yes

Chúng tôi cũng sẽ thiết lập một chức năng phân trang tùy chỉnh cho phép chúng tôi kiểm tra xem phân trang có tồn tại / có sẵn hay không và nếu có, chúng tôi sẽ hiển thị số lượng trang và liên kết đến các trang kế tiếp cho truy vấn của chúng tôi. Chúng tôi cũng sẽ sử dụng truy vấn này trên Trang chính tĩnh, nơi phổ biến nhất để thiết lập truy vấn tùy chỉnh. Nếu bạn không biết cách thiết lập trang đầu tĩnh, tôi thực sự khuyên bạn nên đọc nhanh tài liệu này trên WordPress.

The WP_Query Class

Trước tiên, hãy xem tham chiếu lớp cho WP_Query. Tại đây, chúng ta có thể xem mọi thứ chúng ta cần biết về việc tạo một phiên bản mới của WP_Query và kết quả là tạo một vòng lặp mới. Bạn nên tự làm quen và đánh dấu tài liệu tham khảo này vì đối với bất kỳ dự án nào yêu cầu hiển thị tùy chỉnh các trang hoặc bài đăng, đây là hướng dẫn của bạn. Đặc biệt quan tâm là các phần “phương pháp và thuộc tính” và “tham số”. Các phương thức và thuộc tính của truy vấn của chúng tôi cung cấp cho chúng tôi những cách mà chúng tôi có thể tương tác với truy vấn sau khi chúng tôi khởi tạo nó và các tham số là những gì chúng tôi sẽ đặt trong phần khởi tạo của truy vấn của chúng tôi. Các tham số này sẽ hoạt động như các đối số của chúng tôi và sẽ cho phép chúng tôi điều chỉnh truy vấn để chỉ hiển thị kết quả chúng tôi cần.

Interacting with WP_Query & Usage

Để bắt đầu, bạn nên đọc phần tương tác với WP_Query để biết cách bạn có thể tương tác với nó khi đã thiết lập xong. Hãy xem cách sử dụng cơ bản được ghi lại trên trang tham khảo của lớp.

<?php 
// the query
$the_query = new WP_Query( $args ); ?>
<?php if ( $the_query->have_posts() ) : ?>
  <!-- pagination here -->
    <!-- the loop -->
      <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
        <h2><?php the_title(); ?></h2>
      <?php endwhile; ?>
    <!-- end of the loop -->
  <!-- pagination here -->
  <?php wp_reset_postdata(); ?>
<?php else:  ?>
  <p><?php _e( 'Sorry, no posts matched your criteria.' ); ?></p>
<?php endif; ?>

Trong mẫu mã do WordPress cung cấp, chúng tôi thấy một vài điều đang xảy ra. Chúng tôi tạo một biến có tên là $ the_query và đặt nó bằng một phiên bản mới của WP_Query. Bên trong WP_Query, chúng tôi gửi một biến có tên là $ args. Biến này chứa tất cả các đối số của chúng ta cho truy vấn. Khi truy vấn mới được thiết lập, chúng ta có thể tương tác với nó. Chúng tôi bắt đầu bằng cách xem liệu có bất kỳ bài đăng nào để hiển thị dựa trên truy vấn của chúng tôi hay không. Chúng tôi thực hiện việc này bằng cách kiểm tra xem $ the_query-> have_posts () có trả về true hay không. Nếu đúng, thì chúng ta có thể vào vòng lặp của mình. Chúng tôi thực hiện điều này bằng cách sử dụng vòng lặp while với has_posts () làm điều kiện. Sau đó, trong mỗi lần lặp, chúng ta gọi $ the_query-> the_post () để thiết lập các biến nội bộ bên trong $ the_query và đặt biến $ post toàn cục. Bây giờ, bên trong vòng lặp của chúng tôi, chúng tôi có thể gọi bất kỳ hàm vòng lặp nào cũng như các phương thức và thuộc tính truy vấn và tùy chỉnh vòng lặp của chúng tôi. Trong mẫu ở trên, chúng tôi đang xuất ra tiêu đề của mỗi bài đăng trong truy vấn của mình bằng cách sử dụng hàm the_title (). Bạn nên khôi phục dữ liệu bài đăng gốc của trang bạn đang truy cập sau khi sử dụng truy vấn tùy chỉnh. Điều này được thực hiện bằng cách gọi hàm wp_reset_postdata ().

Setting Up Our Arguments

Như đã lưu ý ở trên, chúng tôi có bốn đối số mà chúng tôi muốn chuyển vào truy vấn của mình. Đây là một lần nữa để tham khảo nhanh:

  • Type of post: post

  • # of posts we want to display on our page: 5

  • Category that our post resides in: Tutorials

  • Pagination: Yes

Biến đối số của chúng ta có thể được chuyển vào dưới dạng một mảng, vì vậy hãy làm điều đó. Trước tiên, chúng tôi sẽ tạo mảng và gán nó cho một biến có tên là $ args, sau đó chuyển mảng đó vào phiên bản WP_Query mới của chúng tôi. Nếu chúng ta kiểm tra phần thông số trong mục lục, chúng ta sẽ thấy các liên kết đến mọi thứ chúng ta cần. Đầu tiên chúng ta hãy kiểm tra “thông số loại” sẽ cho phép chúng tôi đặt loại bài đăng của mình.

The Type Parameter

Tham số type cho phép chúng tôi hiển thị các bài đăng được liên kết với một số loại nhất định. Có một số có sẵn như đã thấy trong tài liệu. Trong trường hợp của chúng tôi, chúng tôi đang tìm kiếm một bài đăng. May mắn cho chúng tôi, đây là loại mặc định nếu bạn không chỉ định không, vì vậy chúng tôi thực sự không cần chỉ định nó. Nhưng để dự phòng và làm quen, chúng tôi vẫn sẽ thiết lập nó. Cho đến nay, biến đối số của chúng ta sẽ trông như thế này:

$query_args = array(
  'post_type' => 'post'
);

Bây giờ, hãy đặt thông số danh mục của chúng tôi. Chúng ta sẽ kiểm tra "thông số danh mục" để xem chúng ta cần bao gồm những gì.

The Category Parameter

Chúng tôi biết rằng chúng tôi chỉ muốn hiển thị các bài đăng từ danh mục “Hướng dẫn” của chúng tôi, vì vậy, hãy xem những gì có sẵn cho chúng tôi. Chúng ta có thể thấy ngay rằng chúng ta có một tham số theo ý mình mà chúng ta có thể sử dụng. Đây là thông số "category_name". Từ định nghĩa, chúng ta thấy rằng tham số này yêu cầu loại slug, không phải tên thực. Trong bảng quản trị WordPress của chúng tôi, chúng tôi có thể lấy slug cho danh mục của chúng tôi được đề cập. Trong trường hợp này, nó là "hướng dẫn". Bây giờ chúng ta có thể thêm điều này vào biến đối số của mình, bây giờ sẽ trông giống như sau:

$query_args = array(
  'post_type' => 'post',
  'category_name' => 'tutorials'
);

Bây giờ chúng ta hãy xem chỉ hiển thị một số bài đăng nhất định trên mỗi trang và thêm phân trang cho các bài đăng của chúng ta. Nhìn lướt qua mục lục, chúng ta thấy rằng chúng ta cần tìm hiểu kỹ phần gọi là “tham số phân trang”. Phần này cho chúng ta biết tất cả về cấu trúc bài đăng thành các trang khác nhau.

Pagination Parameters

Phần này của WP_Query có xu hướng khiến mọi người nhầm lẫn, nhưng chúng tôi sẽ xem xét và làm cho nó rất đơn giản. Điều đầu tiên chúng tôi muốn làm là giới hạn số lượng bài đăng hiển thị trên mỗi trang trong truy vấn được phân trang của chúng tôi. Để làm điều này, chúng tôi sử dụng tham số “posts_per_page” rất tự giải thích. Trong trường hợp của chúng tôi, chúng tôi muốn 5 bài đăng hiển thị trên mỗi trang. Truy vấn của chúng tôi bây giờ sẽ giống như sau:

$query_args = array(
  'post_type' => 'post',
  'category_name' => 'tutorials',
  'posts_per_page' => 5
);

Bây giờ, hãy xem xét việc phân trang các bài đăng. Trong tham chiếu lớp, chúng ta thấy hai định nghĩa trong phần tham số phân trang. Một cho “trang” và một cho “trang”. Hai định nghĩa như sau:

paged (int) - số trang. Hiển thị các bài đăng thường chỉ hiển thị trên trang X khi sử dụng liên kết "Bài viết cũ hơn".
page (int) - số trang cho một trang đầu tĩnh. Hiển thị các bài đăng thường chỉ hiển thị trên trang X của Trang chính tĩnh.

Trong trường hợp của chúng tôi, vì chúng tôi đang thiết lập truy vấn của mình trên trang đầu tĩnh, chúng tôi cần sử dụng tham số “page”. Đây thường là phần mà nhiều người nhầm lẫn, nhưng tài liệu luôn ở đây để giải cứu. Đoạn văn bản sau được lấy trực tiếp từ tài liệu:

Phân trang Lưu ý: Sử dụng get_query_var (‘page’); nếu bạn muốn truy vấn của mình hoạt động trong Mẫu trang mà bạn đã đặt làm trang đầu tĩnh của mình… Hiển thị các bài đăng từ trang hiện tại trên trang đầu tĩnh:

$paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1; 
$query = new WP_Query( array( 'paged' => $paged ) );

Làm thế nào đơn giản là vậy? Họ đã cung cấp cho chúng tôi thông số mà chúng tôi cần đưa vào truy vấn của mình để cho phép phân trang. Bây giờ, truy vấn của chúng tôi sẽ trông giống như thế này cho các trang chủ tĩnh:

$paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1;
$query_args = array(
  'post_type' => 'post',
  'category_name' => 'tutorials',
  'posts_per_page' => 5,
  'paged' => $paged
);

Hãy nhớ rằng, đối với các trang tùy chỉnh KHÔNG phải là trang chủ tĩnh, biến $ paged sẽ thay đổi thành:

$paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1;

Và cùng với đó, chúng tôi đã sẵn sàng thiết lập vòng lặp, thực hiện kiểm tra phân trang và hiển thị các liên kết phân trang nếu chúng xảy ra. Tất nhiên, chúng sẽ chỉ xảy ra nếu truy vấn của chúng tôi mang lại nhiều hơn số lượng bài đăng trên mỗi trang mà chúng tôi đã chỉ định. Trong trường hợp này, con số đó là 5. Hãy xây dựng vòng lặp của chúng ta!

Constructing The WordPress Loop

Bây giờ, đã đến lúc tạo vòng lặp. Khi chúng tôi bắt đầu vòng lặp, chúng tôi sẽ có quyền truy cập vào một loạt các chức năng của WordPress cho phép chúng tôi hiển thị những thứ như tiêu đề của bài đăng, đoạn trích của bài đăng và nội dung bài đăng. Vì đơn giản, chúng tôi sẽ chỉ xuất tiêu đề bài đăng. Tất nhiên, bạn có thể muốn hiển thị nhiều hơn thế, vì vậy hãy tìm hiểu kỹ Tham khảo chức năng của WordPress để biết một loạt các chức năng có sẵn cho bạn. Hãy cùng xem tài liệu “Vòng lặp” trên WordPress codex. Sau khi xem nhanh, chúng ta có thể thấy rằng vòng lặp được khởi tạo như sau:

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
    <!-- some code here -->
<?php endwhile; else: ?>
    <p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
<?php endif; ?>

Đây là cú pháp vòng lặp cơ bản, nhưng vì chúng tôi đang sử dụng truy vấn tùy chỉnh của mình, chúng tôi phải truy cập vòng lặp bằng cách sử dụng biến truy vấn đại diện cho một phiên bản mới của WP_Query. Từ trên, phiên bản truy vấn của chúng tôi được ghi nhận bởi biến $ the_query. Vòng lặp của chúng ta, trong trường hợp này, sẽ giống như sau:

<?php if ( $the_query->have_posts() ) : while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
  <!-- some code here -->
<?php endwhile; else: ?>
  <p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
<?php endif; ?>

Bộ mã trên cung cấp một bộ chức năng nhất định. Đầu tiên, nó kiểm tra xem có bất kỳ bài đăng nào được gửi đi do truy vấn tùy chỉnh của chúng tôi hay không. Nếu điều này là đúng và chúng tôi có bài đăng, chúng tôi sẽ tham gia vào một vòng lặp while. Vòng lặp while lặp lại qua truy vấn của chúng tôi, sau đó thực hiện một bộ mã cho mỗi bài đăng được trả về. Đây là nơi chúng tôi có thể xuất những thứ như mỗi tiêu đề bài đăng, v.v. Nếu không có bài đăng nào được trả lại do truy vấn của chúng tôi, chúng tôi nhập phần khác của khối và trả về một câu lệnh lỗi thông báo cho người dùng rằng không có bài đăng nào tồn tại dựa trên truy vấn. Hãy cải thiện vòng lặp này một chút và làm cho nó thân thiện với người dùng hơn một chút để nó thực sự hiển thị một cái gì đó nếu chúng ta có bài đăng. Sử dụng hàm the_title () của WordPress, chúng tôi sẽ xuất ra tiêu đề của mỗi bài đăng được trả về. Vòng lặp của chúng ta bây giờ trông như thế này:

<?php if ( $the_query->have_posts() ) : while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
  <h1><?php echo the_title(); ?></h1>
<?php endwhile; else: ?>
  <h1>Sorry...</h1>
  <p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
<?php endif; ?>

Và Voila! Vòng lặp của chúng tôi hiện đang xuất các bài đăng dựa trên truy vấn tùy chỉnh của chúng tôi. Bây giờ, thời gian cho một số chức năng ngọt ngào. Chúng tôi muốn phân trang vòng lặp của mình, điều này đặc biệt hữu ích khi chúng tôi có nhiều bài đăng và chỉ muốn hiển thị một lượng bài viết nhất định trên mỗi trang. Như chúng tôi đã lưu ý ở trên, chúng tôi chỉ hiển thị 5 bài đăng trên mỗi trang dựa trên truy vấn của chúng tôi. Giả sử rằng truy vấn của chúng tôi đang trả về 20 bài đăng (hoặc bất kỳ số tiền nào nhiều hơn 5). Chúng tôi muốn người dùng có thể truy cập các bài đăng cũ hơn nếu họ muốn. Nhưng bằng cách nào?

Paginating The Custom Loop

Bây giờ, vòng lặp của chúng ta đã sẵn sàng để phân trang, hãy cùng xem một số chức năng cốt lõi của WordPress. Nếu chúng ta xem Tham khảo hàm WordPress, chúng ta sẽ bắt gặp hai hàm có tên get_next_posts_link và get_previous_posts_link. Lưu ý rằng cũng có hai hàm next_posts_link và before_posts_link, nhưng theo tham chiếu hàm, chúng không hoạt động trên các trang tĩnh, do đó làm cho chúng vô hiệu trong trường hợp của chúng tôi. Chúng ta hãy xem xét hai chức năng chúng ta cần.

Hàm get_next_posts_link, như được mô tả ở đây trong tham chiếu hàm, nhận liên kết đến nhóm bài đăng trước đó trong truy vấn hiện tại. Theo tài liệu tham khảo:

Vì các truy vấn bài đăng thường được sắp xếp theo thứ tự thời gian ngược lại, get_next_posts_link () thường trỏ đến các mục cũ hơn (về cuối tập hợp) và get_previous_posts_link () thường trỏ đến các mục nhập mới hơn (về đầu tập hợp).

Nói một cách dễ hiểu, giờ đây chúng tôi có thể hiển thị các liên kết rất nhanh chóng và dễ dàng tới các nhóm bài đăng cũ hơn và mới hơn nếu chúng tồn tại (tức là nếu truy vấn của chúng tôi trả lại nhiều bài đăng hơn giá trị chúng tôi đặt trong thông số bài đăng trên mỗi trang). Hãy xem cách chúng tôi sử dụng hàm get_next_posts_link khi chúng tôi đang truy vấn một vòng lặp với WP_Query. Theo tài liệu,

Thêm tham số $ max_pages vào hàm get_next_posts_link () khi truy vấn vòng lặp với WP_Query. Để nhận tổng số trang, bạn có thể sử dụng thuộc tính max_num_pages của đối tượng WP_Query tùy chỉnh.

The Full Query With Next & Previous Post Links

Bây giờ, truy vấn tùy chỉnh đầy đủ của chúng tôi, với các liên kết phân trang đơn giản (tức là các liên kết trỏ đến các trang có bài đăng cũ hơn và mới hơn) sẽ trông như thế này:

Bây giờ, truy vấn tùy chỉnh đầy đủ của chúng tôi, với các liên kết phân trang đơn giản (tức là các liên kết trỏ đến các trang có bài đăng cũ hơn và mới hơn) sẽ trông như thế này:

Static Home Page

<?php
  // set up or arguments for our custom query
  $paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1;;
  $query_args = array(
    'post_type' => 'post',
    'category_name' => 'tutorials',
    'posts_per_page' => 5,
    'paged' => $paged
  );
  // create a new instance of WP_Query
  $the_query = new WP_Query( $query_args );
?>

<?php if ( $the_query->have_posts() ) : while ( $the_query->have_posts() ) : $the_query->the_post(); // run the loop ?>
  <article>
    <h1><?php echo the_title(); ?></h1>
    <div class="excerpt">
      <?php the_excerpt(); ?>
    </div>
  </article>
<?php endwhile; ?>

<?php if ($the_query->max_num_pages > 1) { // check if the max number of pages is greater than 1  ?>
  <nav class="prev-next-posts">
    <div class="prev-posts-link">
      <?php echo get_next_posts_link( 'Older Entries', $the_query->max_num_pages ); // display older posts link ?>
    </div>
    <div class="next-posts-link">
      <?php echo get_previous_posts_link( 'Newer Entries' ); // display newer posts link ?>
    </div>
  </nav>
<?php } ?>

<?php else: ?>
  <article>
    <h1>Sorry...</h1>
    <p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
  </article>
<?php endif; ?>

Custom Static Page (that isn’t home page)

<?php
  // set up or arguments for our custom query
  $paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1;
  $query_args = array(
    'post_type' => 'post',
    'category_name' => 'tutorials',
    'posts_per_page' => 5,
    'paged' => $paged
  );
  // create a new instance of WP_Query
  $the_query = new WP_Query( $query_args );
?>

<?php if ( $the_query->have_posts() ) : while ( $the_query->have_posts() ) : $the_query->the_post(); // run the loop ?>
  <article>
    <h1><?php echo the_title(); ?></h1>
    <div class="excerpt">
      <?php the_excerpt(); ?>
    </div>
  </article>
<?php endwhile; ?>

<?php if ($the_query->max_num_pages > 1) { // check if the max number of pages is greater than 1  ?>
  <nav class="prev-next-posts">
    <div class="prev-posts-link">
      <?php echo get_next_posts_link( 'Older Entries', $the_query->max_num_pages ); // display older posts link ?>
    </div>
    <div class="next-posts-link">
      <?php echo get_previous_posts_link( 'Newer Entries' ); // display newer posts link ?>
    </div>
  </nav>
<?php } ?>

<?php else: ?>
  <article>
    <h1>Sorry...</h1>
    <p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
  </article>
<?php endif; ?>

Bây giờ chúng tôi đã tạo các liên kết đến các trang trước và trang sau. Điều này thật tuyệt, và nó hoạt động, nhưng nếu chúng ta muốn một cấu trúc phân trang nâng cao hơn thì sao? Điều gì sẽ xảy ra nếu chúng ta muốn hiển thị số lượng trang, liên kết đến số trang thực tế và liên kết đến trang cuối cùng và đầu tiên nếu chúng ta có nhiều trang? Đối với điều này, chúng ta cần viết một hàm phức tạp hơn một chút dựa trên các nguyên tắc tương tự như trên. Chúng tôi đã biết một số điều sẽ giúp chúng tôi:

Chúng tôi có thể kiểm tra xem phân trang có tồn tại hay không Chúng tôi có thể kiểm tra số lượng trang tối đa hoặc trả về truy vấn dựa trên số lượng bài viết được trả lại so với số lượng bài viết trên mỗi trang được hiển thị Bây giờ chúng ta hãy xem xét thiết lập chức năng của chúng tôi.

Advanced Pagination Function

Chúng tôi muốn tạo một hàm chấp nhận ba giá trị: 1. The number of pages, tức là số trang của các bài đăng được trả về bởi truy vấn của chúng tôi. Điều này được thuộc tính $ max_num_pages 2. The page range. Đây là một loạt các trang mà chúng tôi sẽ hiển thị để người dùng có thể nhấp vào. Chúng tôi muốn nó là một số chẵn

3. Giá trị $ paged, sẽ khác với các trang chủ tĩnh và các trang tùy chỉnh khác.

Chúng tôi cũng muốn hàm của mình có các giá trị dự phòng, để chúng tôi có thể sử dụng hàm này trong bất kỳ truy vấn tùy chỉnh nào mà chúng tôi thiết lập và cả trên bất kỳ trang nào có truy vấn toàn cục (ví dụ: trang lưu trữ danh mục). Cho đến nay, hàm của chúng ta sẽ trông như thế này:

function custom_pagination($numpages = '', $pagerange = '', $paged='') {
  if (empty($pagerange)) {
    $pagerange = 2;
  }
  /**
   * This first part of our function is a fallback
   * for custom pagination inside a regular loop that
   * uses the global $paged and global $wp_query variables.
   * 
   * It's good because we can now override default pagination
   * in our theme, and use this function in default quries
   * and custom queries.
   */
  global $paged;
  if (empty($paged)) {
    $paged = 1;
  }
  if ($numpages == '') {
    global $wp_query;
    $numpages = $wp_query->max_num_pages;
    if(!$numpages) {
        $numpages = 1;
    }
  }
}

Bây giờ mọi thứ đã được thiết lập, chúng ta cần tìm ra cách xuất cấu trúc phân trang. May mắn thay, WordPress xử lý điều này một cách tuyệt vời cho chúng tôi bằng một chức năng cốt lõi paginate_links. Hãy xem tham chiếu chức năng cho chức năng này và xem những gì nó cung cấp. Dưới đây là định nghĩa về hàm trực tiếp từ tài liệu:

Truy xuất liên kết được phân trang cho các trang lưu trữ bài đăng. Về mặt kỹ thuật, hàm có thể được sử dụng để tạo danh sách liên kết được phân trang cho bất kỳ khu vực nào

Nếu chúng ta đọc qua, chúng ta nhận thấy rằng hàm nhận một mảng đối số. Bạn nên đọc qua phần này và làm quen với ý nghĩa của mỗi đối số. Tuy nhiên, bây giờ hãy cuộn xuống phần “ví dụ với truy vấn tùy chỉnh”. Chúng tôi sẽ sử dụng cái này làm mẫu, nhưng chỉnh sửa một vài thứ. Trước hết, chúng tôi đã thiết lập số lượng trang (tức là số trang tối đa) bằng biến $ numpages. Chúng tôi cũng biết số trang hiện tại của chúng tôi được đặt bởi biến $ paged. Đối số ‘mid_size’ biểu thị “có bao nhiêu số ở một trong hai bên của trang hiện tại, nhưng không bao gồm trang hiện tại”. Chúng tôi sẽ sử dụng biến $ pagerange của chúng tôi ở đây, biến này được mặc định là 2 nếu không được chuyển qua hàm. Cuối cùng, chúng ta sẽ đặt các nút trước đó và nút tiếp theo của chúng ta thành mũi tên kép, và xuất phân trang của chúng ta ở định dạng đơn giản. Hai đối số quan trọng mà chúng tôi sẽ chỉnh sửa là:

base: chúng tôi sẽ không sử dụng gợi ý mặc định từ tham chiếu, vì trên các trang cao hơn 1, trang 1 tham chiếu trở lại “pageurl / page / 1” và chúng tôi muốn nó trỏ trở lại tham chiếu “pageurl”. Chúng tôi sẽ sử dụng một hàm WordPress khác thường hơn không được ghi lại trong tham chiếu hàm, được gọi là getpagenum_link (). Hàm này trả về url của một trang được phân trang khi bạn chuyển số trang vào. Nếu số trang là 1, nó được mặc định là url trang cơ sở. Cũng theo định nghĩa, “% %” trong đối số này sẽ được thay thế bằng đối số “định dạng” trên mỗi trang. Đối số ‘base’ của chúng ta sau đó sẽ giống như sau: 'base' => getpagenum_link (1). ‘% %’

format: trong trường hợp của tôi, tôi đang sử dụng các liên kết cố định khá tốt, vì vậy tôi sẽ không tuân theo định dạng mặc định. Tôi sẽ sử dụng định dạng “page /% #%”, định dạng này sẽ kết hợp với đối số "base" để xuất url ở định dạng này: nếu đó là trang 1, nó sẽ xuất ra “pageurl” bất kỳ trang nào cao hơn trang 1 sẽ cho kết quả là “pageurl / page / pagenumber” Các đối số của chúng ta bây giờ sẽ như thế này:

/** 
 * We construct the pagination arguments to enter into our paginate_links
 * function. 
 */
$pagination_args = array(
  'base'            => get_pagenum_link(1) . '%_%',
  'format'          => 'page/%#%',
  'total'           => $numpages,
  'current'         => $paged,
  'show_all'        => False,
  'end_size'        => 1,
  'mid_size'        => $pagerange,
  'prev_next'       => True,
  'prev_text'       => __('&laquo;'),
  'next_text'       => __('&raquo;'),
  'type'            => 'plain',
  'add_args'        => false,
  'add_fragment'    => ''
);

Cuối cùng, hãy xuất mọi thứ. Chúng ta chuyển các đối số của mình vào hàm paginate_links, hàm này sẽ trả về cấu trúc phân trang nếu chúng ta có nhiều hơn một trang. Sau đó, chúng tôi có thể kiểm tra xem paginate_links có trả về bất kỳ thứ gì không và nếu có, hãy chạy đầu ra bao gồm một số nội dung bổ sung như “trang x của y”. Chúng tôi sẽ thêm một vài lớp CSS và cũng kiểm tra các lớp trong đầu ra của chúng tôi và thêm các kiểu vào biểu định kiểu của chúng tôi cho phù hợp. Chúng ta nên có một cái gì đó giống như sau:

$paginate_links = paginate_links($pagination_args);
if ($paginate_links) {
  echo "<nav class='custom-pagination'>";
    echo "<span class='page-numbers page-num'>Page " . $paged . " of " . $numpages . "</span> ";
    echo $paginate_links;
  echo "</nav>";
}

The Full Code

Hãy xem mã đầy đủ cho các truy vấn phân trang tùy chỉnh trên trang chủ tĩnh và mẫu trang tùy chỉnh không phải là trang chủ.

Static Home Page

<?php
/**
 * Template Name: Home Page
 * The home page template file
 */
?>

<?php get_header(); ?>

<h2>Posts</h2>

<?php 

  $paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1;;

  $query_args = array(
      'post_type' => 'post',
      'posts_per_page' => 2,
      'paged' => $paged,
      'page' => $paged
    );

  $the_query = new WP_Query( $query_args ); ?>

  <?php if ( $the_query->have_posts() ) : ?>

    <!-- the loop -->
    <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
      <article class="loop">
        <h3><?php the_title(); ?></h3>
        <div class="content">
          <?php the_excerpt(); ?>
        </div>
      </article>
    <?php endwhile; ?>
    <!-- end of the loop -->

    <!-- pagination here -->
    <?php
      if (function_exists(custom_pagination)) {
        custom_pagination($the_query->max_num_pages,"",$paged);
      }
    ?>

  <?php wp_reset_postdata(); ?>

  <?php else:  ?>
    <p><?php _e( 'Sorry, no posts matched your criteria.' ); ?></p>
  <?php endif; ?>

<?php get_footer(); ?>

Custom Page That Isn’t Static Home Page

<?php
/**
 * Template Name: Custom Page
 * The custom page template file
 */
?>

<?php get_header(); ?>

<h2>Posts</h2>

<?php 

  $paged = ( get_query_var('paged') ) ? get_query_var('paged') : 1;

  $custom_args = array(
      'post_type' => 'post',
      'posts_per_page' => 2,
      'paged' => $paged
    );

  $custom_query = new WP_Query( $custom_args ); ?>

  <?php if ( $custom_query->have_posts() ) : ?>

    <!-- the loop -->
    <?php while ( $custom_query->have_posts() ) : $custom_query->the_post(); ?>
      <article class="loop">
        <h3><?php the_title(); ?></h3>
        <div class="content">
          <?php the_excerpt(); ?>
        </div>
      </article>
    <?php endwhile; ?>
    <!-- end of the loop -->

    <!-- pagination here -->
    <?php
      if (function_exists(custom_pagination)) {
        custom_pagination($custom_query->max_num_pages,"",$paged);
      }
    ?>

  <?php wp_reset_postdata(); ?>

  <?php else:  ?>
    <p><?php _e( 'Sorry, no posts matched your criteria.' ); ?></p>
  <?php endif; ?>

<?php get_footer(); ?>

Function.php File

function custom_pagination($numpages = '', $pagerange = '', $paged='') {

  if (empty($pagerange)) {
    $pagerange = 2;
  }

  /**
   * This first part of our function is a fallback
   * for custom pagination inside a regular loop that
   * uses the global $paged and global $wp_query variables.
   * 
   * It's good because we can now override default pagination
   * in our theme, and use this function in default quries
   * and custom queries.
   */
  global $paged;
  if (empty($paged)) {
    $paged = 1;
  }
  if ($numpages == '') {
    global $wp_query;
    $numpages = $wp_query->max_num_pages;
    if(!$numpages) {
        $numpages = 1;
    }
  }

  /** 
   * We construct the pagination arguments to enter into our paginate_links
   * function. 
   */
  $pagination_args = array(
    'base'            => get_pagenum_link(1) . '%_%',
    'format'          => 'page/%#%',
    'total'           => $numpages,
    'current'         => $paged,
    'show_all'        => False,
    'end_size'        => 1,
    'mid_size'        => $pagerange,
    'prev_next'       => True,
    'prev_text'       => __('&laquo;'),
    'next_text'       => __('&raquo;'),
    'type'            => 'plain',
    'add_args'        => false,
    'add_fragment'    => ''
  );

  $paginate_links = paginate_links($pagination_args);

  if ($paginate_links) {
    echo "<nav class='custom-pagination'>";
      echo "<span class='page-numbers page-num'>Page " . $paged . " of " . $numpages . "</span> ";
      echo $paginate_links;
    echo "</nav>";
  }

}

Style.css File

/* ============================================================
  CUSTOM PAGINATION
============================================================ */
.custom-pagination span,
.custom-pagination a {
  display: inline-block;
  padding: 2px 10px;
}
.custom-pagination a {
  background-color: #ebebeb;
  color: #ff3c50;
}
.custom-pagination a:hover {
  background-color: #ff3c50;
  color: #fff;
}
.custom-pagination span.page-num {
  margin-right: 10px;
  padding: 0;
}
.custom-pagination span.dots {
  padding: 0;
  color: gainsboro;
}
.custom-pagination span.current {
  background-color: #ff3c50;
  color: #fff;
}

Đọc thêm bài viết https://stackoverflow.com/questions/60012883/pagination-not-working-with-custom-url-structure-of-cpt-taxonomy

Ask QuestionAsked 3 years, 4 months agoModified 2 years, 1 month agoViewed 841 times0

I'm fairly new to posting here and I'll try to keep it as clear as possible! Any guidance is greatly appreciated!

The Goal:


I am trying to create a Custom Post Type and a Taxonomy where I can upload posts inside the Taxonomy and have a URL structure like this: site-name.com/cpt-slug/taxonomy-slug/post-slu.

I also need to be able to have pagination on both site-name.com/cpt-slug/ and site-name.com/cpt-slug/taxonomy-slug

Currently:


I have studied a few other posts that have gotten me 99% of the way there! I just can't quite figure out how to finish it at this point. I have done most of what this answer has suggested and i will show my code below for clarity.

CPT registration

register_post_type( 'knowledge_base',
        array(
            'labels' => array(
                'name' => __( 'Knowledge Base' ),
                'singular_name' => __( 'Knowledge Base Post' )
            ),
            'public' => true,
            'has_archive' => true,
            'menu_position' => 25,
            'menu_icon' => 'dashicons-book',
            'hierarchical' => true,
            'supports' => array(
                'title',
                'editor',
                'excerpt',
                'page-attributes',
                'thumbnail'
            ),
            'taxonomies' => array('kb_topics'),
            'rewrite' => array(
                'slug' => 'kb/%taxonomy_name%',
                'with_front' => false
            ),
        )
    );

Custom taxonomy registration

register_taxonomy(
        'kb_topics',
        'knowledge_base',
        array(
            'label' => 'Categories',
            'hierarchical' => true,
            'rewrite' => array(
                'slug' => 'kb',
                'with_front' => false
            ),
        )
    );

Tell WordPress how to interpret my knowledge base URL structure:

function add_rewrite_rules( $rules ) {
  $new = array();
  $new['kb/([^/]+)/(.+)/?$'] = 'index.php?knowledge_base=$matches[2]';
  $new['kb/(.+)/?$'] = 'index.php?kb_topics=$matches[1]';

  return array_merge( $new, $rules ); // Ensure our rules come first
}
add_filter( 'rewrite_rules_array', 'add_rewrite_rules' );

Handle the %taxonomy_name% URL placeholder

function filter_post_type_link( $link, $post ) {
  if ( $post->post_type == 'knowledge_base' ) {
    if ( $cats = get_the_terms( $post->ID, 'kb_topics' ) ) {
      $link = str_replace( '%taxonomy_name%', current( $cats )->slug, $link );
    }
  }
  return $link;
}
add_filter( 'post_type_link', 'filter_post_type_link', 10, 2 );

The problem:


Overall the post I referenced makes a ton of sense, I feel I understand whats going on here and everything was good until I tried setting up Pagination on the page site-name.com/kb.

This page was working great. I was able to show all the posts per category and I was able to go from here and click any post or Taxonomy and have the URL structure mentioned above. However whenever I try to go to the next page or third page I either get a 404 error or I get redirected to a post in my CPT.

As an example site-name.com/kb/page/2 always goes to the same post that is a post in my CPT and site-name.com/kb/page/3 always goes to 404. After some digging around some more I found another post that seemed to have a very promising answer and I still feel it could be the right answer I just can't get it to work. Admittedly I don't have a lot of experience with rewrites and this may be where the issue is.

Here is my version of this user's suggestion:

function fix_kb_category_pagination( $wp_rewrite ) {
    unset($wp_rewrite->rules['kb/([^/]+)/page/?([0-9]{1,})/?$']);
    $wp_rewrite->rules = array(
        'kb/?$' => $wp_rewrite->index . '?post_type=knowledge_base',
        'kb/page/?([0-9]{1,})/?$' => $wp_rewrite->index . '?post_type=knowledge_base&paged=' . $wp_rewrite->preg_index( 1 ),
        'kb/([^/]+)/page/?([0-9]{1,})/?$' => $wp_rewrite->index . '?kb_topics=' . $wp_rewrite->preg_index( 1 ) . '&paged=' . $wp_rewrite->preg_index( 2 ),
    ) + $wp_rewrite->rules;
}
add_action( 'generate_rewrite_rules', 'fix_kb_category_pagination' );

I have tested the pagination and my page template by creating a different page with a slug of /kb-test/ and everything works perfectly. So this only happens on /kb/ for some reason.

From what I understand from the posts I mentioned above, WordPress has created rewrites based on the CPT and Taxonomy I set up and so its not able to go to /kb/page/, but I have tried many times now tweaking the code I have here trying to get it to recognize /kb/page/ but to no avail.

Thank you ahead of time to anyone who takes the time to look through this and respond. I really think I must be super close but just cant quite get the last bit alone. Thanks everyone!

**

UPDATE

**

First off thank you for everyone helping me format my question, much appreciated! I wanted to write a quick update to help answer this question for future viewers if I can.

After working on this more I now realize I was very close with the code above. One thing I had to do was remove the rewrite_rules_array hook. I assume because the rules were conflicting with the rules I have in the generate_rewrite_rules hook.

So that's great! However I still have one remaining issue i'm working on. For some reason when I go to site-name.com/kb/page/2/ it still goes to a post that is in the /kb/ CPT. Every other page seems to work great. site-name.com/kb/page/3/ and so on, all work correctly. I even deleted the post that /page/2/ is going to. It still goes to that same URL but now just shows 404.

I'll keep working on this and update when I figure it out. In the mean time if anyone has any tips to help with this i'd appreciate any help!

**

FINAL UPDATE

**

Turns out the last little issue of /page/2/ was a caching issue and all is good now. I will answer my question so we can close it out. Hopefully this helps anyone in the future with this same issue!

ShareEditFollowFlagedited Feb 3, 2020 at 18:32asked Feb 1, 2020 at 0:15RedBeard's user avatarRedBeard1122 bronze badgesAdd a comment

2 Answers

Sorted by: Highest score (default) Trending (recent votes count more) Date modified (newest first) Date created (oldest first) 0

Use this code below:

function column_custom_rewrite_basic( $wp_rewrite ) {
    unset($wp_rewrite->rules['column/([^/]+)/page/?([0-9]{1,})/?$']);
    $feed_rules = array(
        'column/page/?([0-9]{1,})/?$' => $wp_rewrite->index . '?post_type=column&paged=' . $wp_rewrite->preg_index( 1 ),
        'column/([^/]+)/page/?([0-9]{1,})/?$' => $wp_rewrite->index . '?tax_column_category=' . $wp_rewrite->preg_index( 1 ) . '&paged=' . $wp_rewrite->preg_index( 2 ),
        'column/(.+)/(.+)/?$' => $wp_rewrite->index . '?post_type=column&tax_column_category=' . $wp_rewrite->preg_index( 1 ) . '&name=' . $wp_rewrite->preg_index( 2 ),
        'column/(.+)?/?$' => $wp_rewrite->index . '?post_type=column&tax_column_category=' . $wp_rewrite->preg_index( 1 ),
    );

    $wp_rewrite->rules = $feed_rules + $wp_rewrite->rules;
    return $wp_rewrite->rules;
}

add_filter('generate_rewrite_rules', 'column_custom_rewrite_basic');

To anyone running into issues with this I hope my experiences help! Here are the steps I believe you need to follow to get it working correctly:

Custom Post Type Registration - Include rewrite structure like this (kb being whatever slug you want in URL for CPT)

'rewrite' => array(
                'slug' => 'kb/%taxonomy_name%',
                'with_front' => false
            ),

Custom Taxonomy Registration - Include rewrite structure (same as slug above)

'rewrite' => array(
                'slug' => 'kb',
                'with_front' => false
            ),

Include this hook to tell Wordpress how to handle %taxonomy_name% in your CPT registration (for me "knowledge_base" is my CPT name, and "kb_topics" is my taxonomy name. Replace these as necessary throughout)

function filter_post_type_link( $link, $post ) {
  if ( $post->post_type == 'knowledge_base' ) {
    if ( $cats = get_the_terms( $post->ID, 'kb_topics' ) ) {
      $link = str_replace( '%taxonomy_name%', current( $cats )->slug, $link );
    }
  }
  return $link;
}
add_filter( 'post_type_link', 'filter_post_type_link', 10, 2 );

Lastly you need to include some rewrites to fix the pagination

function fix_kb_category_pagination( $wp_rewrite ) {
    unset($wp_rewrite->rules['kb/([^/]+)/page/?([0-9]{1,})/?$']);
    $wp_rewrite->rules = array(
        'kb/?$' => $wp_rewrite->index . '?post_type=knowledge_base',
        'kb/page/?([0-9]{1,})/?$' => $wp_rewrite->index . '?post_type=knowledge_base&paged=' . $wp_rewrite->preg_index( 1 ),
        'kb/([^/]+)/page/?([0-9]{1,})/?$' => $wp_rewrite->index . '?kb_topics=' . $wp_rewrite->preg_index( 1 ) . '&paged=' . $wp_rewrite->preg_index( 2 ),
    ) + $wp_rewrite->rules;
}
add_action( 'generate_rewrite_rules', 'fix_kb_category_pagination' );

TIP: One last tip, in order to use a page template for site-name.com/kb/ you need to create a template in the back end with the filename of archive-knowledge_base.php, replacing knowledge_base with your CPT name.

That's it! I hope this helps others in the future maybe not run into the same issues I did. Good luck and happy coding!

Last updated