WordPressでデータベースを使う

WordPressはそもそもMySQLを利用しているシステムなので当然かもしれませんが、データベースへのアクセスに便利なクラス$wpdbが用意されています。  

投稿、または固定ページの一つ(postIDが一番ちいさいもの)を取得します。

$query = "SELECT * FROM $wpdb->posts";
	$select_result = $wpdb->get_row($query);
	echo "<p>$select_result->ID : $select_result->post_title</p>";

  $wpdb は関数内で使うときは、globalで利用できるようにします。

function myFirstPosts(){
	global $wpdb;
	$query = "SELECT * FROM $wpdb->posts";
	$select_result = $wpdb->get_row($query);
	echo "<p>$select_result->ID : $select_result->post_title</p>";
}
myFirstPosts();

  get_row の代わりに、get_results をつかうと複数行を取り出すことが出来ます。
というか、こっちが普通のSELECTですね。

function myAllPosts(){
	global $wpdb;
	$query = "SELECT * FROM $wpdb->posts";
	$select_result = $wpdb->get_results($query);
	foreach ($select_result as $row){
		echo "<p>$row->ID : $row->post_title</p>";
	}
}
myAllPosts();

  独自のテーブルを作成するときは、$wpdb->query を使ってCREATE文できますが、dbDelta を使ったほうがいいです。
$wpdb->prefix でWPのプレフィックスを取得できます。

function createMyCustamTable($tableName){
	global $wpdb;
	$sql = "CREATE TABLE " . $wpdb->prefix . $tableName . " (
	id bigint(20) unsigned  PRIMARY KEY  AUTO_INCREMENT,
	name varchar(250)
	) " . $wpdb->get_charset_collate() . ";";
	dbDelta($sql);
}
createMyCustamTable("testTable");

ただし、dbDeltaを利用する場合、

・1行につき1つのフィールドを定義する。
・PRIMARY KEYと主キーの定義の間には二つのスペースが必要。
・INDEXという言葉ではなく、KEYという言葉を使う必要がある。

という制約があるので、これらを知らずにつかおうとすると、SQL文そのものがMySQL的にバッチリでもCREATEできなくて、ハマります。  

独自のテーブルからSELECTする場合。

function myAllCustamTable($tableName){
	global $wpdb;
	$query = "SELECT * FROM " . $wpdb->prefix . $tableName;
	$select_result = $wpdb->get_results($query);
	foreach ($select_result as $row){
		echo "<p>$row->id : $row->name</p>";
	}
}
myAllCustamTable("testTable");

 

DELETEなど、データを取得しないクエリの場合、$wpdb->query を使う。(ただし、普通に削除する場合は後述の$wpdb->deleteがあるので、そっちを使う)

function deleteMyCustamTable($tableName , $id){
	global $wpdb;
	$query = "DELETE FROM " . $wpdb->prefix . $tableName . " WHERE id=$id";
	$wpdb->query($query);
}
deleteMyCustamTable("testTable" , 1);

 

行を追加する場合も、$wpdb->queryが使えますが、ユーザーの入力内容をSQL文に入れる場合など、SQLインジェクション攻撃対策が必要な場合は、$wpdb->prepareを使う。

function insertMyCustamTable($tableName , $id , $name){
	global $wpdb;
	$query = "INSERT INTO
	INSERT INTO " . $wpdb->prefix . $tableName
	(id, name)
	VALUES (%d , %s)";
	$wpdb->prepare($query , $id , $name);
}
insertMyCustamTable("testTable" , 1 , "TEST1");

 

行を追加するには、$wpdb->insertを使うと便利な上に、SQLインジェクション攻撃対策も同時になされるので安心。

$wpdb->insert('testTable', array( 'id' => 2, 'name' => 'TEST2' ));

 

更新も$wpdb->updateが用意されています。

$wpdb->update('testTable', array( 'id' => 2, 'name' => 'TEST' ));

 

行を削除するには、$wpdb->deleteを使います。

$wpdb->delete( $wpdb->posts, array( 'ID' => 2 ) );

 

取得したデータのカラム名など調べたければ、SELECTなどのあとに $wpdb->get_col_info で調べられます。

	$query = "SELECT * FROM $wpdb->posts";
	$select_result = $wpdb->get_results($query);
	$colInfo = $wpdb->get_col_info('type', offset);
	foreach ($colInfo as $col){
		echo "<p>$col</p>";
	}

 

SELECTするまえにカラム名を調べたければ、wpdb->get_col で取得したほうが手っ取り早い。

	$query = "DESC $wpdb->posts";
	$colInfo = $wpdb->get_col($query);
	foreach ($colInfo as $col){
		echo "<p>$col</p>";
	}

