カスタム投稿タイプのパーマリンクをカスタム設定できるWordPressプラグイン・Custom Post Type Permalinks

Ads

Warning: Undefined array key "HTTP_USER_AGENT" in /home/youhei0828/kachibito.net/public_html/wp-content/themes/kachibito7_with_cocoon_child/functions-module/other/tiny-fixed.php on line 75

WordPressのカスタム投稿タイプに関する記事が凄く多くなって来ましたが、まだWPに不慣れですとコードの管理が結構大変です。functions.php頼りという感じですしね。そういうわけなので、せめてパーマリンク化くらいはプラグインにお願いしましょう。

当サイトも含め、カスタマイズ記事は沢山あるんですけど、WordPressの大きな魅力はプラグインの豊富さです。もちろん、他デベロッパーへの依存にも繋がるのでメリットばかりとは言えませんが、工数を大きく減らせるのは間違いありません。

Custom Post Type Permalinks


こんな感じでカスタム投稿タイプで作成した記事のURLを、通常の投稿同様にカスタム構造のパーマリンクに変更できます。※事前にパーマリンク設定がカスタム構造に設定されている必要があります。

  • 各カスタム投稿タイプのパーマリンクを設定
  • 各カスタム投稿タイプごとのアーカイブのURL,日付によるアーカイブのURLを追加
  • wp_get_archives()のパラメータにpost_type=exampleを追加
  • カスタム分類のパーマリンクを“/投稿タイプ/カスタム分類名/カスタム分類”に変更

といった特徴があるとの事。楽でいいですね。


タクソノミーが追加されている状態でプラグインを有効化すると、追加したタクソノミーの設定項目がぞれぞれ「設定→パーマリンク設定」のページに現れます。通常のパーマリンク同様のパターン(%postname%など)を使う事が出来ます。

設定後に、追加したカスタム投稿タイプで記事を書けば、普通の投稿同様にパーマリンクの設定項目が出現します。

カスタム投稿タイプを使う

ついでにカスタム投稿タイプを使用している際によく使うコードやTipsをメモします。

と、その前に、「カスタム投稿タイプって何?」っていう方も多いかもしれません。

以下のように理解すると分かりやすいです。

  • 普通にWordPressで記事を書いたとします。この投稿タイプは「post」。
  • 普通にWordPressで固定ページを書いたとします。この投稿タイプは「page」。
  • snippetという名のカスタム投稿タイプで記事を書いたとします。この投稿タイプは「snippet」。

という感じです。postとは別の投稿タイプを追加できるのがカスタム投稿タイプです。

以前書いたので宜しければあわせてごらんください。

WordPressのカスタム投稿(ポスト)タイプを作成するまでの手順リスト

では、以下よりどうぞ。

※投稿タイプは「example」と仮定します。

カスタム投稿タイプのアーカイブを一覧表示する

<?php wp_get_archives('type=example'); ?>

さっき出てきたやつですね。

条件分岐する

<?php if (get_post_type() === 'example'): ?>
 カスタム投稿タイプがexampleのときはこっちが表示
<?php else: ?>
  それ以外のページはこっちが表示
<?php endif; ?>

シンプルな条件分岐です。

テンプレート階層

カスタム投稿タイプとカスタムタクソノミーのテンプレート階層です。

【投稿ページ】
single-exampel.php
single.php
index.php

【アーカイブ】
archive-example.php
archive.php
index.php

【カスタムタクソノミー】
ついで。abcというカスタムタクソノミーでタームがxyzの場合。
taxonomy-abc-xyz.php
taxonomy-abc.php
taxonomy.php
archive.php
index.php

カスタムタクソノミーがやっぱり便利ですよね。

投稿タイプにカスタムタクソノミーを追加する

exampleという投稿タイプにabcというタクソノミーを追加する

