Hardcore URL Routing for WordPress - WordCamp Atlanta 2014

Mike Schinkel
Mike SchinkelManaging Partner; NewClarity and WordPress Architect at The Atlanta WordPress Coder's Guild
Hardcore URL
Routing for WordPress
Mike Schinkel
@mikeschinkel
about.me/mikeschinkel
1
WordCamp Atlanta - March 15th, 2014
To Cover
• URLs We Love
• How Do URLs Get Routed?
• How to see your Rewrite Rules
• Review the Rewrite API Functions & Hooks
• Ways to Add Rewrite Rules
• flush_rewrite_rules( ) & generate_rewrite_rules()
• Full-control Manual URL Routing
• Link Generation: Beyond Routing
• Other Stuff we Won't Cover
4
• /products/{product_name}/details/
• Post type => 'product'
• /products/{prod_cat}/{product_name}/
• Post type => 'product',*Taxonomy ('prod_cat')*term
• /{permalink_path}/json/
• A JSON version of your post
• /comments/{year}/{monthnum}/{day}/
• Archive of all comments not related to a specific post
• /api/{api_request}/
• “Virtual” URL for Custom code
URLs We Love
11
• /{automaker}/
• Post type => 'automaker'
• /{automaker}/{model}/
• Post type => 'automaker',*Post type => 'model'
• /{user_nicename}/
• Specified user
• Other?
• Taking requests....
More URLs We Love
12
• WP Matches URL Path to a Rewrite Pattern
•Rewrite Patterns are Regular Expressions
• Example URL Path:
• /2014/03/15/hello-world/
• Matching Regular Expression
• ([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/([^/]+)(/[0-9]+)?/?$
• Rewrite Pattern:
• index.php?year=$matches[1]&monthnum=$matches[2]&
day=$matches[3]&name=$matches[4]&page=$matches[5]
• Resultant Query String:
• index.php?year=2014&monthnum=03&day=15&name=hello-world&page=
How Do URLs Get Routed?
13
index.php in Rewrite Pattern?
#ABEGINAWordPress
<IfModuleAmod_rewrite.c>
RewriteEngineAOn
RewriteBaseA/
RewriteRuleA^index.php$A1A[L]
RewriteCondA%{REQUEST_FILENAME}A!1f
RewriteCondA%{REQUEST_FILENAME}A!1d
RewriteRuleA.A/index.phpA[L]
</IfModule>
#AENDAWordPress
See: .htaccess:*
* If on IIS then web.config which has a different format.
14
• Stored in $wp1>query_vars
• Processed by WP_Query() for The Loop
The Resultant Query String
//ANotAexactlyAthis,AbutAsimilar
$query_varsA=A'year=2014&monthnum=03&day=15&name=hello1world&page=';
$query=AnewAWP_Query(A$query_varsA);
$queryA=AnewAWP_Query(Aarray(
AA'year'AAAAA=>A2014,
AA'monthnum'A=>A3,
AA'day'AAAAAA=>A15,
AA'name'AAAAA=>A'hello1world',
AA'page'AAAAA=>A'',
));
• Essentially the same as this:
15
See your Rewrite Rules
<?php
/*
A*AFilename:A/rewrite1rules.php
A*AREMEMBERATOASETAYOURAPERMALINKS!ASettingsA>APermalinks
A*/
includeA__DIR__A.A'/wp1load.php';
$rewrite_rulesA=Aget_option(A'rewrite_rules'A);
header(A'Content1type:Atext/plain;'A);
print_r(A$rewrite_rulesA);
16
Rewrite Rules, in a Table!
<?php
/*
A*AFilename:A/rewrite1rules2.phpA
A*AREMEMBERATOASETAYOURAPERMALINKS!ASettingsA>APermalinks
A*/
includeA__DIR__A.A'/wp1load.php';
$rewrite_rulesA=Aget_option(A'rewrite_rules'A);
?>
<html>
<head>
AA<title>RewriteARules</title>
AA<styleAtype="text/css">AthA{Atext1align:Aleft;A}A</style>
</head>
<body>
AA<table>
AAAA<?php
AAAAforeach(A$rewrite_rulesAasA$regexA=>A$patternA)A{
AAAAAAechoA"<tr><th>{$regex}</th><td>{$pattern}</td></tr>";
AAAA}A?>
AA</table>
</body>
</html>
17
Best Way to See Your Rulez
• Monkeyman Rewrite Analyzer
• http://wordpress.org/plugins/monkeyman-rewrite-analyzer/
18
The WordPress Rewrite API
•Rewrite API Functions
• Related Functions
•Rewrite API Hooks
• Related Hooks
19
The Rewrite API Functions
• register_post_type()* andAregister_taxonomy()*
• flush_rewrite_rules()
• add_rewrite_rule() and add_rewrite_tag()
• get_query_var()*
• register_activation_hook() andA
register_deactivation_hook()A
20
*Related functions
The Rewrite API Hooks
• 'parse_request'
• 'request'
• 'after_switch_theme'*
• 'template_redirect'*
• 'template_include'*
• 'redirect_canonical'*
• 'do_parse_request' A
• 'query_var'
21
*Related Hooks
Different Ways to Add Rules
• Adding Rewrite Rules Indirectly
• Adding Rewrite Tags & Rules
• Validating Rewritten URLs
• Adding EndPoints
• Adding Permastructs
• Adding External Rules
22
Adding Rules Indirectly
/products/{product_name}/
<?phpA//Afunctions.php
add_action(A'init',A'x_init'A);
functionAx_init()A{
AAregister_post_type(A'x_product',Aarray(
AAAA'public'A=>Atrue,
AAAA'label'A=>A__(A'Products',A'X'A),
AAAA'rewrite'A=>Aarray(
AAAAAA'with_front'A=>Afalse,
AAAAAA'slug'A=>A'products',
AAAA),
AA));
}
23
Always Remember to Flush!
flush_rewrite_rules(A$hard_or_softA)
add_action(A'init',A'x_init'A);
functionAx_init()A{
AA//ACallAregister_post_type()Ahere
AAifA(AWP_DEBUGA)A{
AAAifA(A!Afunction_exists(A'save_mod_rewrite_rules'A)A)A{
AAAAArequire_once(AABSPATHA.A'wp1admin/includes/file.php'A);
AAAAArequire_once(AABSPATHA.A'wp1admin/includes/misc.php'A);
AAA}
AAAflush_rewrite_rules(AtrueA);
AA}
}
• true (hard) - Writes changes to .htaccess/web.config
• false (soft) - Does not writes changes.
26
Flushing in a Plugin
<?php
/**
#*#Plugin#Name:#Hardcore#URLs
#*/
class#Hardcore_Urls#{
##static#function#on_load()#{
####add_action(#'init',#array(#__CLASS__,#'_init'#)#);
####register_activation_hook(#__FILE__,#array(#__CLASS__,#'_activate'#)#);
####register_deactivation_hook(#__FILE__,#array(#__CLASS__,#'_deactivate'#)#);
##}
##static#function#_init()#{
####//#Register#post#types,#taxonomies#here#
####//#Also#add#rewrite#rules,#tags,#permastructs,#etc.#here.
##}
##static#function#_activate()#{
####flush_rewrite_rules(#true#);
##}
##static#function#_deactivate()#{
####flush_rewrite_rules(#true#);
##}
}
Hardcore_Urls::on_load();
27
Flushing in a Theme
<?phpA//Afunctions.php
classAHardcore_Urls_ThemeA{
AAstaticAfunctionAon_load()A{
AAAAadd_action(A'init',Aarray(A__CLASS__,A'_init'A)A);
AAAAadd_action(A'after_switch_theme',A'_after_switch_theme'A);
AA}
AAstaticAfunctionA_init()A{
AAAA//ARegisterApostAtypes,AtaxonomiesAhereA
AAAA//AAlsoAaddArewriteArules,Atags,Apermastructs,Aetc.Ahere.
AA}
AAstaticAfunctionA_after_switch_theme()A{
AAAAflush_rewrite_rules(AtrueA);
AA}
}
Hardcore_Urls_Theme::on_load();
28
Adding Tags and Rewrite Rules
/products/{product_name}/details/
register_post_type(A'x_product',Aarray(
AA'public'A=>Atrue,
AA'label'A=>A__(A'Products',A'X'A),
AA'query_var'A=>A'x_product',
AA'rewrite'A=>Aarray(
AAAA'with_front'A=>Afalse,
AAAA'slug'A=>A'products',
AA),
));
add_rewrite_tag(A'%x_product_details%',A'details'A);
add_rewrite_rule(
AA'products/([^/]+)/details/?$',
AA'index.php?x_product=$matches[1]&'A.
AAAAAAAAAAAA'x_product_details=true',
AA'top'
);
29
Using in a Theme
<?phpAget_header();A?>
AA<divAid="primary"Aclass="content1area">
AAAA<divAid="content"Aclass="site1content"Arole="main">
AAAAAA<?php
AAAAAAAAifA(Aget_query_var(A'x_product_details'A)A)A{
AAAAAAAAAAechoA'<h2>THISAISAAAPRODUCTADETAILSAPAGE</h2>';
AAAAAAAA}
AAAAAA?>
AAAAAA<?php
AAAAAAAAwhileA(Ahave_posts()A)A:Athe_post();
AAAAAAAAAAget_template_part(A'content',Aget_post_format()A);
AAAAAAAAAAifA(Acomments_open()A||Aget_comments_number()A)A{
AAAAAAAAAAAAcomments_template();
AAAAAAAAAA}
AAAAAAAAendwhile;
AAAAAA?>
AAAA</div>
AA</div>
<?phpAget_footer();
Example:Asingle1acw14_product.php
30
Add Taxonomy Term to Post Type Slug
/products/{prod_cat}/{product_name}/
register_post_type(A'x_product',Aarray(
AA'public'A=>Atrue,
AA'label'A=>A__(A'Products',A'X'A),
AA'rewrite'A=>Aarray(
AAAA'with_front'A=>Afalse,
AAAA'slug'A=>A'products/[^/]+',
AA),
));
/products/valid1cat/{product_name}/
/products/foo1bar/{product_name}/
Except! No {prod_cat} Validation:
31
Validating {prod_cat}
add_action(#'init',#'x_init'#);
function#x_init()#{
##//#First#register#post#type#and#taxonomy#here
##add_rewrite_tag(#'%x_prod_cat%',#'([^/]+)'#);
##add_rewrite_rule(
####'products/([^/]+)/([^/]+)/?$',
####'index.php?x_prod_cat=$matches[1]&x_product=$matches[2]',
####'top'
##);
}
add_filter(#'parse_request',#'x_parse_request'#);
function#x_parse_request(#$wp#)#{
##if#(#!#empty(#$wp]>query_vars['x_prod_cat']#)#)#{
####$term_slug#=#$wp]>query_vars['x_prod_cat'];
####$query#=#new#WP_Query(#$wp]>query_vars#);
####if#(#!#has_term(#$term_slug,#'x_prod_cat',#$query]>post#)#)#{
######$wp]>query_vars#=#array(#
########'error'#####=>#404,#
########'post_type'#=>#true#
######);
######status_header(#404#);#
######nocache_headers();
####}
##}
}
/products/{prod_cat}/{product_name}/
32
More Rewrite Functions
• add_rewrite_endpoint()
• add_permastruct()
• WP_Rewrite ClassA
• $wp_rewrite1>add_external_rule()A
• generate_rewrite_rules() *
• save_mod_rewrite_rules() andA
iis7_save_url_rewrite_rules()
• get_sample_permalink() *
• redirect_guess_404_permalink()!!!
33
*Called internally byWordPress
!!! Bad Actor!
Adding an EndPoint
add_filter(A'request',A'x_request'A);
functionAx_request(A$query_varsA)A{
AAif(Aisset(A$query_vars['json']A)A)A{
AAAA$query_vars['json']A=Atrue;
AA}
AAreturnA$query_vars;
}
add_filter(A'template_redirect',A'x_template_redirect'A);
functionAx_template_redirect()A{
AAif(Aget_query_var(A'json'A)A)A{
AAAAglobalA$post;
AAAAheader(A'Content1type:Aapplication/json'A);
AAAAechoAjson_encode(A$postA);
AAAAexit;
AA}
}
/products/{product_name}/json/
34
Adding a Permastruct
add_action(A'init',A'x_init'A);
functionAx_init()A{
AA//ARegisterAotherAthingsAhere
AAadd_permastruct(A
AAAA'x_comments',A
AAAA'comments/%year%/%monthnum%/%day%',A
AAAAarray(
AAAAAA'with_front'AA=>Atrue,
AAAAAA'ep_mask'AAAAA=>AEP_NONE,
AAAAAA'paged'AAAAAAA=>Atrue,
AAAAAA'feed'AAAAAAAA=>Atrue,
AAAAAA'forcomments'A=>Afalse,
AAAAAA'walk_dirs'AAA=>Atrue,
AAAAAA'endpoints'AAA=>Atrue,
AAAA)
AA);
AA//AFlushArulesAifAWP_DEBUG
}
/comments/{year}/{monthnum}/{day}/
35
Recognizing a Permastruct
add_filter(A'parse_request',A'x_parse_request'A);
functionAx_parse_request(A$wpA)A{
AA$regexA=A'#^comments/?([019]{4}/?([019]{1,2}/?([019]{1,2}/?(.*))?)?)?$#';
AAifA(Apreg_match(A$regex,A$wp1>request,A$matchA)A)A{
AAAA$wp1>query_varsA+=Aarray(
AAAAAA'post_type'A=>Atrue,
AAAAAA'x_comments_permastruct'A=>Atrue
AAAA);
AA}
}
/comments/{year}/{monthnum}/{day}/
36
*WhyYes! That is a rather ugly regular expression!
Preparing a Special Template
add_filter(A'template_include',A'x_template_include'A);
functionAx_template_include(A$template_fileA)A{
AAglobalA$wp,A$wp_the_query;
AAif(Aget_query_var(A'x_comments_permastruct'A)A)A{
AAAA$wp_the_query1>queried_object_idA=Anull;
AAAA$wp_the_query1>queried_objectA=A$queryA=AnewAWP_Comment_Query;
AAAA$date_queryA=Aarray();
AAAAforeach(A$wp1>query_varsAasA$var_nameA=>A$var_valueA)A{
AAAAAAifA(Apreg_match(A'#^(year|monthnum|day)$#',A$var_nameA)A)A{
AAAAAAAA$date_query[]A=Aarray(A$var_nameA=>A$var_valueA);
AAAAAA}
AAAA}
AAAA$query1>commentsA=A$query1>query(Aarray(
AAAAAA'date_query'A=>A$date_query,
AAAA));
AAAAx_load_comments(Aget_stylesheet_directory()A.A'/comment.php'A);
AA}
AAreturnA$template_file;
}
/comments/{year}/{monthnum}/{day}/
37
Serving a Special Template
functionAx_load_comments(A$_template_fileA)A{
AAglobalA$post,A$wp_rewrite,A$wpdb,A$wp_version,A$wp,A$user_ID;
AAglobalA$comments,A$comment,A$wp_comment_query;
AA$wp_comment_queryA=Aget_queried_object();
AA$commentsA=A$wp_comment_query1>comments;
AA$commentA=Areset(A$commentsA);
AA$postA=Aget_post(A$comment1>comment_post_IDA);
AArequire(A$_template_fileA);
AAexit;
}
/comments/{year}/{monthnum}/{day}/
38
A Special Comment Template
<?php#//#comment.php#
get_header();#?>
<div#id="main]content"#class="main]content">
##<div#id="primary"#class="content]area">
####<div#id="content"#class="site]content"#role="main">
####<?php#if#(#count(#$comments#)#)#:
########echo#'<ul>';
########foreach(#$comments#as#$comment#):
##########$post#=#get_post(#$comment]>comment_post_ID#);
##########$url#=#get_comment_link(#$comment#);
##########$excerpt#=#get_comment_excerpt(#$comment#);
##########$date_time#=#get_comment_date()#.#'#'.#get_comment_time();
##########echo#"<li>About:#";#the_title();
##########echo#"<br>{$comment]>comment_author}#&lt;{$date_time}&gt;";
##########echo#"<br><a#href="{$url}">{$excerpt}</a><hr>";
########endforeach;
########echo#'</ul>';
######else:
########get_template_part(#'content',#'none'#);
######endif;#?>
####</div><!]]##content#]]>
##</div><!]]##primary#]]>
##<?php#get_sidebar(#'content'#);#?>
</div><!]]##main]content#]]><?php#
get_sidebar();
get_footer();
/comments/{year}/{monthnum}/{day}/
39
Rules Generated by Permastruct
/comments/{year}/{monthnum}/{day}/
40
comments/([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]&withcomments=1
comments/([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/(feed|rdf|rss|rss2|atom)/?$
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]&withcomments=1
comments/([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/page/?([0]9]{1,})/?$
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]
comments/([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/comment]page]([0]9]{1,})/?$
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&cpage=$matches[4]
comments/([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/json(/(.*))?/?$
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&json=$matches[5]
comments/([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/?$
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]
comments/([0]9]{4})/([0]9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$
=>#index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]&withcomments=1
comments/([0]9]{4})/([0]9]{1,2})/(feed|rdf|rss|rss2|atom)/?$
=>#index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]&withcomments=1
comments/([0]9]{4})/([0]9]{1,2})/page/?([0]9]{1,})/?$
=>#index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]
comments/([0]9]{4})/([0]9]{1,2})/comment]page]([0]9]{1,})/?$
=>#index.php?year=$matches[1]&monthnum=$matches[2]&cpage=$matches[3]
comments/([0]9]{4})/([0]9]{1,2})/json(/(.*))?/?$
=>#index.php?year=$matches[1]&monthnum=$matches[2]&json=$matches[4]
comments/([0]9]{4})/([0]9]{1,2})/?$
=>#index.php?year=$matches[1]&monthnum=$matches[2]
comments/([0]9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$
=>#index.php?year=$matches[1]&feed=$matches[2]&withcomments=1
comments/([0]9]{4})/(feed|rdf|rss|rss2|atom)/?$
=>#index.php?year=$matches[1]&feed=$matches[2]&withcomments=1
comments/([0]9]{4})/page/?([0]9]{1,})/?$
=>#index.php?year=$matches[1]&paged=$matches[2]
comments/([0]9]{4})/comment]page]([0]9]{1,})/?$
=>#index.php?year=$matches[1]&cpage=$matches[2]
comments/([0]9]{4})/json(/(.*))?/?$
=>#index.php?year=$matches[1]&json=$matches[3]
comments/([0]9]{4})/?$
=>#index.php?year=$matches[1]
This was for all*true*&
A'ep_mask'A=>AEP_ALL
Adding an External Rule
/api/{whatevah}/
41
RewriteRuleA^api/?.*$A/wp1content/themes/wp38/api.phpA[QSA,L]
Adds the following to .htaccess:
add_action(A'init',A'x_init'A);
functionAx_init()A{
AAglobalA$wp_rewrite;
AA$local_pathA=Asubstr(A__DIR__A.A'/api.php',Astrlen(AABSPATHA)A);
AA$wp_rewrite1>add_external_rule(A'api/?.*$',A$local_pathA);
}
Recognizing the External Rule
/api/{whatevah}/
42
<?php
/*AFilename:Aapi.php
A*AThisAisAonlyAifAyouAneedAWordPressAloaded.
A*ATheA"/../../.."AassumesAinAaApluginAdirectory.
A*/
require(A__DIR__A.A'/../../../wp1load.php'A);
$url_pathA=A$_SERVER['REQUEST_URI'];
$requestA=Apreg_replace(A'#^/api/(.*)$#',A'$1',A
$url_pathA);
echoA"YourAAPIArequestAwas:A{$request}";
The generate_rewrite_rules() Function
43
• This is where it gets really ugly.
• This can turn your brain to mush.
• You might even switch to Drupal!
• Not really. Who'd want Drupal
if they can haz WordPress?!?
• But I digress...
44
Hooks Used During Rule Generation
45
• 'delete_option_rewrite_rules'
• 'pre_get_option_rewrite_rules'
• 'default_option_rewrite_rules'
• 'post_rewrite_rules'
• 'date_rewrite_rules'
• 'root_rewrite_rules'
• 'comments_rewrite_rules'
• 'search_rewrite_rules'
• 'author_rewrite_rules'
• 'page_rewrite_rules'
• "{$permastructname}_rewrite_rules"
• 'tag_rewrite_rules'
• 'generate_rewrite_rules'
• 'rewrite_rules_array'
For 'post_rewrite_rules'
46
##[[0]9]{4}/[0]9]{1,2}/[0]9]{1,2}/[^/]+/attachment/([^/]+)/?$]#=>#index.php?attachment=$matches[1]
##[[0]9]{4}/[0]9]{1,2}/[0]9]{1,2}/[^/]+/attachment/([^/]+)/trackback/?$]#=>#index.php?attachment=$matches[1]&tb=1
##[[0]9]{4}/[0]9]{1,2}/[0]9]{1,2}/[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?attachment=$matches[1]&feed=$matches[2]
##[[0]9]{4}/[0]9]{1,2}/[0]9]{1,2}/[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?attachment=$matches[1]&feed=$matches[2]
##[[0]9]{4}/[0]9]{1,2}/[0]9]{1,2}/[^/]+/attachment/([^/]+)/comment]page]([0]9]{1,})/?$]#=>#index.php?attachment=$matches[1]&cpage=$matches[2]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/([^/]+)/trackback/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&name=$matches[4]&tb=1
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&name=$matches[4]&feed=$matches[5]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/([^/]+)/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&name=$matches[4]&feed=$matches[5]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/([^/]+)/page/?([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&name=$matches[4]&paged=$matches[5]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/([^/]+)/comment]page]([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&name=$matches[4]&cpage=$matches[5]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/([^/]+)/json(/(.*))?/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&name=$matches[4]&json=$matches[6]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/([^/]+)(/[0]9]+)?/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&name=$matches[4]&page=$matches[5]
##[[0]9]{4}/[0]9]{1,2}/[0]9]{1,2}/[^/]+/([^/]+)/?$]#=>#index.php?attachment=$matches[1]
##[[0]9]{4}/[0]9]{1,2}/[0]9]{1,2}/[^/]+/([^/]+)/trackback/?$]#=>#index.php?attachment=$matches[1]&tb=1
##[[0]9]{4}/[0]9]{1,2}/[0]9]{1,2}/[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?attachment=$matches[1]&feed=$matches[2]
##[[0]9]{4}/[0]9]{1,2}/[0]9]{1,2}/[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?attachment=$matches[1]&feed=$matches[2]
##[[0]9]{4}/[0]9]{1,2}/[0]9]{1,2}/[^/]+/([^/]+)/comment]page]([0]9]{1,})/?$]#=>#index.php?attachment=$matches[1]&cpage=$matches[2]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/page/?([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/comment]page]([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&cpage=$matches[4]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/json(/(.*))?/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&json=$matches[5]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]
##[([0]9]{4})/([0]9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]
##[([0]9]{4})/([0]9]{1,2})/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]
##[([0]9]{4})/([0]9]{1,2})/page/?([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]
##[([0]9]{4})/([0]9]{1,2})/comment]page]([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&cpage=$matches[3]
##[([0]9]{4})/([0]9]{1,2})/json(/(.*))?/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&json=$matches[4]
##[([0]9]{4})/([0]9]{1,2})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]
##[([0]9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?year=$matches[1]&feed=$matches[2]
##[([0]9]{4})/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?year=$matches[1]&feed=$matches[2]
##[([0]9]{4})/page/?([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&paged=$matches[2]
##[([0]9]{4})/comment]page]([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&cpage=$matches[2]
##[([0]9]{4})/json(/(.*))?/?$]#=>#index.php?year=$matches[1]&json=$matches[3]
##[([0]9]{4})/?$]#=>#index.php?year=$matches[1]
For 'date_rewrite_rules'
47
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/(feed|rdf|rss|rss2|atom)/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/page/?([0]9]{1,})/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]
##[([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]
##[([0]9]{4})/([0]9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]
##[([0]9]{4})/([0]9]{1,2})/(feed|rdf|rss|rss2|atom)/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]
##[([0]9]{4})/([0]9]{1,2})/page/?([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]
##[([0]9]{4})/([0]9]{1,2})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]
##[([0]9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?year=$matches[1]&feed=$matches[2]
##[([0]9]{4})/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?year=$matches[1]&feed=$matches[2]
##[([0]9]{4})/page/?([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&paged=$matches[2]
##[([0]9]{4})/?$]#=>#index.php?year=$matches[1]
For 'root_rewrite_rules'
48
AA[feed/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?&feed=$matches[1]
AA[(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?&feed=$matches[1]
AA[page/?([019]{1,})/?$]A=>Aindex.php?&paged=$matches[1]
For 'comments_rewrite_rules'
49
AA[comments/feed/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?&feed=$matches[1]&withcomments=1
AA[comments/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?&feed=$matches[1]&withcomments=1
For 'search_rewrite_rules'
50
AA[search/(.+)/feed/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?s=$matches[1]&feed=$matches[2]
AA[search/(.+)/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?s=$matches[1]&feed=$matches[2]
AA[search/(.+)/page/?([019]{1,})/?$]A=>Aindex.php?s=$matches[1]&paged=$matches[2]
AA[search/(.+)/?$]A=>Aindex.php?s=$matches[1]
For 'author_rewrite_rules'
51
AA[author/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?author_name=$matches[1]&feed=$matches[2]
AA[author/([^/]+)/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?author_name=$matches[1]&feed=$matches[2]
AA[author/([^/]+)/page/?([019]{1,})/?$]A=>Aindex.php?author_name=$matches[1]&paged=$matches[2]
AA[author/([^/]+)/?$]A=>Aindex.php?author_name=$matches[1]
For 'page_rewrite_rules'
52
AA[.?.+?/attachment/([^/]+)/?$]A=>Aindex.php?attachment=$matches[1]
AA[.?.+?/attachment/([^/]+)/trackback/?$]A=>Aindex.php?attachment=$matches[1]&tb=1
AA[.?.+?/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$]A
=>Aindex.php?attachment=$matches[1]&feed=$matches[2]
AA[.?.+?/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$]A
=>Aindex.php?attachment=$matches[1]&feed=$matches[2]
AA[.?.+?/attachment/([^/]+)/comment1page1([019]{1,})/?$]A
=>Aindex.php?attachment=$matches[1]&cpage=$matches[2]
AA[(.?.+?)/trackback/?$]A=>Aindex.php?pagename=$matches[1]&tb=1
AA[(.?.+?)/feed/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?pagename=$matches[1]&feed=$matches[2]
AA[(.?.+?)/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?pagename=$matches[1]&feed=$matches[2]
AA[(.?.+?)/page/?([019]{1,})/?$]A=>Aindex.php?pagename=$matches[1]&paged=$matches[2]
AA[(.?.+?)/comment1page1([019]{1,})/?$]A=>Aindex.php?pagename=$matches[1]&cpage=$matches[2]
AA[(.?.+?)(/[019]+)?/?$]A=>Aindex.php?pagename=$matches[1]&page=$matches[2]
For 'category_rewrite_rules'
53
##[category/(.+?)/feed/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?category_name=$matches[1]&feed=$matches[2]
##[category/(.+?)/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?category_name=$matches[1]&feed=$matches[2]
##[category/(.+?)/page/?([0]9]{1,})/?$]#=>#index.php?category_name=$matches[1]&paged=$matches[2]
##[category/(.+?)/?$]#=>#index.php?category_name=$matches[1]
For 'post_tag_rewrite_rules'
54
AA[tag/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?tag=$matches[1]&feed=$matches[2]
AA[tag/([^/]+)/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?tag=$matches[1]&feed=$matches[2]
AA[tag/([^/]+)/page/?([019]{1,})/?$]A=>Aindex.php?tag=$matches[1]&paged=$matches[2]
AA[tag/([^/]+)/?$]A=>Aindex.php?tag=$matches[1]
For 'tag_rewrite_rules'
55
AA[tag/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?tag=$matches[1]&feed=$matches[2]
AA[tag/([^/]+)/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?tag=$matches[1]&feed=$matches[2]
AA[tag/([^/]+)/page/?([019]{1,})/?$]A=>Aindex.php?tag=$matches[1]&paged=$matches[2]
AA[tag/([^/]+)/?$]A=>Aindex.php?tag=$matches[1]
For 'post_format_rewrite_rules'
56
AA[type/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?post_format=$matches[1]&feed=$matches[2]
AA[type/([^/]+)/(feed|rdf|rss|rss2|atom)/?$]A=>Aindex.php?post_format=$matches[1]&feed=$matches[2]
AA[type/([^/]+)/page/?([019]{1,})/?$]A=>Aindex.php?post_format=$matches[1]&paged=$matches[2]
AA[type/([^/]+)/?$]A=>Aindex.php?post_format=$matches[1]
For 'x_comments_rewrite_rules'
57
##[comments/([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]&withcomments=1
##[comments/([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/(feed|rdf|rss|rss2|atom)/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&feed=$matches[4]&withcomments=1
##[comments/([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/page/?([0]9]{1,})/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&paged=$matches[4]
##[comments/([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/comment]page]([0]9]{1,})/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&cpage=$matches[4]
##[comments/([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/json(/(.*))?/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&json=$matches[5]
##[comments/([0]9]{4})/([0]9]{1,2})/([0]9]{1,2})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&day=$matches[3]
##[comments/([0]9]{4})/([0]9]{1,2})/feed/(feed|rdf|rss|rss2|atom)/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]&withcomments=1
##[comments/([0]9]{4})/([0]9]{1,2})/(feed|rdf|rss|rss2|atom)/?$]#
=>#index.php?year=$matches[1]&monthnum=$matches[2]&feed=$matches[3]&withcomments=1
##[comments/([0]9]{4})/([0]9]{1,2})/page/?([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]
##[comments/([0]9]{4})/([0]9]{1,2})/comment]page]([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&cpage=$matches[3]
##[comments/([0]9]{4})/([0]9]{1,2})/json(/(.*))?/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]&json=$matches[4]
##[comments/([0]9]{4})/([0]9]{1,2})/?$]#=>#index.php?year=$matches[1]&monthnum=$matches[2]
##[comments/([0]9]{4})/feed/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?year=$matches[1]&feed=$matches[2]&withcomments=1
##[comments/([0]9]{4})/(feed|rdf|rss|rss2|atom)/?$]#=>#index.php?year=$matches[1]&feed=$matches[2]&withcomments=1
##[comments/([0]9]{4})/page/?([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&paged=$matches[2]
##[comments/([0]9]{4})/comment]page]([0]9]{1,})/?$]#=>#index.php?year=$matches[1]&cpage=$matches[2]
##[comments/([0]9]{4})/json(/(.*))?/?$]#=>#index.php?year=$matches[1]&json=$matches[3]
##[comments/([0]9]{4})/?$]#=>#index.php?year=$matches[1]
Whew!
58
• I hope you never have to use any of that!
• But if you do, you are now equipped.
• (Assumed your brain didn't turn to mush.)
The*'do_parse_request'AHook
59
• This is the strong magic.
• Using 'do_parse_request' is like....
• ...getting the Glengarry Leads!!!
• Seriously! It's omni-powerful!
• But Theme/Plugin Devs beware...
• The magic might overpower you.
Context Specific URLs
60
• WordPress only supports Context Free URLs
• But we often want Context Sensitive URLs
• 'do_parse_request'Amakes it possible
• But, it's not without risk
• Plugins and Themes won't expect it
• And you can have one URL hide another
• With Great Power Comes Great Responsibility
Post Type Name in Root
/{product_name}/
61
add_filter(A'do_parse_request',A'x_do_parse_request',A10,A3A);
functionAx_do_parse_request(A$continue,A$wp,A$extra_query_varsA)A{
AAifA(A$continueA)A{
AAAA$url_pathA=Atrim(A$_SERVER['REQUEST_URI'],A'/'A);
AAAA$url_pathA=Asanitize_title_with_dashes(A$url_pathA);
AAAA$queryA=AnewAWP_Query(Aarray(
AAAAAA'name'AAAAAAAA=>A$url_path,
AAAAAA'post_type'AAA=>A'x_product',
AAAAAA'post_status'A=>A'publish',
AAAA));
AAAAifA(A1A==A$query1>found_postsA)A{
AAAAAA$wp1>query_varsA=Aarray(
AAAAAAAA'post_type'A=>A'x_product',
AAAAAAAA'x_product'A=>A$url_path,
AAAAAAAA'name'AAAAAA=>A$url_path,
AAAAAAAA'page'AAAAAA=>A'',
AAAAAA);
AAAAAA$continueA=Afalse;AA//AIMPORTANT
AAAA}
AA}
AAreturnA$continue;
}
Parent and Child Post Types
Part 1 of 3
/{product_name}/{model_name}/
62
add_filter(A'do_parse_request',A'x_do_parse_request',A10,A3A);
functionAx_do_parse_request(A$continue,A$wp,A$extra_query_varsA)A{
AAifA(A$continueA&&A!Ais_admin()A)A{
AAAA$url_pathA=Atrim(A$_SERVER['REQUEST_URI'],A'/'A);
AAAAifA(A!Aempty(A$url_pathA)A&&A'?'A!=A$url_path[0]A)A{
AAAAAA$url_path_partsA=Aarray_map(A'sanitize_title_with_dashes',Aexplode(A'/',A
$url_pathA)A);
AAAAAAswitch(Acount(A$url_path_partsA)A)A{
AAAAAAAAcaseA2:
AAAAAAAAAAifA(A$modelA=Ax_match_post_name(A'x_model',A$model_nameA=A$url_path_parts[1]A)A)A{
AAAAAAAAAAAAifA(A$productA=Ax_match_post_name(A'x_product',A$url_path_parts[0]A)A)A{
AAAAAAAAAAAAAAifA(A$model1>post_parentA==A$product1>IDA)A{
AAAAAAAAAAAAAAAAx_route_post_name(A$wp,A'x_model',A$model_nameA);
AAAAAAAAAAAAAAAA$continueA=Afalse;
AAAAAAAAAAAAAA}
AAAAAAAAAAAA}
AAAAAAAAAA}
AAAAAAAAAAbreak;
AAAAAAAAcaseA1:
AAAAAAAAAA$continueA=A!Ax_match_post_name(A'x_product',A$url_path_parts[0],A$wpA);
AAAAAAAAAAbreak;
AAAAAA}
AAAA}
AA}
AAreturnA$continue;
}
Parent and Child Post Types
Part 2 of 3
/{product_name}/{model_name}/
63
functionAx_route_post_name(A$post_type,A$post_name,A$wpA)A{
AA$wp1>query_varsA=Aarray(
AAAA'post_type'A=>A$post_type,
AAAA$post_typeAA=>A$post_name,
AAAA'name'AAAAAA=>A$post_name,
AAAA'page'AAAAAA=>A'',
AA);
}
functionAx_match_post_name(A$post_type,A$post_name,A$wpA=AfalseA)A{
AA$queryA=AnewAWP_Query(Aarray(
AAAA'name'AAAAAAAA=>A$post_name,
AAAA'post_type'AAA=>A$post_type,
AAAA'post_status'A=>A'publish',
AA));
AAifA(A(A$matchedA=A1A==A$query1>found_postsA)A&&A$wpA)A{
AAAAx_route_post_name(A$wp,A$post_type,A$post_nameA);
AA}
AAreturnA$matchedA?A$query1>postA:Afalse;
}
Parent and Child Post Types
Part 3 of 3
/{product_name}/{model_name}/
64
add_action(#'add_meta_boxes',#'x_add_meta_boxes'#);
function#x_add_meta_boxes(#$post_type#)#{
##if#(#'x_model'#==#$post_type#)#{
####add_meta_box(#'product_parent_box',#//#$id
######__(#'Parent#Product',#'X'#),######//#$title
######'x_model_parent_metabox',#########//#$callback
######'x_model',## #####################//#$post_type
######'side',###########################//#$context
######'low'## # #####################//#$priority
####);
##}
}
function#x_model_parent_metabox(#$post#)#{
##$post_type_object#=#get_post_type_object(#'x_product'#);
##$post_type_object]>hierarchical#=#true;
##wp_dropdown_pages(#array(
####'name'#=>#'parent_id',
####'post_type'#=>#'x_product',
####'selected'#=>#$post]>post_parent,
####'show_option_none'#=>#__(#'Select#a#Parent',#'X'#),
####'option_none_value'#=>#0,
##));
##$post_type_object]>hierarchical#=#false;
}
Stopping the "Helpful" Guesses
/{product_name}/{model_name}/A
=>
?post_type=x_model&p={post_id}
65
add_action(A'redirect_canonical',A'x_redirect_canonical',A10,A2A);
functionAx_redirect_canonical(A$redirect_url,A$requested_urlA)A{
AAlist(A$url,A$queryA)A=Aexplode(A'?',A"{$redirect_url}?"A);
AAifA(A$urlA==Ahome_url('/')A)A{
AAAAparse_str(A$query,A$paramsA);
AAAAifA(Aisset(A$params['post_type']A)A&&A'x_model'A==A$params['post_type']A)A{
AAAAAA//ADammit;Aredirect_guess_404_permalink()Ainterfered!ASetAitAback!!!
AAAAAA$redirect_urlA=A$requested_url;
AAAA}
AA}
AAreturnA$redirect_url;
}
Reveal the Query Vars
66
add_action(A'shutdown',A'x_shutdown'A);
functionAx_shutdown()A{
AAifA(A!Ais_admin()A)A{
AAAAglobalA$wp;
AAAAechoA'<pre><code><fontAsize="7">';
AAAAechoA'$wp1>query_vars:A';
AAAAprint_r(A$wp1>query_varsA);
AAAAechoA'</font></code></pre>';
AA}
}
Performance Optimization
67
• Cache Top N URLs in wp_options
•
In array; key=URL, value=$wp1>query_vars
• Periodically Recalculate Top N
• Even Better if using Memcache
• This is your Homework!
Warning:
May Be Incompatible
68
• Highly Custom Sites:
• A-OK
• Normal Websites or Blog:
• Plugins and themes might be incompatible
• Plugins or Themes for Distribution:
• Be very careful; this is non-standard routing
• Also...
• Very easy to create ambiguity in routing
The Dispatch Library
69
• Using URI Templates as Patterns
• Inspecting URLs by Segment
• Plan to do lots of caching
• Still PRE-ALPHA
• github.com/newclarity/dispatch
But Routing Ain't Enough
70
• Link Generation
• Allow Editing the Slug
• Changing the Permalink Sample
Generate the Link
71
add_filter(A'post_type_link',A'x_post_type_link',A10,A2A);
functionAx_post_type_link(A$post_link,A$postA)A{
AAswitchA(A$post1>post_typeA)A{
AAAAcaseA'x_product':
AAAAAA$post_linkA=Ahome_url(A"/{$post1>post_name}/"A);
AAAAAAbreak;
AAAAcaseA'x_model':
AAAAAA$product_linkA=Ax_post_type_link(Afalse,Aget_post(A$post1>post_parentA)A);
AAAAAA$post_linkA=A"{$product_link}{$post1>post_name}/";
AAAAAAbreak;
AA}
AAreturnA$post_link;
}
/{product_name}/{model_name}/
Make the Slug Editable
72
add_filter(A'post_type_link',A'x_post_type_link',A10,A3A);
functionAx_post_type_link(A$post_link,A$post,A$leavenameA=AfalseA)A{
AA$slugA=A$leavenameA?A"%{$post1>post_type}%"A:A$post1>post_name;
AAswitchA(A$post1>post_typeA)A{
AAAAcaseA'x_product':
AAAAAA$post_linkA=Ahome_url(A"/{$slug}/"A);
AAAAAAbreak;
AAAAcaseA'x_model':
AAAAAA$product_linkA=Ax_post_type_link(Afalse,Aget_post(A$post1>post_parentA)A);
AAAAAA$post_linkA=A"{$product_link}{$slug}/";
AAAAAAbreak;
AA}
AAreturnA$post_link;
}
/{product_name}/A{model_name}A/{model_name}
Changing the Sample Permalink
73
add_filter(A'post_type_link',A'x_post_type_link',A10,A3A);
functionAx_post_type_link(A$post_link,A$post,A$leavenameA=AfalseA)A{
AA$slugA=A$leavenameA?A"%{$post1>post_type}%"A:A$post1>post_name;
AAswitchA(A$post1>post_typeA)A{
AAAAcaseA'x_product':
AAAAAA$post_linkA=Ahome_url(A"/{$slug}/"A);
AAAAAAbreak;
AAAAcaseA'x_model':
AAAAAA$product_linkA=Ax_post_type_link(Afalse,Aget_post(A$post1>post_parentA)A);
AAAAAA$post_linkA=A"{$product_link}{$slug}/";
AAAAAAbreak;
AA}
AAifA(A$sampleA)A{
AAAA//ANO#CLUE#WHY#YOU'D#WANT#TO#DO#THIS...#BUT#YOU#CAN!
AAAA$post_linkA=A"11111>[A%{$post1>post_type}%A]<11111";
AA}
AAreturnA$post_link;
}
11111>[AAAAAAAAAAAAAA]<11111{model_name}
74
In Review
• How WordPress Routes URLs
• index.php and .htaccess
• That URLs ultimately create $argsAfor WP_Query()
• Rewrite Rules and how to see/test them.
• How and why to Flush Rules
• The Rewrite API Functions
• Especially add_rewrite_rule() and add_rewrite_tag()
• The Rewrite API Hooks
• Especially 'parse_request'and 'do_parse_request'
75
What We Learned Today
In Review (cont'd)
• About Endpoints, Permastructs and External Rules
• How abysmal generate_rewrite_rules() is
• All them crazii hooks when generating.
• The Strong Magic: do_parse_request
• Provides Context Sensitive URLs
• How to discover the query vars needed to route URLs
• Generating links, editing elugs and changing samples.
• And finally the logic flow for URL rewriting/routing.
76
What Else We Learned Today
About the 'x_' Prefix
See: http://nacin.com/2010/05/11/in-wordpress-prefix-everything/
24
I just used 'x_' (FOR THIS TALK, ONLY) because it was short!
25
And Stuff I Wrote for the Intro
77
• But I decided it didn't flow.
• Like how scenes gets left...
• on the cutting room floor.
• So I added to the DVD. ;-)
WordPress' URL Patterns
78
• Post & Page URL Patterns
• Archive URL Patterns
• Feed URL Patterns
• Other URL Patterns
Post & Page URL Patterns
Posts /{year}/{month}/{day}/{postname}/
Pages /{pagename}/
Hierarchical Pages /{rootname}/../{leafname}/
Custom Post Types /{post_type_slug}/{postname}/
Attachments
/{y}/{m}/{d}/attachment/{attachment}/
Attachments /{whatever}/attachment/{attachment}/Attachments
/{whatever}/{attachment}/
79
Archive URL Patterns
Tag Archive /tag/{tag}/
Category Archive /category/{category}/
Author Archive /author/{author_name}/
Search Results /search/{s}/AandA/s?={s}
Post Format Archive /type/{post_format}/
Custom Post Type Archive /{post_type_slug}/
Also Day, Month andYear Archives.
80
Feed URL Patterns
Feed* /feed/{feed|rdf|rss|rss2|atom}/
Category Feed* /category/{category}/feed/{feed_type}/
Tag Feed* /tag/{tag}/feed/{feed_type}/
Comment Feed* /comment/feed/{feed_type}/
Search Results Feed* /search/{s}/feed/{feed_type}/
Author Feed* /author/{author_name}/feed/{feed_type}/
**{feed_type}*=* Afeed,Arss,Ardf,Arss2,Aatom
Also Attachment, Post, Page, Day, Month andYear Feeds. (Why?)
81
Other URL Patterns
Robots /robots.txt
Register /{whatever}/wp1register.php
Comments /{post_url}/comment1page1{cpage}/
Trackback /{post_url}/trackback/{whatever}
And some... I’m*sure*I*forgot*about.
82
• The mapping of a URL Path of an HTTP
Request identifying the Resource on your
WordPress site for which you want an
HTTP Response to return a Representation.
• Whew! What a mouthful!
What is URL Routing?
83
• Because we want to control what
Representation is returned for a
given URL Path.
• So let’s define those terms...
Why Do We Care?
84
What is a URL Path?
• Example URL:
• http://example.com/2014/03/15/hello-world/
• The URL Path is:
• /2014/03/15/hello-world/
85
What is an HTTP Request?
It’s when you type a URL into your browser and press Enter!
86
What is a Resource?
• It’s the idea of the thing you want, the thing
located at the URL you type into your browser.
• Don’t worry, nerd types have argued over this
definition for years!
• A Representation is simply the HTML file,
image or other file you want when you enter a
URL.
87
What is an HTTP Response?
• It is what your WordPress website packages up
and sends back to your browser and is based
on the URL path you request.
88
It’s what gets downloaded when you
request a URL with your browser.
What is a Representation?
89
• So in review:
URL Routing is the mapping of a URL Path
of an HTTP Request identifying the Resource
on yourWordPress site for which you want an
HTTP Response to return a Representation.
See, That Wasn’t Hard!
90
Future Reading
• The Rewrite API:The Basics
• http://code.tutsplus.com/tutorials/the-rewrite-api-the-basics--wp-25474
• The Rewrite API: Post Types & Taxonomies
• http://code.tutsplus.com/articles/the-rewrite-api-post-types-taxonomies--wp-25488
• A (Mostly) Complete Guide to the WordPress Rewrite API
• http://o.pmg.co/a-mostly-complete-guide-to-the-wordpress-rewrite-api
• Some background on rewrite rules
• http://wordpress.stackexchange.com/a/5478/89
91
Me, in More Detail
• President, NewClarity LLC
• Boutique WordPress consulting firm with a small but expert team.
• Specialize in complex sites & vertical market products
• Offering: Back-end architecture, implementation, training, code reviews.
• 375+ Answers at WordPress Answers
• Was a founding moderator
• Have blogged at HardcoreWP.com
• Hope to find time again (doh!)
• Several "Libraries" for WordPress at github.com/newclarity
• Exo - "MVC that doesn't changeWordPress, it just makes it stronger."
• Sunrise - Code-based Forms and Fields
• Dispatch - Hardcore URL Routing forWordPress
• Contact us for more of the "Strong Magic"
• mike@newclarity.net
92
Thanks for Attending
93
May all your URLs be pretty
and may they all match
their intended patterns
Mike Schinkel
@mikeschinkel
mike@newclarity.net
1 of 85

Recommended

Hardcore URL Routing for WordPress - WordCamp Atlanta 2014 (PPT) by
Hardcore URL Routing for WordPress - WordCamp Atlanta 2014 (PPT)Hardcore URL Routing for WordPress - WordCamp Atlanta 2014 (PPT)
Hardcore URL Routing for WordPress - WordCamp Atlanta 2014 (PPT)Mike Schinkel
10.5K views92 slides
Rails 4.0 by
Rails 4.0Rails 4.0
Rails 4.0Robert Gogolok
2.9K views62 slides
Moving from Django Apps to Services by
Moving from Django Apps to ServicesMoving from Django Apps to Services
Moving from Django Apps to ServicesCraig Kerstiens
3.4K views118 slides
Introduction to plugin development by
Introduction to plugin developmentIntroduction to plugin development
Introduction to plugin developmentCaldera Labs
164.3K views53 slides
Synapseindia reviews sharing intro cakephp by
Synapseindia reviews sharing intro cakephpSynapseindia reviews sharing intro cakephp
Synapseindia reviews sharing intro cakephpSynapseindiaComplaints
517 views65 slides
The Best (and Worst) of Django by
The Best (and Worst) of DjangoThe Best (and Worst) of Django
The Best (and Worst) of DjangoJacob Kaplan-Moss
35.7K views53 slides

More Related Content

What's hot

Django Heresies by
Django HeresiesDjango Heresies
Django HeresiesSimon Willison
34K views83 slides
Rails 3 overview by
Rails 3 overviewRails 3 overview
Rails 3 overviewYehuda Katz
2.1K views139 slides
Hooked on WordPress: WordCamp Columbus by
Hooked on WordPress: WordCamp ColumbusHooked on WordPress: WordCamp Columbus
Hooked on WordPress: WordCamp ColumbusShawn Hooper
1.2K views40 slides
Oracle APEX Performance by
Oracle APEX PerformanceOracle APEX Performance
Oracle APEX PerformanceScott Wesley
3.5K views127 slides
I Love codeigniter, You? by
I Love codeigniter, You?I Love codeigniter, You?
I Love codeigniter, You?إسماعيل عاشور
1.1K views23 slides
Django - 次の一歩 gumiStudy#3 by
Django - 次の一歩 gumiStudy#3Django - 次の一歩 gumiStudy#3
Django - 次の一歩 gumiStudy#3makoto tsuyuki
2.6K views150 slides

What's hot(20)

Rails 3 overview by Yehuda Katz
Rails 3 overviewRails 3 overview
Rails 3 overview
Yehuda Katz2.1K views
Hooked on WordPress: WordCamp Columbus by Shawn Hooper
Hooked on WordPress: WordCamp ColumbusHooked on WordPress: WordCamp Columbus
Hooked on WordPress: WordCamp Columbus
Shawn Hooper1.2K views
Oracle APEX Performance by Scott Wesley
Oracle APEX PerformanceOracle APEX Performance
Oracle APEX Performance
Scott Wesley3.5K views
Django - 次の一歩 gumiStudy#3 by makoto tsuyuki
Django - 次の一歩 gumiStudy#3Django - 次の一歩 gumiStudy#3
Django - 次の一歩 gumiStudy#3
makoto tsuyuki2.6K views
Action View Form Helpers - 1, Season 2 by RORLAB
Action View Form Helpers - 1, Season 2Action View Form Helpers - 1, Season 2
Action View Form Helpers - 1, Season 2
RORLAB848 views
Rails 3 Beautiful Code by GreggPollack
Rails 3 Beautiful CodeRails 3 Beautiful Code
Rails 3 Beautiful Code
GreggPollack13.1K views
Introduction to CodeIgniter (RefreshAugusta, 20 May 2009) by Michael Wales
Introduction to CodeIgniter (RefreshAugusta, 20 May 2009)Introduction to CodeIgniter (RefreshAugusta, 20 May 2009)
Introduction to CodeIgniter (RefreshAugusta, 20 May 2009)
Michael Wales1.3K views
Powershell to the People #suguk by Chris McKinley
Powershell to the People #sugukPowershell to the People #suguk
Powershell to the People #suguk
Chris McKinley488 views
Maintainable JavaScript 2012 by Nicholas Zakas
Maintainable JavaScript 2012Maintainable JavaScript 2012
Maintainable JavaScript 2012
Nicholas Zakas90.8K views
Zend Framework 1.9 Setup & Using Zend_Tool by Gordon Forsythe
Zend Framework 1.9 Setup & Using Zend_ToolZend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_Tool
Gordon Forsythe8.5K views
シックス・アパート・フレームワーク by Takatsugu Shigeta
シックス・アパート・フレームワークシックス・アパート・フレームワーク
シックス・アパート・フレームワーク
Takatsugu Shigeta3.3K views
Rails for Beginners - Le Wagon by Alex Benoit
Rails for Beginners - Le WagonRails for Beginners - Le Wagon
Rails for Beginners - Le Wagon
Alex Benoit147 views
Routing 1, Season 1 by RORLAB
Routing 1, Season 1Routing 1, Season 1
Routing 1, Season 1
RORLAB656 views
Satellite Apps around the Cloud: Integrating your infrastructure with JIRA St... by Atlassian
Satellite Apps around the Cloud: Integrating your infrastructure with JIRA St...Satellite Apps around the Cloud: Integrating your infrastructure with JIRA St...
Satellite Apps around the Cloud: Integrating your infrastructure with JIRA St...
Atlassian462 views
Bootstrat REST APIs with Laravel 5 by Elena Kolevska
Bootstrat REST APIs with Laravel 5Bootstrat REST APIs with Laravel 5
Bootstrat REST APIs with Laravel 5
Elena Kolevska3.9K views

Similar to Hardcore URL Routing for WordPress - WordCamp Atlanta 2014

Resource Routing in ExpressionEngine by
Resource Routing in ExpressionEngineResource Routing in ExpressionEngine
Resource Routing in ExpressionEngineMichaelRog
3K views47 slides
LAB PHP Consolidated.ppt by
LAB PHP Consolidated.pptLAB PHP Consolidated.ppt
LAB PHP Consolidated.pptYasirAhmad80
2 views107 slides
Mule soft RAML API Designing by
Mule soft RAML API DesigningMule soft RAML API Designing
Mule soft RAML API DesigningRaja Reddy
297 views17 slides
PHP Basic by
PHP BasicPHP Basic
PHP BasicYoeung Vibol
476 views96 slides
Ruby on Rails + AngularJS + Twitter Bootstrap by
Ruby on Rails + AngularJS + Twitter BootstrapRuby on Rails + AngularJS + Twitter Bootstrap
Ruby on Rails + AngularJS + Twitter BootstrapMarcio Marinho
3K views21 slides
Oracle APEX URLs Untangled & SEOptimized by
Oracle APEX URLs Untangled & SEOptimizedOracle APEX URLs Untangled & SEOptimized
Oracle APEX URLs Untangled & SEOptimizedChristian Rokitta
6K views30 slides

Similar to Hardcore URL Routing for WordPress - WordCamp Atlanta 2014(20)

Resource Routing in ExpressionEngine by MichaelRog
Resource Routing in ExpressionEngineResource Routing in ExpressionEngine
Resource Routing in ExpressionEngine
MichaelRog3K views
Mule soft RAML API Designing by Raja Reddy
Mule soft RAML API DesigningMule soft RAML API Designing
Mule soft RAML API Designing
Raja Reddy297 views
Ruby on Rails + AngularJS + Twitter Bootstrap by Marcio Marinho
Ruby on Rails + AngularJS + Twitter BootstrapRuby on Rails + AngularJS + Twitter Bootstrap
Ruby on Rails + AngularJS + Twitter Bootstrap
Marcio Marinho3K views
Lunacloud's Compute RESTful API - Programmer's Guide by Lunacloud
Lunacloud's Compute RESTful API - Programmer's GuideLunacloud's Compute RESTful API - Programmer's Guide
Lunacloud's Compute RESTful API - Programmer's Guide
Lunacloud539 views
Design API using RAML - basics by kunal vishe
Design API using RAML - basicsDesign API using RAML - basics
Design API using RAML - basics
kunal vishe1.6K views
At Begin, URL Handling and REST by Brian Loomis
At Begin, URL Handling and RESTAt Begin, URL Handling and REST
At Begin, URL Handling and REST
Brian Loomis2K views
Using Sinatra to Build REST APIs in Ruby by LaunchAny
Using Sinatra to Build REST APIs in RubyUsing Sinatra to Build REST APIs in Ruby
Using Sinatra to Build REST APIs in Ruby
LaunchAny9.5K views
Laravel - Website Development in Php Framework. by SWAAM Tech
Laravel - Website Development in Php Framework.Laravel - Website Development in Php Framework.
Laravel - Website Development in Php Framework.
SWAAM Tech4.8K views
Super simple introduction to REST-APIs (2nd version) by Patrick Savalle
Super simple introduction to REST-APIs (2nd version)Super simple introduction to REST-APIs (2nd version)
Super simple introduction to REST-APIs (2nd version)
Patrick Savalle18.6K views
Php i basic chapter 3 (syahir chaer's conflicted copy 2013-04-22) by Muhamad Al Imran
Php i basic chapter 3 (syahir chaer's conflicted copy 2013-04-22)Php i basic chapter 3 (syahir chaer's conflicted copy 2013-04-22)
Php i basic chapter 3 (syahir chaer's conflicted copy 2013-04-22)
Muhamad Al Imran59.1K views
Php i basic chapter 3 (afifah rosli's conflicted copy 2013-04-23) by Muhamad Al Imran
Php i basic chapter 3 (afifah rosli's conflicted copy 2013-04-23)Php i basic chapter 3 (afifah rosli's conflicted copy 2013-04-23)
Php i basic chapter 3 (afifah rosli's conflicted copy 2013-04-23)
Muhamad Al Imran88.8K views

Recently uploaded

Setting Up Your First CloudStack Environment with Beginners Challenges - MD R... by
Setting Up Your First CloudStack Environment with Beginners Challenges - MD R...Setting Up Your First CloudStack Environment with Beginners Challenges - MD R...
Setting Up Your First CloudStack Environment with Beginners Challenges - MD R...ShapeBlue
173 views15 slides
Elevating Privacy and Security in CloudStack - Boris Stoyanov - ShapeBlue by
Elevating Privacy and Security in CloudStack - Boris Stoyanov - ShapeBlueElevating Privacy and Security in CloudStack - Boris Stoyanov - ShapeBlue
Elevating Privacy and Security in CloudStack - Boris Stoyanov - ShapeBlueShapeBlue
222 views7 slides
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ... by
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...ShapeBlue
166 views28 slides
DRBD Deep Dive - Philipp Reisner - LINBIT by
DRBD Deep Dive - Philipp Reisner - LINBITDRBD Deep Dive - Philipp Reisner - LINBIT
DRBD Deep Dive - Philipp Reisner - LINBITShapeBlue
180 views21 slides
"Surviving highload with Node.js", Andrii Shumada by
"Surviving highload with Node.js", Andrii Shumada "Surviving highload with Node.js", Andrii Shumada
"Surviving highload with Node.js", Andrii Shumada Fwdays
56 views29 slides
2FA and OAuth2 in CloudStack - Andrija Panić - ShapeBlue by
2FA and OAuth2 in CloudStack - Andrija Panić - ShapeBlue2FA and OAuth2 in CloudStack - Andrija Panić - ShapeBlue
2FA and OAuth2 in CloudStack - Andrija Panić - ShapeBlueShapeBlue
147 views23 slides

Recently uploaded(20)

Setting Up Your First CloudStack Environment with Beginners Challenges - MD R... by ShapeBlue
Setting Up Your First CloudStack Environment with Beginners Challenges - MD R...Setting Up Your First CloudStack Environment with Beginners Challenges - MD R...
Setting Up Your First CloudStack Environment with Beginners Challenges - MD R...
ShapeBlue173 views
Elevating Privacy and Security in CloudStack - Boris Stoyanov - ShapeBlue by ShapeBlue
Elevating Privacy and Security in CloudStack - Boris Stoyanov - ShapeBlueElevating Privacy and Security in CloudStack - Boris Stoyanov - ShapeBlue
Elevating Privacy and Security in CloudStack - Boris Stoyanov - ShapeBlue
ShapeBlue222 views
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ... by ShapeBlue
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...
ShapeBlue166 views
DRBD Deep Dive - Philipp Reisner - LINBIT by ShapeBlue
DRBD Deep Dive - Philipp Reisner - LINBITDRBD Deep Dive - Philipp Reisner - LINBIT
DRBD Deep Dive - Philipp Reisner - LINBIT
ShapeBlue180 views
"Surviving highload with Node.js", Andrii Shumada by Fwdays
"Surviving highload with Node.js", Andrii Shumada "Surviving highload with Node.js", Andrii Shumada
"Surviving highload with Node.js", Andrii Shumada
Fwdays56 views
2FA and OAuth2 in CloudStack - Andrija Panić - ShapeBlue by ShapeBlue
2FA and OAuth2 in CloudStack - Andrija Panić - ShapeBlue2FA and OAuth2 in CloudStack - Andrija Panić - ShapeBlue
2FA and OAuth2 in CloudStack - Andrija Panić - ShapeBlue
ShapeBlue147 views
Backroll, News and Demo - Pierre Charton, Matthias Dhellin, Ousmane Diarra - ... by ShapeBlue
Backroll, News and Demo - Pierre Charton, Matthias Dhellin, Ousmane Diarra - ...Backroll, News and Demo - Pierre Charton, Matthias Dhellin, Ousmane Diarra - ...
Backroll, News and Demo - Pierre Charton, Matthias Dhellin, Ousmane Diarra - ...
ShapeBlue186 views
Developments to CloudStack’s SDN ecosystem: Integration with VMWare NSX 4 - P... by ShapeBlue
Developments to CloudStack’s SDN ecosystem: Integration with VMWare NSX 4 - P...Developments to CloudStack’s SDN ecosystem: Integration with VMWare NSX 4 - P...
Developments to CloudStack’s SDN ecosystem: Integration with VMWare NSX 4 - P...
ShapeBlue194 views
Backup and Disaster Recovery with CloudStack and StorPool - Workshop - Venko ... by ShapeBlue
Backup and Disaster Recovery with CloudStack and StorPool - Workshop - Venko ...Backup and Disaster Recovery with CloudStack and StorPool - Workshop - Venko ...
Backup and Disaster Recovery with CloudStack and StorPool - Workshop - Venko ...
ShapeBlue184 views
Import Export Virtual Machine for KVM Hypervisor - Ayush Pandey - University ... by ShapeBlue
Import Export Virtual Machine for KVM Hypervisor - Ayush Pandey - University ...Import Export Virtual Machine for KVM Hypervisor - Ayush Pandey - University ...
Import Export Virtual Machine for KVM Hypervisor - Ayush Pandey - University ...
ShapeBlue119 views
State of the Union - Rohit Yadav - Apache CloudStack by ShapeBlue
State of the Union - Rohit Yadav - Apache CloudStackState of the Union - Rohit Yadav - Apache CloudStack
State of the Union - Rohit Yadav - Apache CloudStack
ShapeBlue297 views
VNF Integration and Support in CloudStack - Wei Zhou - ShapeBlue by ShapeBlue
VNF Integration and Support in CloudStack - Wei Zhou - ShapeBlueVNF Integration and Support in CloudStack - Wei Zhou - ShapeBlue
VNF Integration and Support in CloudStack - Wei Zhou - ShapeBlue
ShapeBlue203 views
Extending KVM Host HA for Non-NFS Storage - Alex Ivanov - StorPool by ShapeBlue
Extending KVM Host HA for Non-NFS Storage -  Alex Ivanov - StorPoolExtending KVM Host HA for Non-NFS Storage -  Alex Ivanov - StorPool
Extending KVM Host HA for Non-NFS Storage - Alex Ivanov - StorPool
ShapeBlue123 views
Live Demo Showcase: Unveiling Dell PowerFlex’s IaaS Capabilities with Apache ... by ShapeBlue
Live Demo Showcase: Unveiling Dell PowerFlex’s IaaS Capabilities with Apache ...Live Demo Showcase: Unveiling Dell PowerFlex’s IaaS Capabilities with Apache ...
Live Demo Showcase: Unveiling Dell PowerFlex’s IaaS Capabilities with Apache ...
ShapeBlue126 views
iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas... by Bernd Ruecker
iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas...iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas...
iSAQB Software Architecture Gathering 2023: How Process Orchestration Increas...
Bernd Ruecker54 views
Enabling DPU Hardware Accelerators in XCP-ng Cloud Platform Environment - And... by ShapeBlue
Enabling DPU Hardware Accelerators in XCP-ng Cloud Platform Environment - And...Enabling DPU Hardware Accelerators in XCP-ng Cloud Platform Environment - And...
Enabling DPU Hardware Accelerators in XCP-ng Cloud Platform Environment - And...
ShapeBlue106 views
Declarative Kubernetes Cluster Deployment with Cloudstack and Cluster API - O... by ShapeBlue
Declarative Kubernetes Cluster Deployment with Cloudstack and Cluster API - O...Declarative Kubernetes Cluster Deployment with Cloudstack and Cluster API - O...
Declarative Kubernetes Cluster Deployment with Cloudstack and Cluster API - O...
ShapeBlue132 views

Hardcore URL Routing for WordPress - WordCamp Atlanta 2014