ちなみに、DESC は、DESCRIBE と同じ。(MySQL DESCRIBE)  

$wpdbのメソッド一覧 https://developer.wordpress.org/reference/files/wp-includes/wp-db.php/  
$wpdbの中身を見てみますと。。。

object(wpdb)[1]
  public 'show_errors' => boolean false
  public 'suppress_errors' => boolean false
  public 'last_error' => string '' (length=0)
  public 'num_queries' => int 21
  public 'num_rows' => int 0
  public 'rows_affected' => int 0
  public 'insert_id' => int 0
  public 'last_query' => string 'SELECT t.*, tt.* FROM wp_terms AS t INNER JOIN wp_term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = 'nav_menu' AND t.name = 'メインメニュー' LIMIT 1' (length=175)
  public 'last_result' => 
    array (size=0)
      empty
  protected 'result' => resource(131, mysql result)
  protected 'col_meta' => 
    array (size=1)
      'wp_terms' => 
        array (size=4)
          'term_id' => 
            object(stdClass)[2267]
              ...
          'name' => 
            object(stdClass)[2268]
              ...
          'slug' => 
            object(stdClass)[2262]
              ...
          'term_group' => 
            object(stdClass)[2406]
              ...
  protected 'table_charset' => 
    array (size=1)
      'wp_terms' => string 'utf8' (length=4)
  protected 'check_current_query' => boolean true
  private 'checking_collation' => boolean false
  protected 'col_info' => null
  public 'queries' => null
  protected 'reconnect_retries' => int 5
  public 'prefix' => string 'wp_' (length=5)
  public 'base_prefix' => string 'wp_' (length=5)
  public 'ready' => boolean true
  public 'blogid' => int 0
  public 'siteid' => int 0
  public 'tables' => 
    array (size=9)
      0 => string 'posts' (length=5)
      1 => string 'comments' (length=8)
      2 => string 'links' (length=5)
      3 => string 'options' (length=7)
      4 => string 'postmeta' (length=8)
      5 => string 'terms' (length=5)
      6 => string 'term_taxonomy' (length=13)
      7 => string 'term_relationships' (length=18)
      8 => string 'commentmeta' (length=11)
  public 'old_tables' => 
    array (size=3)
      0 => string 'categories' (length=10)
      1 => string 'post2cat' (length=8)
      2 => string 'link2cat' (length=8)
  public 'global_tables' => 
    array (size=2)
      0 => string 'users' (length=5)
      1 => string 'usermeta' (length=8)
  public 'ms_global_tables' => 
    array (size=7)
      0 => string 'blogs' (length=5)
      1 => string 'signups' (length=7)
      2 => string 'site' (length=4)
      3 => string 'sitemeta' (length=8)
      4 => string 'sitecategories' (length=14)
      5 => string 'registration_log' (length=16)
      6 => string 'blog_versions' (length=13)
  public 'comments' => string 'wp_comments' (length=13)
  public 'commentmeta' => string 'wp_commentmeta' (length=16)
  public 'links' => string 'wp_links' (length=10)
  public 'options' => string 'wp_options' (length=12)
  public 'postmeta' => string 'wp_postmeta' (length=13)
  public 'posts' => string 'wp_posts' (length=10)
  public 'terms' => string 'wp_terms' (length=10)
  public 'term_relationships' => string 'wp_term_relationships' (length=23)
  public 'term_taxonomy' => string 'wp_term_taxonomy' (length=18)
  public 'usermeta' => string 'wp_usermeta' (length=13)
  public 'users' => string 'wp_users' (length=10)
  public 'blogs' => null
  public 'blog_versions' => null
  public 'registration_log' => null
  public 'signups' => null
  public 'site' => null
  public 'sitecategories' => null
  public 'sitemeta' => null
  public 'field_types' => 
    array (size=34)
      'post_author' => string '%d' (length=2)
      'post_parent' => string '%d' (length=2)
      'menu_order' => string '%d' (length=2)
      'term_id' => string '%d' (length=2)
      'term_group' => string '%d' (length=2)
      'term_taxonomy_id' => string '%d' (length=2)
      'parent' => string '%d' (length=2)
      'count' => string '%d' (length=2)
      'object_id' => string '%d' (length=2)
      'term_order' => string '%d' (length=2)
      'ID' => string '%d' (length=2)
      'comment_ID' => string '%d' (length=2)
      'comment_post_ID' => string '%d' (length=2)
      'comment_parent' => string '%d' (length=2)
      'user_id' => string '%d' (length=2)
      'link_id' => string '%d' (length=2)
      'link_owner' => string '%d' (length=2)
      'link_rating' => string '%d' (length=2)
      'option_id' => string '%d' (length=2)
      'blog_id' => string '%d' (length=2)
      'meta_id' => string '%d' (length=2)
      'post_id' => string '%d' (length=2)
      'user_status' => string '%d' (length=2)
      'umeta_id' => string '%d' (length=2)
      'comment_karma' => string '%d' (length=2)
      'comment_count' => string '%d' (length=2)
      'active' => string '%d' (length=2)
      'cat_id' => string '%d' (length=2)
      'deleted' => string '%d' (length=2)
      'lang_id' => string '%d' (length=2)
      'mature' => string '%d' (length=2)
      'public' => string '%d' (length=2)
      'site_id' => string '%d' (length=2)
      'spam' => string '%d' (length=2)
  public 'charset' => string 'utf8' (length=4)
  public 'collate' => string '' (length=0)
  protected 'dbuser' => string 'root' (length=4)
  protected 'dbpassword' => string 'password' (length=12)
  protected 'dbname' => string 'wordpress' (length=9)
  protected 'dbhost' => string 'host' (length=2)
  protected 'dbh' => resource(21, mysql link)
  public 'func_call' => string '$db->query("SELECT t.*, tt.* FROM wp_terms AS t INNER JOIN wp_term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = 'nav_menu' AND t.name = 'メインメニュー' LIMIT 1")' (length=189)
  public 'is_mysql' => boolean true
  protected 'incompatible_modes' => 
    array (size=5)
      0 => string 'NO_ZERO_DATE' (length=12)
      1 => string 'ONLY_FULL_GROUP_BY' (length=18)
      2 => string 'STRICT_TRANS_TABLES' (length=19)
      3 => string 'STRICT_ALL_TABLES' (length=17)
      4 => string 'TRADITIONAL' (length=11)
  private 'use_mysqli' => boolean false
  private 'has_connected' => boolean true
  public 'categories' => string 'wp_categories' (length=15)
  public 'post2cat' => string 'wp_post2cat' (length=13)
  public 'link2cat' => string 'wp_link2cat' (length=13)