add_action( 'init', 'create_example_taxonomies', 0 );
function create_example_taxonomies() 
{
  // 階層化可能な新規のタクソノミーを追加
  $labels = array(
    'name' => _x( 'abc', 'taxonomy general name' ),
    'singular_name' => _x( 'abc', 'taxonomy singular name' ),
    'search_items' =>  __( 'Search abc' ),
    'all_items' => __( 'All abc' ),
    'parent_item' => __( 'Parent abc' ),
    'parent_item_colon' => __( 'Parent abc:' ),
    'edit_item' => __( 'edit abc' ), 
    'update_item' => __( 'abc update' ),
    'add_new_item' => __( 'Add new abc' ),
    'new_item_name' => __( 'New abc Name' ),
  ); 	

  register_taxonomy('abc',array('example'), array(
    'hierarchical' => true,
    'labels' => $labels,
    'show_ui' => true,
    'query_var' => true,
    'rewrite' => array( 'slug' => 'abc'),
  ));
}

以下はjQueryとWordPressというタクソノミーを加えたものです。WPが小文字になってたw

詳しくはCODEXをご覧下さい。

カスタム投稿タイプの投稿を一覧表示する

<?php
	query_posts(
		Array(
			'post_type' => 'example',
			'orderby' => 'date',
			'order' => 'DESC',
			'paged' => get_query_var('paged')
		)
	);
	if (have_posts()) : while (have_posts()) : the_post();
?>
	<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
<?php endwhile; endif; wp_reset_query(); ?>

複数の投稿タイプを表示させるなら

'post_type' => array( 'post', 'page', 'example', 'example2' ),

と、混ぜてあげてください。

via:Codex – query posts
via:WordPress で新着記事を表示する方法の決定版

ダッシュボードにカスタム投稿の投稿数を表示する

function custom_post_dashboard() {
    $custom_post_type = 'example'; // カスタム投稿タイプのラベル
    global $wp_post_types;
    $num_post_type = wp_count_posts( $custom_post_type );
    $num = number_format_i18n($num_post_type->publish);
    $text = _n( $wp_post_types[$custom_post_type]->labels->singular_name, $wp_post_types[$custom_post_type]->labels->name, $num_post_type->publish );
    $capability = $wp_post_types[$custom_post_type]->cap->edit_posts;
 
    if (current_user_can($capability)) {
        $num = "<a href='edit.php?post_type=" . $custom_post_type . "'>$num</a>";
        $text = "<a href='edit.php?post_type=" . $custom_post_type . "'>$text</a>";
    }
 
    echo '<tr>';
    echo '<td class="first b b_' . $custom_post_type . '">' . $num . '</td>';
    echo '<td class="t ' . $custom_post_type . '">' . $text . '</td>';
    echo '</tr>';
}
add_action('right_now_content_table_end', 'custom_post_dashboard');

↓ これでダッシュボードに投稿タイプの項目が追加されます。

via:Standing on the Shoulder of Linus

パンくずを表示する

ループ内に含めます。

<?php $type = get_post_type_object($post->post_type); ?>
<div class="breadcrumb">
  <a href="<?php echo home_url( '/' ); ?>">HOME</a> &rsaquo; 
  <a href="<?php echo get_post_type_archive_link( $type->name ); ?>"><?php echo esc_attr($type->label); ?></a> &rsaquo; 
  <?php the_title(); ?>
</div>

パンくずを自作している方は先ほどの条件分岐と併用すると良さそうですね。

via:varl

指定のカスタム投稿ページでターム単位で条件分岐する

タームって言うのはカスタムタクソノミーとかで作成したカテゴリとかタグの事です。下図で言うと、紫と茶色がタームに当たります。

例えばabcとdefっていう2つのカテゴリを作成し、投稿ページでそれぞれのカテゴリに属してる時に分岐させたい、とします。以下のようにループ内で分岐させます。

<?php if(have_posts()): while(have_posts()): the_post(); ?>
<?php
  $postID = get_the_ID();
  $terms = get_the_terms($postID,'example');
  foreach ( $terms as $term ) {
    $tslug = esc_html($term->slug);
  }
?>
<?php endwhile; endif; ?>
<?php if($tslug === 'abc'): ?>
  スラッグがabcならこっちが表示
<?php elseif($tslug === 'def'): ?>
  スラッグがdefならこっちが表示
<?php endif; ?>

こちらはちゃんと検証してませんが問題なく動くっぽいです。

via:カスタム投稿のsingleページ内を条件分岐させたい

カスタムタクソノミーを使ったWebサイトの構築にはタームが必ず関わります。タームに関しては本家リファレンスを見たほうがいいかもしれません。

※is_term()は現在非推奨となっています。代わりにterm_exists()を使用します。

キリがないのでこの辺で・・殆ど基本しか書いていませんが、基本さえ分かっていれば運営にはさほど困らない・・と思います。その他ググれば出てきますので、今日ご紹介したCustom Post Type Permalinksと併用して素敵なWebサイトを作ってくださいませ。

余談

ちょっと余談なんですが、こういったパーマリンクのカスタム化も無理にfunctions.phpで~という必要は全くありません。この辺は割と面倒で、問題も生じやすい側面もあります。

貴方が開発者なら試行錯誤は勉強になるでしょうが、そうでないならこういったカスタマイズに大量の時間を費やし、無理をして問題が生じるリスクを背負うのが果たして正しい選択なのかな、という疑問はたまに感じます。以前、TwitterでWPのバージョンアップをしたら画面が真っ白になったのを「WPのバグ」と仰っている方を見かけましたが、実際はfunctions.phpで自分で書かれたコードのミスが原因でした。

カスタマイズ出来ない事は恥ではなく、むしろ無理して問題を発生させるほうを恥じるべきではないかと、個人的には思います。クライアントワークならあってはならない事ですし・・

勿論学べば問題回避に繋がるのは確かですが、プラグインで済むならプラグインを使う事をオススメします。大勢で使ってフィードバックを送り、良いものを皆で作れる、というのもオープンソースならではの利点じゃないですかね。

フィードバックも開発に必要不可欠なのですから、使って感想や問題を開発者に伝えるだけでも、あなたも開発者の一員と言えるわけです。特に、このプラグインのデベロッパーさんは日本人です。フィードバックもしやすいと思いますよ。

ぜひ使ってみてください

Custom Post Type Permalinks

version 3.0.83 (Wed, 16 Apr 2014 03:56:09 GMT)
http://alexgorbatchev.com/SyntaxHighlighter
JavaScript code syntax highlighter.
Copyright 2004-2013 Alex Gorbatchev.
If you like this script, please donate to
keep development active!

