planet-green.com

[開発/備忘録] WordPressのテーブルを手動でutf8mb4に一括変換する



現在、WordPressを新規でインストールするかv4.2以前から最新版にアップデートすると、DBの各テーブルの文字コードが自動的にutf8mb4_general_ciに設定されます。
(ただし、DBがMySQLの5.5.3以上かMariaDBの10以上だった場合のみ)

しかし、DBのバージョンが古かったサーバーでWordPressを運用していて既にv4.3以降にアップデートした後、新しいサーバーにデータごと引っ越した場合は、文字コードがutf8_general_ciのままになってしまいます。(当サイトがそうでした。)

そんな時は、まず wp-config.php の DB_CHARSET を utf8mb4 にして、

define('DB_CHARSET', 'utf8mb4');

下記のコードをfunction.phpに貼り付け、 http://(サイトのURL)/?upgradeUtf8mb4=1 をブラウザで呼び出せば utf8mb4_general_ci に一括返還してくれます。
(v4.7.3で動作確認はしましたが、必ず実行前にバックアップしてください)

if( isset($_GET["upgradeUtf8mb4"]) && $_GET["upgradeUtf8mb4"==1 )
{
        require_once ABSPATH."wp-admin/includes/upgrade.php";

        if ( is_multisite() ) {
                $tables = $wpdb->tables( 'blog' );
        } else {
                $tables = $wpdb->tables( 'all' );
                if ( ! wp_should_upgrade_global_tables() ) {
                        $global_tables = $wpdb->tables( 'global' );
                        $tables = array_diff_assoc( $tables, $global_tables );
                }
        }

        foreach ( $tables as $table ) {
                maybe_convert_table_to_utf8mb4( $table );
        }

        echo "done.";
        exit;
}

実行後、テーブルが変換されているのを確認したらこのコードは削除してください。

ちなみに utf8mb4_general_ci は何が違うのかと言うと、絵文字を直接保存できるようになるのです。
(逆に言うと、絵文字を使わないのであればテーブルを変換する必要は無いです)

コメント
planet-green.com

[開発/備忘録] WordPressでSchema.org用JSON-LDを出力(複数画像対応)



WordPressで構造化データ(schema.org用)のJSON-LDを<head>内に出力する関数です。function.phpに追記してください。

ネットで調べてみると似たようなコードはいくつかありましたが、複数の添付画像・複数のカテゴリーに対応したものが無かったので自作しました。

function insert_json_ld_for_schema_org()
{
	if( !is_single() || !have_posts() ) {
		return;
	}
	
  //あなたのサイトのロゴ画像をここで設定してください
  $logo = array(
		'@type' => 'ImageObject',
		'url' => 'http://*****/****.jpg',
		'width' => '240',
		'height' => '80'
	);
	
	while (have_posts()) {
		the_post();
		$category_info = get_the_category();
		if( count($category_info) > 1 ) {
			$articleSection = array();
			foreach( $category_info as $ct )
			{
				$articleSection[] = $ct->name;
			}
		} else 	{
			$articleSection = $category_info[0]->name;
		}
		
		//添付画像を取得
		$attachments = get_children( array( 'post_parent' => get_the_ID(), 'post_type' => 'attachment', 'post_mime_type' => 'image') );
		
		$images = array();
		foreach($attachments as $image) {
			$src = wp_get_attachment_image_src( $image->ID, 'medium' );
			$images[] = array(
				'@type' => 'ImageObject',
				'url' => $src[0],
				'width' => strval($src[1]),
				'height' => strval($src[2])
			);
		}
		
		//添付画像が無い場合はアイキャッチ画像を取得
		if( !count($images) ) {
			if( has_post_thumbnail() ) {
				$thumbnail_id = get_post_thumbnail_id(); 
				$src = wp_get_attachment_image_src( $thumbnail_id , 'medium' );
				$images = array(
                                	'@type' => 'ImageObject',
                                	'url' => $src[0],
                                	'width' => strval($src[1]),
                                	'height' => strval($src[2])
				);
			} else {
				//それも無い場合はロゴ画像をセット
				$images = $logo;
			}
		}
		$data = array(
			'@context' => 'http://schema.org',
			'@type' => 'Article',
			'headline' => get_the_title(),
			'author' => array(
					'@type' => 'Person',
					'name' => get_the_author(),
			),
			'datePublished' => get_the_date('Y-m-d'),
			'dateModified' => get_the_modified_time( 'Y-m-d' ),
			'articleSection' => $articleSection,
			'url' => get_permalink(),
			'mainEntityOfPage' => array(
				'@type' => 'WebPage',
				'@id' => get_permalink()
			),
			'publisher' => array(
				'@type' => 'Organization',
				'name' => get_bloginfo('name'),
				'logo' => $logo,
			),									
			'image' => $images,
		);
		
		//php5.4以前のバージョンではオプション引数(JSON_UNESCAPED〜)を削除すれば大丈夫だと思います。
		echo '<script type="application/ld+json">'
			.json_encode($data, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT)
			.'</script>'.PHP_EOL;
	}
	rewind_posts();
}
add_action('wp_head','insert_json_ld_for_schema_org');

出力サンプル。

<script type="application/ld+json">{
    "@context": "http://schema.org",
    "@type": "Article",
    "headline": "[Photo] 幌見峠のラベンダー園",
    "author": {
        "@type": "Person",
        "name": "tomoya"
    },
    "datePublished": "2017-04-08",
    "dateModified": "2017-04-08",
    "articleSection": "Photo",
    "url": "https://planet-green.com/horomitouge-lavender/1501",
    "mainEntityOfPage": {
        "@type": "WebPage",
        "@id": "https://planet-green.com/horomitouge-lavender/1501"
    },
    "publisher": {
        "@type": "Organization",
        "name": "planet-green.com",
        "logo": {
            "@type": "ImageObject",
            "url": "https://planet-green.com/wp-content/uploads/2017/03/Twenty-Seventeen-capture-2017-03-28-.jpg",
            "width": "640",
            "height": "573"
        }
    },
    "image": [
        {
            "@type": "ImageObject",
            "url": "https://planet-green.com/wp-content/uploads/2017/04/horomitouge-7399-720x480.jpg",
            "width": "720",
            "height": "480"
        },
        {
            "@type": "ImageObject",
            "url": "https://planet-green.com/wp-content/uploads/2017/04/horomitouge-7418-720x480.jpg",
            "width": "720",
            "height": "480"
        },
        (略)
    ]
}</script>

そのSchema.orgなんですが、仕様についてネット上の情報が錯綜している上に、公式サイトの説明も曖昧で、いまいちよくわからない部分が多いのですよね。

例えば、記事タイトルはnameプロパティで指定すると書いてあるサイトもあれば、headlineプロパティで指定すると書いてあるサイトもあります。
Schema.org公式サイトでは簡素にnameはname、headlineはheadlineと書いてありますが言葉の定義についての説明が無いのです。

しかもgoogleまで錯綜していて、Search Console(webmasterツール)の 〔その他のリソース〕 にある 〔構造化データ マークアップ支援ツール〕 を使って画像タグのマークアップをしても、〔構造化データ テスト ツール〕を使ってチェックするとエラーになるという状態です。

そこで上記のPHP関数は、私が調べた範囲において最大公約数的な解釈をして実装しました。
間違いがありましたらコメントでご指摘していただけると幸いです。

コメント
planet-green.com

[開発/備忘録] 個人でSSL証明書を取得してHTTPS化してみた話



個人でSSL証明書を取得して当サイトをHTTPS化してみました。
証明書はGeoTrust社のRapid SSLです。

実はその少し前に無料のLet’s Encryptを試しに入れていたのですが、次章で書く懸念事項があり、Rapid SSLが3年間3,200円(税別)と安かったので購入することにしました。ちなみにニジモさんで購入しました。

Let’s Encryptについて

無料なのは大変ありがたいです。理念も素晴らしいと思います。

証明書を取得・更新するためには、一時的にWEBサーバー(Apacheやnginxなど)を停止し、専用のスクリプトを走らせる必要があります。

このスクリプトはサーバーの80ポートまたは443(SSL)ポートを独占して認証サーバーのAPIにアクセスし証明書を取得するようです。
(おそらくサーバーの所有者であることを確認するためにそのようにしているのでは無いかと思います)

証明書は90日ごとに更新が必要なので、サーバー管理者が手動で行うか自動化スクリプトをcronで登録する必要があります。

怖いのは、いつか忘れた頃にLet’s Encrypt側のAPI仕様やURLが変更されて自動更新に失敗したり、無料サービスが打ち切られてしまうことですね。

最初は無料で始まったサービスがいつのまにか有料になったり、大手企業に買収されて理念が大きく変質してしまった例を何度か見てきていますので・・。
(前者ならDynDNS、後者ならMySQLなどですね)

また、サーバーを管理する者としては、自分が忘れている間にWebサーバーが一時停止された後、何らかのトラブルで再起動に失敗するのが一番怖いです。

・・・と、懸念事項を思いつくだけ書いてみましたが、個人が趣味で運営しているようなサイトなら大きな問題にはならないでしょう。

しかし、商用利用してるサーバーや業務で扱うサーバーではリスクが大きいかもしれません。

Rapid SSLとFUJI SSLの比較

ニジモさんではFUJI SSLという日本のセコム社が販売しているSSLも取り扱っていて、こちらの方が3年2,400円(税別)と安かったので、購入時の比較対象としました。

ちなみに両者の対応ブラウザは次のようになっています。

RapidSSL

  • Internet Explorer ver.6以降 ※1
  • Chrome 1.0以降
  • Opera 9.5以降
  • Firefox 1.0.0以降
  • Mac OS X Safari 2.0以降 ※1
  • Android Ver.1.5以降 ※2
  • iOS Safari iOS3.0以降
  • Windows Phone 7以降

https://www.geotrust.co.jp/products/resources/compatibility_listing/より抜粋

FUJI SSL

  • Internet Explorer 8以上
  • Chrome(IEと同様 Windows XP SP3以降)
  • Opera 9.5以上
  • Firefox 12.0以上
  • Mac OS X 10.6.7以上
  • Android ver1.5以上 ※2
  • iOS ver2.0以上 ※2
  • Windows phone ver7以上

https://www.fujissl.jp/certificate/cer-browser/より抜粋

※1 実際にはSHA-2(SHA-256)の制限があるので、IE7以降・Mac OS X 10.5以降になるはずです。
※2 中間CA証明書を追加する必要あり。
 
携帯電話、いわゆるガラケーに関してはFUJIの方が対応率が高いようですが、当サイトに携帯からのアクセスはほとんど無いので気にしないことにします。

IEに関してはVer.7を使っている人はもうほとんどいないので問題ないのですが、Mac OSに関しては、当サイトはMac関係の記事も書いているせいか、アクセスログを見ると10.4や10.5のユーザーが稀にいるのです。

これら古いMac OS Xのサポートはとっくに切れているので使い続けるとセキュリティ的に危険なのですが、現実としてまだ使い続けている人がいる以上、完全に切り捨てるわけにもいかないというところです。

(本当は切り捨てた方が、新しいOS・ブラウザへの移行を促すのでいいのかもしれないですけど)

というわけでRapid SSLを導入したのですが、個人的には、今回の有効期限が切れる3年後ならFUJI SSLにしてもいい時期かなと思います。両者とも最近のスマホには完全対応しているので、スマホ向けサイトなどであればFUJI SSLは魅力的な選択肢になるのではないでしょうか。

古いブラウザへの各社のSSL対応状況

ここでちょっと気になったので、大手サイト各社の古いIEへのSSL対応状況を調べてみました。(2017年4月現在)
以前、仕事で各ブラウザでの動作確認用にVMwereに入れたWindows XPを複数種類用意していたのを手元に残していたので、それを使いました。

Internet Explorer 8
IE8でyahoo IE8でtwitterIE8でyoutube

Yahoo、Google、facebook、Amazon は表示できました。
Twitterはモバイル用のページに飛ばされましたが表示は出来ました。
YouTubeはSSLは対応してますがサイト自体が対応してないようです。

Internet Explorer 7
IE7でYahooを表示IE7でGoogleを表示IE7でTwitterを表示
IE7でAmazonを表示

Yahoo、Google、facebook、Amazon は表示できました。
Twitterはテキストは表示されますが画像が表示されませんでした。

Internet Explorer 6
IE6でYahooを表示

さすがに全滅でした。これは対応ブラウザの問題というより、SHA-2移行のためだと思われます。

本当はMac OSでも検証したかったところですが、手持ちの仮想マシンがありませんでした。

 

個人でRapid SSLを取得する

さて、話は戻ります。

いざ個人で取得しようとした時にCSR(Certificate Signing Request/署名要求)をどのように入力すればいいのか、ネットで検索しても情報がなかなか見つかりませんでした。

Rapid SSLは企業の実在性調査が無いので組織名は何を入力しても大丈夫かと思い、Organization Nameにはサイト名を入れて下記のようにしました。

Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Hokkaido
Locality Name (eg, city) []:Sapporo
Organization Name (eg, company) [Internet Widgits Pty Ltd]:PLANET-GREEN
Organizational Unit Name (eg, section) []:web
Common Name (eg, YOUR name) []:www.planet-green.com

(もちろん、ここで登録した”PLANET-GREEN”は、ただのサイト名であって法人登記しているわけではありせん。)

尚、SSL販売サイトには次の注意書きがありました。

RapidSSL、PositiveSSLをお申込みの型へ ホームページが未完成、ホームページに運営者情報が掲載されていない場合、会員制など、ログインが必要なホームページの場合、審査を通過さないケースが増えております。

RapidSSL、PositiveSSLをお申込みの方へ

ホームページが未完成、ホームページに運営者情報が掲載されていない場合、会員制など、ログインが必要なホームページの場合、審査を通過さないケースが増えております。

つまり、Rapid SSLでも運営者情報ページが必要ということでしょうか?

そこで、一時的に下記の運営者情報ページをトップページから辿れるところに設置。

一時的なサイト情報
代表者・所在地は、実際に調査されるわけでは無いので何を書いてもいいかもしれません。私の場合は特に隠す理由も無かったので正直に記載。SSLの申請をしてから認証されるまでの30分ほどだけ表示させておきました。

後からアクセスログを確認したところ、このページへの外部からのアクセスはありませんでした。運営者情報ページは不要だったのかもしれませんが、申請直後に届いた案内メールには下記の記述がありましたので、万全を期すならあった方がいいのではないかと思います。

(前略)
ご提出頂いたCSR(証明書申請データ)より審査を行います。
審査は自動化されたシステムにて行われますが、稀に人的
審査が行なわれる場合があります。

 

サーバーに設置

さて、証明書が届いたのでサーバーに設置。設置方法は検索すればたくさん出てると思うので割愛します。

ちなみに当サイトの動作環境は PHP7(php-fpm) + MariaDB + nginx + WordPress です。(2017年4月現在)

古いURL(HTTP)にアクセスがあったらHTTPSに301リダイレクトするように設定。SEO的に301を指定するのが重要です。

server {
	listen 80;
	server_name planet-green.com;
	return 301 https://$host$request_uri;
}

WordPressの管理画面 →〔設定〕→〔一般〕でURLを変更。

そしてDBの値を下記SQLで変更。

UPDATE wp_options SET option_value = REPLACE(option_value,'http://planet-green.com','https://planet-green.com') WHERE option_value like '%http://planet-green.com%';
UPDATE wp_posts SET post_content = REPLACE(post_content,'http://planet-green.com','https://planet-green.com') WHERE post_content like '%http://planet-green.com%';
UPDATE wp_posts SET guid = REPLACE(guid,'http://planet-green.com','https://planet-green.com') WHERE guid like '%http://planet-green.com%';
UPDATE wp_postmeta SET meta_value = REPLACE(meta_value,'http://planet-green.com','https://planet-green.com') WHERE meta_value like '%http://planet-green.com%';
UPDATE wp_commentmeta SET meta_value = REPLACE(meta_value,'http://planet-green.com','https://planet-green.com') WHERE meta_value like '%http://planet-green.com%';
※URLは各自の環境に合わせて書き換えてください。

WordPressのプラグインで同じ機能のものがあるようですが、直接DBを操作できるならこの方が早いのではないでしょうか。(ただしご利用は自己責任で。事前バックアップは忘れずに!)

そしてブラウザで表示した時に鍵アイコンが緑になっていることを確認。

ブラウザの保護された通信の表示

ジオトラスト・インストールチェッカー でチェックして問題なければ完了。
(ジオトラスト以外も各社がSSLサーバー証明書インストールチェッカーを提供しています。)

WebサーバーのSSL設定をIE8など古いブラウザにも対応させようとするとインストールチェッカーで満点を得られないのがジレンマです。

そもそも、どうしてHTTPS化?

ただの個人サイトなのにどうしてHTTPS化したかと言いますと、やはりgoogleがHTTPS推奨を表明したのが大きいです。

Google ウェブマスター向け公式ブログ: HTTPS をランキング シグナルに使用します

良かれ悪かれ、WEBに携わる者がgoogleの掌の上で踊らされている状況はまだまだ続きそうです・・・。

他の理由としては、HTTP/2 を試してみたかったのと、WordPressの管理画面やphpMyAdminなどを操作する時にHTTPSだと安心できるという点でしょうか。
HTTP/2は、体感的はそれほど早くなったわけではないような・・・。PHP5.3から7.0に変えた時やキャッシュプラグインを導入した時の方が体感速度の向上は大きかったような気がします。

また、証明書はメールサーバーにも使えますので、SSL化すると安全性が高まります。
(特に、ホテルなど公共のLANに接続してメールの送受信をする時など)

コメント
planet-green.com

[備忘録] WordPressのテーマをTwenty Seventeenに変更



ほぼ独り言。
当ブログのWordPressのテーマをTwenty Seventeenに変更。

WordPressのテーマ Twenty Seventeen

公式テーマだけあって可も無く不可も無く、最近のトレンド合わせて作っているという感じ。

そのままだと当ブログのような写真メインのブログには合わない部分もあるので少し微調整・カスタマイズ。SEO的にも初期状態のままだと良くない箇所がある。

hentry(microformats.org)のマークアップに対応しているのでSearch Console(Googleウェブマスターツール)の構造化データ・テストツールでエラーが出なくなったのが嬉しい。

でもschema.orgのマークアップには対応していないので、後で自分でカスタマイズしないといけない。

気が向いたらここらへんの詳細を後で追記するかも。

コメント
planet-green.com

Safari 10での確定申告にはまる



今年も期限日ギリギリになって確定申告したのですが、MacのSafari 10でのe-taxでの申請にはまりました。

証明書のインストールやJavaアプレットの有効化までは問題なく出来たものの、データ送信画面で

『HJS0407E ICカードを認識できませんでした。ICカードリーダライタの設定等をご確認の上、再度処理してください。』

と表示され、そこから先に進めません。

ネットで調べてみると下記の設定が必要なことがわかりました。

Safariの「環境設定」 → 「セキュリティ」→ 「プラグイン設定」ボタンをクリック→ 左の「Java」を選択 →「keisan.nta.go.jp」のプルダウンメニューを「option」キーを押しながらクリックすると表示される「安全なモードで実行」のチェックを外す

e-Tax利用時のSafariのセキュリティ設定

参考にさせていただいたサイト
macOS Sierra 10.12とSafari 10にてe-Taxで確定申告する準備と注意点 : トイレのうず/ぼやき

 

やれやれ、、optionキーを押さないと表示されないなんて、普通はわからないですって(笑

e-TaxではSafari 10がサポート対象外になってるのですが、普通にMacを使ってる人の大半は自動アップデートで10になってるはず。
Macユーザーでここまで辿り着けたユーザーは何割いるのやら。
e-taxの説明ページにSafari 10での注意点として書いてくれたらよかったのにと思います。

Safari 10の公開時期は2016年9末。おそらく、その前にシステム更新の発注・納入が終わっていたので間に合わなかったのでしょうけれど、お役所とは言えもうちょっと柔軟になってくれてもいいのではないかと。

もっと根本的なことを言うと、各ブラウザがいつまでJavaアプレットのサポートを続けるのかは未知数なので(実際、ChromeとFirefoxは打ち切っている)、Macでもスタンドアロン版のクライアントを作って欲しいのですが。国税庁さん、お金はあるでしょう?

コメント
planet-green.com

[開発/備忘録][PHP] 複数の画像を結合した1枚のサムネイル画像を自動作成する



複数の画像を入力すると結合された1枚のサムネイル画像を作成するPHP用のライブラリです。
SNSなどでよく見かけるタイプの結合サムネイルです。
サイズは自動計算してくれます。某SNS用に作りました。
create combine thumbnail from multiple images.

System Requirements:
PHP v5.3+
ImageMagick

【実行例】

4枚の元画像を入力 / source image files



↓↓↓↓↓↓↓↓
連結して1枚のサムネイル画像を出力 / output a thumbnail  image
combine thumbnail library for php

元画像は1〜4枚に対応。

・2枚の場合は上下に並びます。 / in case of two src images
combine thumbnail library for php.(two images)

・3枚の場合は横に。 / in case of three src images
combine thumbnail library for php.(three images images)

コードと使用例。試しにGithub Gistにコードを載せてみました。

コメント
planet-green.com

[D.I.Y.] SLINGBOX 350が熱暴走するのを改善する



少し前のことですが、自宅で居間から離れている部屋でもテレビを見たいと思い Sling Media SLINGBOX 350 を購入しました。

画質・操作性などはまずまずで快適に使っていたのですが、夏が近づくにつれ、使用中にフリーズする現象が多発。
おそらく熱暴走ではないかと思い、とりあえず中がどうなっているのか確認するために分解してみました。

Disassemble Sling Media SLINGBOX 350

ちなみに本体を開封するのが異様に難しかったです。真似したい人はプラスチックの爪を1,2本折るのを覚悟するべき。
基盤を見ると、穴の空いたアルミ板が中央に鎮座しており、これはヒートシンクではと思うかもしれませんが、チップには接触していないので、ただの電磁シールドだと思われます。
全てのチップにヒートシンクが付いていないので、とにかく冷却させることにします。

“[D.I.Y.] SLINGBOX 350が熱暴走するのを改善する”の続きを読む

コメント(2)
planet-green.com

[開発/備忘録] WordPress + Custom Fields に複雑な検索機能を実装する



先日、クライアントさんからの依頼でWordPress+カスタムフィールドで構築されたサイトに絞り込み検索機能を実装する必要が出てきました。
googleで検索すると下記のサイトに説明がありましたが、シンプルな全文検索にしか対応してないようなので、
これを参考にしつつ、もう少し複雑な検索(OR検索とAND検索の組み合わせ)に対応したものを作ってみました。

参考 http://www.deluxeblogtips.com/2012/04/search-all-custom-fields.html


global $wpdb;

//次のSQLに SQL_CACHE を含めているのは、レンタルサーバーによってはMySQLのクエリキャッシュの
//デフォルト設定を無効にしているところがあるので、明示的にクエリキャッシュを有効にするように指定している。
$sql = "SELECT SQL_CACHE DISTINCT posts.ID FROM {$wpdb->posts} posts";


//チェックボックスorセレクトボックスの検索(複数のAND検索)
//例えばcolorというフィールド名だとする。
//$_GET['color']にはカスタムフィールドの設定画面の選択肢の箇所で入力したラベルと値のうち、値の方が配列になって入ってくるとする。 
if( count($_GET['color']) )
{
	$flg_query = true;

	$sql .= " INNER JOIN {$wpdb->postmeta} tbl_color ON tbl_color.post_id=posts.ID AND tbl_color.meta_key = 'color'";

	$tmp_arr = array();
	foreach ($_GET['color'] as $item)
	{
		//シリアライズ化された文字列の中を全文検索する
		$keyword = '%' . $wpdb->esc_like( '"' . trim($item) . '"' ) . '%';
		$tmp_arr[] = "tbl_color.meta_value LIKE '{$keyword}'";
	}
	$sql .= " AND (" . implode(" AND ", $tmp_arr) . ") ";
	
	//↓ちなみにOR検索の場合はこうする
	//$sql .= " AND (" . implode(" OR ", $tmp_arr) . ") ";
}

//複数のチェックボックスorセレクトボックスがある場合は、ここに追記していく。


//注意点として、選択肢の値を 1,2,3・・や a,b,c・・のように設定すると
//他のフィールドと値が重複して正確な検索が出来ないので、全フィールドを通じてユニークな値にする必要がある。

$sql .= " WHERE posts.post_type='(※投稿タイプ名※)' AND posts.post_status='publish'";


//全文検索(AND検索)

//全文検索用キーワード
//$_GET['keyword']にはキーワードが半角スペース区切りで入っているとする。
$free_keyword = isset($_GET['keyword']) ? trim($_GET['keyword']) : "";    //「キーワード検索」

if( $free_keyword != "" )
{
	$arr_free_keyword = $free_keyword == "" ? array() : explode(" ", $free_keyword);
	$arr_free_keyword = array_unique($arr_free_keyword);

	if( count($arr_free_keyword) > 8 )
	{
		//検索キーワード数の上限をとりあえず8個までにする。
		//ここが無制限だと、意図的に膨大なキーワード数で検索を実行された時にサーバーの処理が追いつかなくなる危険性が生じるので。
		$arr_free_keyword = array_slice($arr_free_keyword, 0, 8);
	}

	$tbl_cnt = 1;
	foreach ($arr_free_keyword as $_key)
	{
		$tbl = "key" . $tbl_cnt++;
		$keyword = '%' . $wpdb->esc_like( trim($_key) ) . '%';
		
		$sql .= " AND (SELECT 1 FROM {$wpdb->postmeta} {$tbl} WHERE {$tbl}.post_id=posts.ID AND ( ({$tbl}.meta_key NOT LIKE '\_%' AND {$tbl}.meta_value LIKE '{$keyword}') OR posts.post_title LIKE '{$keyword}' OR posts.post_content LIKE '{$keyword}') LIMIT 1)";
		
		//↑DISTINCTにしてもいいのだが、LIMIT 1 の方が速度が早いと思われるので(ただし未検証)
	}
}


$post_ids = $wpdb->get_col($sql);	//検索でヒットした投稿データのIDが入る
$cnt = count($post_ids); //投稿数

if($cnt)
{
	$args = array(
		'post_type' => '(※投稿タイプ名※)',
		'post_status' => 'publish',
		'post__in' => $post_ids,
		
		//ページングする場合は次のパラメーターをお好みで
		'posts_per_page' => ****,
		'offset' => ***,
		
		//ソートする場合は	次のパラメーターをお好みで
		'meta_key' => '******',
		'order' => "ASC",	/* or DESC */
		'orderby' => "meta_value"
	);

	$posts = get_posts($args);
}
else
{
	$posts = array();
}

以下略

尚、テーブル wp_postmeta にインデックスを追加するとセレクトボックス・チェックボックスでの検索速度が向上すると思われます(ただしキーワードによる全文検索にはインデックスは効果ありません)。

例) ALTER TABLE wp_postmeta ADD INDEX idx_custom1 (post_id, meta_key(16), meta_value(16));

と、ここまで書いておいて言うのもなんですが、今回の記事で書いた方法は、小規模〜中規模のサイトまでしか通用しないということにご留意ください。

カスタムフィールドを追加していくとテーブル wp_postmeta の行数が等比級数的に増えていきます。
(私の知ってるクライアントでの例だとカスタムフィールド数が約60、登録ページ数が約30で wp_postmeta が約40万行でした。)

これだけ膨大なデータの中から検索を実行するわけなので、かなりの負荷になります。

最近、なんでもかんでもWordPress+カスタムフィールドで実装すようとするIT屋さんが少なくないようですが、データ数やアクセス数(PV)がある程度まで増えると負荷が増大して破綻するのは火を見るよりも明らかです。

データ数やアクセス数が大規模になりそうなサイトはWordPressに頼らずフルスクラッチで開発するべきです。

 

コメント