モバイルフレンドリーテスト

Googleは、モバイル端末(スマートフォンなどポケットサイズの端末)でのウェブサイトの可読性を評価するようになっています。

つまり、モバイル端末で閲覧する人が見難い、と感じるサイトは、モバイル端末での検索順位を下げる可能性がある、という意味です。
https://developers.google.com/webmasters/mobile-sites/mobile-seo/?hl=ja

で、その評価は、Googleが提供するモバイルフレンドリーテストでチェックすることが出来ます。

https://www.google.com/webmasters/tools/mobile-friendly/
mobilefriendrytest  

とあるサイトをチェックすると、こんなことを言われてしまいました。
no-mobilefriendry

ページがモバイル フレンドリーではないと判断される可能性のある理由
×テキストが小さすぎて読めません
×モバイル用 viewport が設定されていません
×リンク同士が近すぎます

でも、そのサイトはCSSで画面サイズに併せて2カラムから1カラムに表示を変えるようになっていて、実際にiPhoneでチェックしても問題ありません。
なのに、Googleのモバイルフレンドリーテストでは合格できません。

で、いろいろ調べてみたら、headタグ内にmetaタグでviewportを追加すれば解決するとわかりました。
具体的には、

<meta name="viewport" content="width=device-width,initial-scale=1.0">

をheadタグ内に追加する。

参考サイト: これがスマートフォン向けサイトを作るときの viewport 設定3パターンだ

「ます」だけ四角に文字化け?

bonesという骨組みだけのテーマをとりあえず入れただけのこのサイト。

http://themble.com/bones/

ふときがつくと、「ます」だけが〼と表示されることに気が付きました。
ん?文字化けか? と文字エンコードを疑ったりしていたのですが、CSSを見てるうちにわかりました。

-webkit-font-feature-settings: "liga", "dlig";
-moz-font-feature-settings: "liga=1, dlig=1";
-ms-font-feature-settings: "liga", "dlig";
-o-font-feature-settings: "liga", "dlig";
font-feature-settings: "liga", "dlig";

font-feature-settings お前か!

とりあえず、コメントアウトで対処できました。

ちなみに、font-feature-settingsについて参考になったサイトさん。
http://www.riaxdnp.jp/?p=5094

JIS記号とか表示できて楽しげです。

JIS ます 株式会社 センチ cm2 am/pm

せっかくなのでメモ。

日付入力を簡単にする

http://kwski.net/jquery/1041/ http://blog.hrendoh.com/how-to-use-bootstrap-datepicker/#.VT8fh_m_vcc1 http://opensource-customize.info/wordpress/43/

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

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

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