'}},vars:{discoveredBrushes:null,highlighters:{}},brushes:{},regexLib:{multiLineCComments:XRegExp("/\\*.*?\\*/","gs"),singleLineCComments:/\/\/.*$/gm,singleLinePerlComments:/#.*$/gm,doubleQuotedString:/"([^\\"\n]|\\.)*"/g,singleQuotedString:/'([^\\'\n]|\\.)*'/g,multiLineDoubleQuotedString:XRegExp('"([^\\\\"]|\\\\.)*"',"gs"),multiLineSingleQuotedString:XRegExp("'([^\\\\']|\\\\.)*'","gs"),xmlComments:XRegExp("(<|<)!--.*?--(>|>)","gs"),url:/https?:\/\/[\w-.\/?%&=:@;#]*/g,phpScriptTags:{left:/(<|<)\?(?:=|php)?/g,right:/\?(>|>)/g,eof:!0},aspScriptTags:{left:/(<|<)%=?/g,right:/%(>|>)/g},scriptScriptTags:{left:/(<|<)\s*script.*?(>|>)/gi,right:/(<|<)\/\s*script\s*(>|>)/gi}},toolbar:{getHtml:function(e){function t(e,t){return B.toolbar.getButtonHtml(e,t,B.config.strings[t])}for(var n='

',r=B.toolbar.items,i=r.list,a=0,l=i.length;l>a;a++)n+=(r[i[a]].getHtml||t)(e,i[a]);return n+="

"},getButtonHtml:function(t,n,r){return n=e(n),''+e(r)+""},handler:function(e){function t(e){var t=RegExp(e+"_(\\w+)"),n=t.exec(r);return n?n[1]:null}var n=e.target,r=n.className||"",i=s(g(n,".syntaxhighlighter").id),a=t("command");i&&a&&B.toolbar.items[a].execute(i),e.preventDefault()},items:{list:["expandSource","help"],expandSource:{getHtml:function(e){if(1!=e.getParam("collapse"))return"";var t=e.getParam("title");return B.toolbar.getButtonHtml(e,"expandSource",t?t:B.config.strings.expandSource)},execute:function(e){var t=o(e.id);r(t,"collapsed")}},help:{execute:function(){var e=x("","_blank",500,250,"scrollbars=0"),t=e.document;t.write(B.config.strings.aboutDialog),t.close(),e.focus()}}}},findElements:function(e,t){var n=t?[t]:i(document.getElementsByTagName(B.config.tagName)),r=B.config,a=[];if(r.useScriptTags&&(n=n.concat(A())),0===n.length)return a;for(var l=0,s=n.length;s>l;l++){var o={target:n[l],params:p(e,E(n[l].className))};null!=o.params.brush&&a.push(o)}return a},highlight:function(e,t){var n=this.findElements(e,t),r="innerHTML",i=null,a=B.config;if(0!==n.length)for(var l=0,s=n.length;s>l;l++){var o,t=n[l],u=t.target,c=t.params,g=c.brush;if(null!=g){if("true"==c["html-script"]||1==B.defaults["html-script"])i=new B.HtmlScript(g),g="htmlscript";else{var h=b(g);if(!h)continue;i=new h}o=u[r],a.useScriptTags&&(o=M(o)),""!=(u.title||"")&&(c.title=u.title),c.brush=g,i.init(c),t=i.getDiv(o),""!=(u.id||"")&&(t.id=u.id),u.parentNode.replaceChild(t,u)}}},all:function(e){m(window,"load",function(){B.highlight(e)})}};return B.Match=function(e,t,n){this.value=e,this.index=t,this.length=e.length,this.css=n,this.brushName=null},B.Match.prototype.toString=function(){return this.value},B.HtmlScript=function(e){function t(e,t){for(var n=0,r=e.length;r>n;n++)e[n].index+=t}function n(e){for(var n,a=e.code,l=[],s=r.regexList,o=e.index+e.left.length,u=r.htmlScript,c=0,g=s.length;g>c;c++)n=L(a,s[c]),t(n,o),l=l.concat(n);null!=u.left&&null!=e.left&&(n=L(e.left,u.left),t(n,e.index),l=l.concat(n)),null!=u.right&&null!=e.right&&(n=L(e.right,u.right),t(n,e.index+e[0].lastIndexOf(e.right)),l=l.concat(n));for(var h=0,g=l.length;g>h;h++)l[h].brushName=i.brushName;return l}var r,i=b(e),a=new B.brushes.Xml,l=this,s="getDiv getHtml init".split(" ");if(null!=i){r=new i;for(var o=0,u=s.length;u>o;o++)(function(){var e=s[o];l[e]=function(){return a[e].apply(a,arguments)}})();return null==r.htmlScript?(v(B.config.strings.brushNotHtmlScript+e),void 0):(a.regexList.push({regex:r.htmlScript.code,func:n}),void 0)}},B.Highlighter=function(){},B.Highlighter.prototype={getParam:function(e,t){var n=this.params[e];return d(null==n?t:n)},create:function(e){return document.createElement(e)},findMatches:function(e,t){var n=[];if(null!=e)for(var r=0,i=e.length;i>r;r++)"object"==typeof e[r]&&(n=n.concat(L(t,e[r])));return this.removeNestedMatches(n.sort(k))},removeNestedMatches:function(e){for(var t=0,n=e.length;n>t;t++)if(null!==e[t])for(var r=e[t],i=r.index+r.length,a=t+1,n=e.length;n>a&&null!==e[t];a++){var l=e[a];if(null!==l){if(l.index>i)break;l.index==r.index&&l.length>r.length?e[t]=null:l.index>=r.index&&i>l.index&&(e[a]=null)}}return e},figureOutLineNumbers:function(e){var t=[],n=parseInt(this.getParam("first-line"));return y(e,function(e,r){t.push(r+n)}),t},isLineHighlighted:function(e){var t=this.getParam("highlight",[]);return"object"!=typeof t&&null==t.push&&(t=[t]),-1!=h(t,""+e)},getLineHtml:function(e,t,n){var r=["line","number"+t,"index"+e,"alt"+(""+(0==t%2?1:2))];return this.isLineHighlighted(t)&&r.push("highlighted"),0==t&&r.push("break"),'

'+n+"

"},getLineNumbersHtml:function(e,t){var n="",r=a(e).length,i=parseInt(this.getParam("first-line")),l=this.getParam("pad-line-numbers");1==l?l=(""+(i+r-1)).length:1==isNaN(l)&&(l=0);for(var s=0;r>s;s++){var o=t?t[s]:i+s,e=0==o?B.config.space:S(o,l);n+=this.getLineHtml(s,o,e)}return n},getCodeLinesHtml:function(e,t){e=C(e);for(var n=a(e),r=(this.getParam("pad-line-numbers"),parseInt(this.getParam("first-line"))),e="",i=this.getParam("brush"),l=0,s=n.length;s>l;l++){var o=n[l],u=/^( |\s)+/.exec(o),c=null,g=t?t[l]:r+l;null!=u&&(c=""+u[0],o=o.substr(c.length),c=c.replace(" ",B.config.space)),o=C(o),0==o.length&&(o=B.config.space),e+=this.getLineHtml(l,g,(null!=c?''+c+"":"")+o)}return e},getTitleHtml:function(t){return t?"

"+e(t)+"

":""},getMatchesHtml:function(e,t){function n(e){var t=e?e.brushName||a:a;return t?t+" ":""}for(var r=0,i="",a=this.getParam("brush",""),l=0,s=t.length;s>l;l++){var o,u=t[l];null!==u&&0!==u.length&&(o=n(u),i+=N(e.substr(r,u.index-r),o+"plain")+N(u.value,o+u.css),r=u.index+u.length+(u.offset||0))}return i+=N(e.substr(r),n()+"plain")},getHtml:function(t){var n,r,i,a="",s=["syntaxhighlighter"];return 1==this.getParam("light")&&(this.params.toolbar=this.params.gutter=!1),className="syntaxhighlighter",1==this.getParam("collapse")&&s.push("collapsed"),0==(gutter=this.getParam("gutter"))&&s.push("nogutter"),s.push(this.getParam("class-name")),s.push(this.getParam("brush")),t=w(t).replace(/\r/g," "),n=this.getParam("tab-size"),t=1==this.getParam("smart-tabs")?R(t,n):H(t,n),this.getParam("unindent")&&(t=P(t)),gutter&&(i=this.figureOutLineNumbers(t)),r=this.findMatches(this.regexList,t),a=this.getMatchesHtml(t,r),a=this.getCodeLinesHtml(a,i),this.getParam("auto-links")&&(a=I(a)),"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.match(/MSIE/)&&s.push("ie"),a='

'+(this.getParam("toolbar")?B.toolbar.getHtml(this):"")+''+this.getTitleHtml(this.getParam("title"))+""+""+(gutter?'":"")+'"+""+""+"
'+this.getLineNumbersHtml(t)+"'+'
'+a+"
"+"
"+"

"},getDiv:function(e){null===e&&(e=""),this.code=e;var t=this.create("div");return t.innerHTML=this.getHtml(e),this.getParam("toolbar")&&m(c(t,".toolbar"),"click",B.toolbar.handler),this.getParam("quick-code")&&m(c(t,".code"),"dblclick",X),t},init:function(e){this.id=f(),u(this),this.params=p(B.defaults,e||{}),1==this.getParam("light")&&(this.params.toolbar=this.params.gutter=!1)},getKeywords:function(e){return e=e.replace(/^\s+|\s+$/g,"").replace(/\s+/g,"|"),"\\b(?:"+e+")\\b"},forHtmlScript:function(e){var t={end:e.right.source};e.eof&&(t.end="(?:(?:"+t.end+")|$)"),this.htmlScript={left:{regex:e.left,css:"script"},right:{regex:e.right,css:"script"},code:XRegExp("(?"+e.left.source+")"+"(?.*?)"+"(?"+t.end+")","sgi")}}},B}();"undefined"!=typeof exports?exports.SyntaxHighlighter=SyntaxHighlighter:null