Skip to content

Instantly share code, notes, and snippets.

@tylerlwsmith
Last active November 10, 2019 16:40
Show Gist options
  • Save tylerlwsmith/f09479f71698fb86cec90697ec45e157 to your computer and use it in GitHub Desktop.
Save tylerlwsmith/f09479f71698fb86cec90697ec45e157 to your computer and use it in GitHub Desktop.
Custom Post Rewrites for Josh
<?php
/*
Plugin Name: Example Custom Post Types
Plugin URI: https://deadhandmedia.com/
Description: Adds custom post types and taxonomies.
Version: 1.0.0
Author: Tyler Smith
Author URI: https://deadhandmedia.com/
License: GPL2
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: example-text-domain
Domain Path: /languages
*/
require_once ( dirname( __FILE__ ) . '/cpt-label-helper.php' );
class CPT_Example {
private $text_domain = 'example-text-domain';
public function __construct() {
add_action( 'init', [ $this, 'register_post_types' ] );
add_action( 'init', [ $this, 'register_taxonomies' ] );
add_action( 'init', [ $this, 'add_rewrite_rules' ] );
add_filter( 'term_link', [ $this, 'update_term_link' ], 10, 3 );
add_filter( 'post_type_link', [ $this, 'update_post_type_link' ], 10, 2 );
add_filter( 'template_redirect', [ $this, 'redirect_on_wrong_url' ] );
register_activation_hook( __FILE__, [ $this, 'activate' ] );
register_deactivation_hook( __FILE__, 'flush_rewrite_rules' );
}
/**
* Add custom post types and taxomonies then flush permalinks.
*
* @link https://developer.wordpress.org/reference/functions/flush_rewrite_rules/#comment-597
*/
public function activate() {
// Register rewrite rules before init hook so WordPress has access to
// the post type and taxonomy slugs.
$this->register_post_types();
$this->register_taxonomies();
// Flush the rewrite rules so these types are included.
flush_rewrite_rules();
}
/**
* Register custom post types.
*
* @link https://codex.wordpress.org/Function_Reference/register_post_type
* @link https://developer.wordpress.org/resource/dashicons/
*/
public function register_post_types() {
// Register Careers
$args = [
'labels' => CPT_Label_Helper::post_type('Career', 'Careers', $this->text_domain),
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'careers' ),
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'menu_position' => null,
'menu_icon' => 'dashicons-universal-access-alt',
'supports' => array( 'title', 'editor', 'thumbnail')
];
register_post_type( 'career', $args );
}
/**
* Register custom taxonomies.
*
* @link https://codex.wordpress.org/Function_Reference/register_taxonomy
*/
public function register_taxonomies() {
// Career Type
$args = [
'hierarchical' => true,
'labels' => CPT_Label_Helper::hierarchical_taxonomy('Career Type', 'Career Types', $this->text_domain),
'show_ui' => true,
'show_admin_column' => true,
'update_count_callback' => '_update_post_term_count',
'query_var' => true,
'rewrite' => [
'slug' => 'career-type',
],
];
register_taxonomy( 'career-type', array( 'career' ), $args );
}
/**
* Adds rewrite rules for WordPress to match against and set the query
* variables.
*
* @link https://developer.wordpress.org/reference/functions/add_rewrite_rule/
*/
public function add_rewrite_rules() {
// For single career page. Example:
// https://jobs.com/careers/tech/wordpress-developer/
add_rewrite_rule(
'careers/([^/]+)/([^/]*)?/?$',
'index.php?post_type=career&career-type=$matches[1]&name=$matches[2]',
'top'
);
// For career type taxonomy page. Example:
// https://jobs.com/careers/tech/
add_rewrite_rule(
'careers/([^/]+)?/?$',
'index.php?post_type=career&career-type=$matches[1]',
'top'
);
}
/**
* Updates the post link. Even though we have the rewrite rules above,
* WordPress only uses that to create a query and delivery the posts
* matching that query. In order to make sure that system permalinks
* match the desired URL, we must explicitly set in in WordPress.
*
* There is some added complexity to ensure that WordPress still allows
* the user to edit the post slug, which is why there is specific logic
* for that below. I've included links for further reading on the reasoning
* behind this code.
*
* @link https://kellenmace.com/edit-slug-button-missing-in-wordpress/
* @link https://wordpress.stackexchange.com/questions/275687/how-to-retain-the-ability-to-modify-the-post-slug-after-applying-a-post-type-lin
* @link https://codex.wordpress.org/Plugin_API/Filter_Reference/post_type_link
*/
public function update_post_type_link( $url, $post ) {
if ( false !== strpos( $url, "%career%" ) ) {
$slug = '%career%';
} elseif ( $post->post_name ) {
$slug = $post->post_name;
} else {
$slug = sanitize_title( $post->post_title );
}
$term = get_the_terms( $post->ID, 'career-type' );
// Set fallback if no category is selected.
$term_slug = $term[0]->slug ?? 'other';
return user_trailingslashit( home_url( "careers/{$term_slug}/{$slug}" ) );
}
/**
* Updates the taxonomy link. Like posts, even though we have new rewrite
* rules added, WordPress must be explicitly told what we want the the
* term permalink to be.
*
* @link https://codex.wordpress.org/Plugin_API/Filter_Reference/post_type_link
*/
public function update_term_link( $url, $term, $taxonomy ) {
return user_trailingslashit( home_url( "careers/{$term->slug}" ) );
}
/**
* In the update_post_type_link() method, we tell WordPress what the post
* link should be. However, the WordPress query is only matching by a
* regex pattern. This means that any characters in the preappended
* taxonomy could match and pull up the page, because the query is
* matching by the slug. This could create endless duplicate pages
* with similar URLs, which has bad implications for SEO.
*
* To mitigate this, we will check if the currently url matches what we
* expect, and if it does not we'll redirect the user to the correct URL.
*
* @link https://codex.wordpress.org/Plugin_API/Action_Reference/template_redirect
*/
public function redirect_on_wrong_url() {
if ( 'career' != get_post_type() ) return;
if ( is_archive() ) return;
global $wp;
$permalink = user_trailingslashit( get_the_permalink() );
$current_page = user_trailingslashit( home_url( $wp->request ) );
if( $current_page != $permalink ){
wp_redirect( $permalink );
die();
}
}
}
new CPT_Example;
<?php
class CPT_Label_Helper {
public static function post_type ($singular, $plural, $textdomain = '') {
$labels = array(
'name' => __( $plural, $textdomain ),
'singular_name' => __( $singular, $textdomain ),
'menu_name' => __( $plural, $textdomain ),
'name_admin_bar' => __( $singular, $textdomain ),
'add_new' => __( 'Add New', $textdomain ),
'add_new_item' => __( 'Add New ' . $singular, $textdomain ),
'new_item' => __( 'New ' . $singular, $textdomain ),
'edit_item' => __( 'Edit ' . $singular, $textdomain ),
'view_item' => __( 'View ' . $singular, $textdomain ),
'all_items' => __( 'All ' . $plural, $textdomain ),
'search_items' => __( 'Search ' . $plural, $textdomain ),
'parent_item_colon' => __( 'Parent ' . $plural . ':', $textdomain ),
'not_found' => __( 'No ' . strtolower($plural) . ' found.', $textdomain ),
'not_found_in_trash' => __( 'No ' . strtolower($plural) . ' found in Trash.', $textdomain )
);
return $labels;
}
public static function hierarchical_taxonomy($singular, $plural, $textdomain = '') {
$labels = array(
'name' => __( $plural, $textdomain ),
'singular_name' => __( $singular, $textdomain ),
'search_items' => __( 'Search ' . $plural, $textdomain ),
'popular_items' => __( 'Popular ' . $plural, $textdomain ),
'all_items' => __( 'All ' . $plural, $textdomain ),
'parent_item' => null,
'parent_item_colon' => null,
'edit_item' => __( 'Edit ' . $singular, $textdomain ),
'update_item' => __( 'Update ' . $singular, $textdomain ),
'add_new_item' => __( 'Add New ' . $singular, $textdomain ),
'new_item_name' => __( 'New ' . $singular . ' Name', $textdomain ),
'menu_name' => __( $plural, $textdomain ),
);
return $labels;
}
public static function non_hierarchical_taxonomy($singular, $plural, $textdomain = '') {
$labels = array(
'name' => __( $plural, $textdomain ),
'singular_name' => __( $singular, $textdomain ),
'search_items' => __( 'Search ' . $plural, $textdomain ),
'popular_items' => __( 'Popular ' . $plural, $textdomain ),
'all_items' => __( 'All ' . $plural, $textdomain ),
'parent_item' => null,
'parent_item_colon' => null,
'edit_item' => __( 'Edit ' . $singular, $textdomain ),
'update_item' => __( 'Update ' . $singular, $textdomain ),
'add_new_item' => __( 'Add New ' . $singular, $textdomain ),
'new_item_name' => __( 'New ' . $singular . ' Name', $textdomain ),
'separate_items_with_commas' => __( 'Separate ' . strtolower($plural) . ' with commas', $textdomain ),
'add_or_remove_items' => __( 'Add or remove ' . strtolower($plural), $textdomain ),
'choose_from_most_used' => __( 'Choose from the most used ' . strtolower($plural), $textdomain ),
'not_found' => __( 'No ' . strtolower($plural) .' found.', $textdomain ),
'menu_name' => __( $plural, $textdomain ),
);
return $labels;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment