All In One SEOを利用時に、とあるページだけcanonicalを出力させたくない

プラグインの All In One SEO では、canonical が設定できますが、
任意のページで ON/OFF 出来ないので、なんとかならないかと、

remove_action('wp_head', 'All_in_One_SEO_Pack::wp_head');

とか試してみたのですが、どうにもならず悶々と調べておりましたところ、こんなやりとりを発見!
https://wordpress.org/support/topic/specify-canonical-url

なるほど。
All In One SEO には、フィルターフックに aioseop_canonical_url が用意されているようです。
では、All In One SEO のソースコードを見てみると、 aioseop_class.php にある、canonical を出力する部分には...

$url = apply_filters( 'aioseop_canonical_url', $url );
	if ( !empty( $url ) )
		echo '<link rel="canonical" href="'. esc_url( $url ) . '" />'."\n";

となっています。

if ( !empty( $url ) )  なので、add_filter で $url に null をかえすようにすれば、canonical のmetaタグは出力されないわけです。
とうことで、たとえば、postID が 306 の固定ページで、canonicalを表示しないようにしたければ、

add_filter( 'aioseop_canonical_url', 'no_canonical_url');
function no_canonical_url($url){
	global $wp_query;
	$postID = $wp_query->post->ID;
	if ($postID=='306'){
		return false;
	}else{
		return $url;
	}
}

というのを、function.phpなりに入れておけば、バッチリです!

0

WordPressで、メインクエリの情報を取得する。

get_queried_object()を使うと、 フックの中でメインクエリのpost_typeを知りたい時など、メインクエリの情報を捕まえることが出来ます。

$obj_query = get_queried_object();
echo $obj_query->name;

戻り値は、ページによって違います。 コチラが参考になります。
http://elearn.jp/wpman/function/get_queried_object.html http://notnil-creative.com/blog/archives/2259

0

WordPress Popular Posts を利用中に、カテゴリページを人気順で並べ替え

WordPress Popular Posts プラグインを使っている時に、 カテゴリーページで人気順に並べ替えたいときに。

カスタムクエリなるフックを使う。
http://wpdocs.sourceforge.jp/%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%82%AF%E3%82%A8%E3%83%AA

クエリするときに発行されるSQLの、JOIN句、ORDER BY句なんかを書き換えることができます。
それを利用して、カテゴリページの一覧を人気順にします。

WordPress Popular Posts のデータをJOINで連結して、ORDER BY句で並べ替えます。
下の例では、念の為に、ID(post ID)でグルーピングしています。

//親カテゴリをチェック
function checkParent($query=null)
{
	if (!$query) {  
		global $wp_query;
		$query = $wp_query;
	}
	$slug = $query->query_vars['category_name'];
	$cat = get_category_by_slug($slug);
	return $cat->parent;
}

//子カテゴリのときは、人気順に並べ替えて表示する。
add_filter('posts_join', 'child_category_pps_join' , 10, 2);
add_filter('posts_orderby', 'child_category_pps_order', 10, 2 );
add_filter('posts_groupby', 'child_category_pps_groupby', 10, 2 );
function child_category_pps_join( $join , $query)
{
	if ( is_admin() || ! $query->is_main_query() )
		return $join;

	if ( $query->is_category() && (checkParent($query) > 0)) {
		global $wpdb;
		$pps_table = $wpdb->prefix . "popularpostssummary";
		$join .= " LEFT JOIN " . $wpdb->prefix . "popularpostssummary ON " . 
		$wpdb->posts . ".ID = " . $pps_table . ".postid ";
	}
	return $join;
}

function child_category_pps_order( $orderby , $query )
{
	if ( is_admin() || ! $query->is_main_query() )
		return $orderby;

	global $wpdb;
	if ( $query->is_category() && (checkParent($query) > 0)) {
			$pps_table = $wpdb->prefix . "popularpostssummary";
			return $pps_table . ".pageviews DESC";
	}
	return $orderby;
}

