投稿をゴミ箱に入れるときと、完全に削除したときのフック

WordPressで投稿をゴミ箱に入れた時のフック。

add_action('publish_to_trash', 'my_trash_post');
add_action('draft_to_trash',   ' my_trash_post');
add_action('future_to_trash',  'my_trash_post');

function my_trash_post($postObj) {
      $postID = $postObj->ID;
}

さらに、ゴミ箱から完全に削除した時。

add_action( 'before_delete_post', 'my_delete_post' );

function my_delete_post($postID) {

}

before_delete_post のときは、PostIDで受け取りますが、 publish_to_trash、draft_to_trash、future_to_trash のときは、オブジェクトで受け取るので、注意が必要です。

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なりに入れておけば、バッチリです!

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

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() ) {

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

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 のときは、きちらをどうぞ。

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

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';
と同じ意味。のはず。