function child_category_pps_groupby( $groupby , $query )
{
	if ( is_admin() || ! $query->is_main_query() )
		return $groupby;

	if ( $query->is_category() && (checkParent($query) > 0)) {
		global $wpdb;
		$pps_table = $wpdb->prefix . "popularpostssummary";
		$slug = $query->query_vars['category_name'];
		$cat = get_category_by_slug($slug);
		$mygroupby = "{$wpdb->posts}.ID";
		if( preg_match( "/$mygroupby/", $groupby )) {
			return $groupby;
		}
		if( !strlen(trim($groupby))) {
			return $mygroupby;
		}
		return $groupby . ", " . $mygroupby;
	}
	return  $groupby;
}

checkParent() で、表示中のカテゴリページに親ページがあるかどうかをチェックしています。
そして、子カテゴリのときだけ、人気順に表示するようにしていますので、 そうでないのなら、3箇所ある、

if ( $query->is_category() && (checkParent($query) > 0)) {

のところを、

if ( $query->is_category() ) {

にすれば、すべてのカテゴリページで人気順になります。

0

pre_get_posts 中にカテゴリID とSlugを取得する

カテゴリページを表示するときに順番を変えていただきたい。

というときに、 表示中のカテゴリIDとスラッグ取得するのにあたふたしたのでメモ スラッグは、

$slug = $query->query_vars['category_name']; で手に入る。
$cat = get_category_by_slug($slug); でカテゴリ情報を取得。
if ($cat->parent > 0) で親カテゴリがあるかどうかをチェック。

以下、そんな例です。

function my_pre_get_posts($query){

	//カテゴリページでは人気順に表示する
	if ( $query->is_category() && $query->is_main_query()) {
		$slug = $query->query_vars['category_name'];
		$cat = get_category_by_slug($slug);
		if ($cat->parent > 0) {
			$query->set('orderby', 'meta_value_num' );
			$query->set('meta_key', 'views' );
			$query->set('order', 'DESC' );
		}
	}

}
add_action( 'pre_get_posts', 'my_pre_get_posts' );

ちなみに、上記の例は、人気ページランキングを表示するウィジェットプラグイン WP-PostViews を使った時に使えます。

WordPress Popular Posts のときは、きちらをどうぞ。

0

query_posts と pre_get_posts と WP_Query

WordPressをいじりはじめたころ、「query_posts」で検索すると、

query_postsを捨てよ、pre_get_postsを使おう

というのが見えて、え!?っとなって、 query_postsのページには、

「ひとことで言うと、query_posts() は決して使うべきではありません。」

とまで書いてあるし。

それじゃ、ということで、 query_posts の代わりに pre_get_posts を使えばいいんですな、と調べたら、必ずしも置き換えられるものではない、としってなんじゃそりゃ。となったり。

結論から言うと、query_posts の置き換えは、get_post か、WP_Query を使おう。となるのですが、 じゃ、get_post と、WP_Query どう使い分けるの?とか、 pre_get_posts はなんに使うの?とか、 query_postsと、get_post、WP_Query は記述の仕方が似て非なるもので、覚え直すのが面倒。とか思うわけです。
特にノンプログラマの人には混乱著しいのではと思うのです。 そこで、そこのところをまとめ。

メインクエリの存在を知る

そもそもWordPressが固定ページや投稿のデータをどのタイミングでとってきているか、ということを頭にいれとかないと使い分けできません。
たとえば、とあるカテゴリページを表示させるとします。 すると、はじめにデータベースから「とあるカテゴリ」に該当する投稿を取得して、それをメインクエリとして保管します。

このメインクエリに保管された、投稿のタイトル、本文、日付なんかを、取得した順番に表示させることでカテゴリページが表示されます。

メインクエリを、上書きするのか、しないのか

さて、その上で category.php の中に、query_posts を書き込むとどうなるかというと、はじめに保管したメインクエリをあたらしい条件で書き換える、ということになります。

書き換えてしまう以上、「とあるカテゴリ」の一覧だったメインクエリは消えてしまいます。
カテゴリページにquery_postsを使って、「おすすめ記事」を3件とか表示させたとすると、メインクエリは「とあるカテゴリ」ではなく、「おすすめ記事」に書き換わってしまう。

その後でページングの処理などにメインクエリが利用された場合、「おすすめ記事」の一覧の内容で処理されて、へんなことになっちゃうぞ! というのが、query_postsを使うな、WP_Queryやget_postsを使え、という理由なわけです。

とかいいつつも、wp_reset_query を使えば、もとのメインクエリ「とあるカテゴリ」に戻すことはできるので、query_posts を使った後にはwp_reset_query で後始末することを徹底すれば問題ないのでは。 とも思っていたのですが、他のプラグインの影響を受けやすいとか、トラブルのたねになりかね無いようなので、やっぱり基本は使わないほうが良さそう。
そもそも、query_posts でやりたいことは、WP_Queryで出来るわけですから。

メインクエリそのものを変えたいとき

以上は、メインクエリとは別の一覧を表示させたいときなんかの場合でしたが、そもそも、カテゴリページの並び方を変えてしまいたい、と考えたとします。
そうすると、query_posts はメインクエリを書き換えますから、願ったり叶ったりとおもうのですが、そうとも言えないようです。

たとえば、カテゴリページを表示しようとするとき、
・メインクエリを取得するために、データベースにアクセスします。(1回目)
・そのあとで、query_postsメソッドで、再度データベースにアクセスします。(2回目)

と、2回データベースにアクセスすることになります。(しかも1回目はムダ!)
だったら、1回目の時に順番を変えておけばいいじゃない。とうのが、pre_get_postsフックです。

pre_get_posts を使えば、1回目のメインクエリ条件そのものを変更することが出来るので、query_posts ではなく、pre_get_posts を使うべきだ。というわけです。
また、通常 pre_get_posts は function.php に書き込むので、query_postsのように、テンプレートごとにコードを書かなくてもいい、というメリットもあります。

ということで、基本的に WP_Query クラス pre_get_posts フック この二つを使えば、やりたいことは出来そうことでOKではないでしょうか。

で、 get_posts メソッド(投稿用) get_pages メソッド(固定ページ用) は、というと、WP_Query の機能限定版と思ってます。
というのも、(query_postsもそうですが)get_postsも、get_pagesも、内部で WP_Query 呼び出してるし、別々に使い方覚えなくてもいいんじゃまいか。 こちらの記事がとても参考になりました。

http://wpxtreme.jp/understanding-wordpress-loops-and-queries

0

MySQL の DELETE で JOIN を使う方法

WordPressでカスタム投稿の投稿をまとめて削除しようと思って、postsテーブル のデータ削除は簡単なんだけれど、 postmetaテーブルのデータ削除がああでもない、こーでもないと格闘。 初めは、こうしたけれどダメ。
DELETE FROM tn_postmeta pm 
LEFT JOIN tn_posts p ON p.ID=pm.post_id 
WHERE p.post_type='postType';
どうやら、DELETE と FROM の間にテーブル名を入れないといけないらしい、ということで... こうしたけれど、これもだめ。
DELETE tn_postmeta FROM tn_postmeta pm 
LEFT JOIN tn_posts p ON p.ID=pm.post_id 
WHERE p.post_type='postType';
どうやら、DELETE と FROM の間のテーブル名には、エイリアス名を入れないといけないということで、 エイリアス名に変えて、これでOKでした。
DELETE pm FROM tn_postmeta pm 
LEFT JOIN tn_posts p ON p.ID=pm.post_id 
WHERE p.post_type='postType';
ちなみに、エイリアス名のAS句は省略できるということなので、
DELETE pm FROM tn_postmeta AS pm 
LEFT JOIN tn_posts AS p ON p.ID=pm.post_id 
WHERE p.post_type='postType';
と同じ意味。のはず。
0

Google XML Sitemaps に独自のページを追加する方法

Google XML Sitemaps 利用時に、WordPressに依存しない、独自に用意したURLを追加する方法です。
Google XML Sitemapsには、sm_buildmap というアクションフックが用意されているのでそれを利用します。

add_action('sm_buildmap', array($this,'addSitemapGXS'));

function addSitemapGXS(){
	$generatorObject = &GoogleSitemapGenerator::GetInstance();
	if ( $generatorObject!=null ) {
		$generatorObject->AddUrl("http://hogehoge.com/test.html", "2015-04-13", "daily", 0.5);
	}
  }
}


AddUrl メソッドで指定された情報が、sitemap-misc.xml に追加されます。

0

Breadcrumb NavXT で独自の項目を追加する

独自のページが有る場合、 Breadcrumb NavXT 導入時に、パンくずリストにそのページを追加する方法を調べました。

Breadcrumb NavXT には、アクションフックが二つ用意されています。

add_action('bcn_before_fill', 'my_filter_breadcrumbs_before');
add_action('bcn_after_fill', 'my_filter_breadcrumbs_after');

bcn_before_fill は、パンくずリスト生成前、 bcn_after_fillは、パンくずリスト生成後のアクションフックです。
パンくずリストを追加することを考えますので、bcn_after_fillを使います。

以下の例は、とあるページ(投稿ページ、固定ページなど)にitemnameというクエリパラメータがついていたら、そのページ下層の独自ページとする。
という前提です。(例 http://test.com/?p=123&itemname=あいうえお)

add_action('bcn_after_fill', 'my_filter_breadcrumbs_after');
function my_filter_breadcrumbs_after($bcnObj) {
	if ( count($bcnObj->trail) > 0 ) {
		$itemname = htmlspecialchars($_GET["itemname"]);
		if ($itemname){
			//独自ページのパンくず生成
			$trail_add[0] = clone $bcnObj->trail[0];//クローンを用意 ※1
			$trail_add[0]->set_title($itemname);//タイトルを設定 ※2
			if($bcnObj->opt['bcurrent_item_linked']){// ※3
				$query = array('itemname' => $itemname);
				$seturl = add_query_arg( $query , get_permalink()) ;
				$trail_add[0]-> set_url($seturl);// ※4
			}else{
				//親ページをリンク付きに変更
				$bcnObj->trail[0]->set_url(get_permalink());//URLを設定 ※5
				$bcnObj->trail[0]->set_template($bcnObj->opt['Hpost_post_template']);//テンプレートを設定 ※6
			}
			$bcnObj->trail = array_merge ($trail_add, $bcnObj->trail);//ここでマージ ※7
		}
	}
	return $bcnObj;
}


フックで受け取った変数$bcnObjにパンくずの情報が入っています。
$bcnObj->opt にBreadcrumb NavXTの設定ページした情報が入っています。
$bcnObj->trail に表示するパンくずが配列で入っています。

$bcnObj->trail[0] が一番右側で、 ホーム > 親ページ > 投稿ページ のようなパンくずリストの場合、 $bcnObj->trail[2] > $bcnObj->trail[1] > $bcnObj->trail[0] という感じになっています。
そこで、一番右にパンくずを追加したいのなら、[0]のところにもう一つ挿入すればいいということになります。

ホーム > 親ページ > 投稿ページ > あいうえお

$bcnObj->trail[3] > $bcnObj->trail[2] > $bcnObj->trail[1] > $bcnObj->trail[0]

なので、$bcnObj->trail[0] からクローンを取り出して、(※1) タイトルを設定して、(※2 例ではクエリパラメータをそのまま突っ込んでます。) 内容を書き換えて、マージするようにしてみました。(※7)

設定で、「現在のページを示す項目にリンクを張る」がONの場合(※3)は、独自ページのところにもリンクを追加したいので、set_urlメソッドで独自ページのリンクを追加します。(※4)

その一方、「現在のページを示す項目にリンクを張る」がOFFの場合は、本来の現在のページにはリンクがついていないので追加します。(※5)

set_template関数でパンくずのテンプレートを設定しますが、$bcnObj->opt['Hpost_post_template'] はBreadcrumb NavXTの設定にある、投稿タイプの投稿テンプレートです。(※6) (その他のテンプレートは、プラグインのclass.bcn_breadcrumb_trail.php にありますが、設定ページのソースコード(HTML)を覗いても大体わかります。)

参考: http://notnil-creative.com/blog/archives/981 http://tenman.info/labo/snip/archives/3761

 

1+

the_titleフィルターフックをつかうと、関係のないメニュータイトルなんかも変わってしまうのを避ける方法

WordPressでタイトルを加工するときなんかに、フィルターフックの the_title を使うわけですが、 目的のページのタイトル以外のメニューまで変わってしまって、ぷちはまってしまったのでメモ。

add_filter( 'the_title', 'modify_single_post_entry_titles' );
function modify_single_post_entry_titles( $title ) {
    if ( is_singular() && in_the_loop() ) {
        /* Modify $title */
    }
    return $title;
}

is_singular() と、 in_the_loop() を入れておくのがポイントですね。

参考:https://codex.wordpress.org/Function_Reference/in_the_loop

0