Introduce
この記事の読了時間:約15分35秒
このところ進めているクライアントワークのWordPress案件で、とあるカスタムポスト上で「すでに同じタイトルの記事があるかどうか」を知れたら良いなというご要望が浮上しまして。
そのカスタムポストってのが、例えて言うなら「人名辞典」とか「パーツ一覧」みたいな感じで、要は似たようなタイトルを持つ記事が多数収められる予定で、月数回くらい管理者さんが手入力で追加していくスタイル。
そうなると、何度も同じタイトルで重複して投稿しちゃったりとか、このデータって入力したっけ?的なことになるのは確実だし、とはいえいちいち投稿前に検索するのも手間かかるし。。。
調べたら使えそうなプラグインがあったんだけど、作りが古く更新もされてないのと「投稿」にしか使えないってコトなので、、、作っちゃいました☆
その名も「Notify Duplicate Title」!
タイトルがかぶってたら(すでに存在していたら)画面上にアラート出すよ!っていうシンプルなWordPressプラグインです。
それではさっそく、使い方やら技術的なお話をば。
実はすでにWordPress公式ディレクトリに公開されているので、そこからzipを落として解凍→アップロードしてもいいし、WordPressの管理画面からプラグイン検索しても見つかるので、どちらでもお好きなようにインストールしてくださいな。
プラグイン自体は特に難しい設定も無いので、有効化するだけでOK!
インストールすると、新しくタイトルを書いたり、編集画面でタイトルが書き換えられたタイミングで(厳密には、タイトルのフォームからフォーカスが外れた時に)裏っ側で「同じポストタイプの中に同じタイトルの記事があるか」を検索、あるか無いかを画面上部に通知してくれます。
今回の技術的ポイントは、何と言っても「WordPress管理画面でのAJAX処理」。
タイトルを記入するinputの値が変更された時に、非同期で検索→結果を受け取って即時表示する、という一連の流れを、WordPress内臓のAJAX機構とjQueryとPHPで実装した形となります。
順を追って処理の流れを見てみると
となりますね。
んで、上記1.はイベント監視、4.は即時表示ということでJavaScript(=jQuery)の担当箇所となり、2.の検索処理と3.の検索結果発表!はPHPで実装するという風になります。
まずはプラグインの本体となるPHPファイル notify-duplicate-title.php と、前述1.と4.を行うJSファイル notify-duplicate-title.js を用意します。
このJSの処理が動いて欲しいのは、投稿やらポストタイプやらの「新規投稿」と「編集」の場面のみなので、手始めに本体PHPにてJSの出し分けをします。
function ndt_enqueue($hook_suffix){ // post.php もしくは post-new.php の時に... if($hook_suffix === 'post.php' || $hook_suffix === 'post-new.php'){ // notify-duplicate-title.jsと、ついでにjQueryも読み込んでね。 wp_enqueue_script('notify_duplicate_title', plugins_url('js/notify-duplicate-title.js', __FILE__), array('jquery')); // おまじない1 ※あとで説明アリ wp_localize_script('notify_duplicate_title', 'NDT', array('endpoint' => admin_url('admin-ajax.php'))); } } // 管理画面だったら上記関数を動かして! add_action('admin_enqueue_scripts', 'ndt_enqueue');
これで不要なトコではプラグインが動かないというコトになりました。
んで続いてはJSファイルにて、jQueryを使ってタイトル記述箇所のイベントを監視しましょー。
jQuery(document).ready(function($){ $('#title').on('change', function(){ // ここに処理が入る予定。 }); });
現在のタイトルやポストタイプを条件として検索するためには、jQueryの$.ajax()なんかを使って「いま記入されたタイトル」と「いまのポストタイプ」をPHP側に渡してあげないといけません。
なので、次工程2.の前に「PHPでの検索に必要な値を、JSで渡してあげる」という処理が必要になります。
jQuery(document).ready(function($){ $('#title').on('change', function(){ // 実際には記事IDも必要になるので取っとく。 var post_id = $('#post_ID').val(); var post_title = $('#title').val(); var post_type = $('#post_type').val(); $.ajax({ url: ●●●●, data: {search_id: post_id, search_title: post_title, search_type: post_type}, type: 'POST' }).done(function(response){ // 通信が成功した場合の処理が入る予定。 }); }); });
さてさて「url: ●●●●」とありますが、検索に必要な値 post_title や post_type は、どこに渡してあげればいいでしょうか。
その答えこそ「WordPress内臓のAJAX機構」こと「admin-ajax.php」なのであります!
この「admin-ajax.php」を経由させることで、JSとPHPで簡単に値を受け取ったり渡したり、受け取ると同時に走らせたいPHPの関数を設定出来たりするのです!
なので、ここには admin-ajax.php へのパス(=admin_url('admin-ajax.php');で取れる)を記述するんですが、JSファイル内だからPHP関数は直接使えない。。。
そこで現れるのが、さっきのPHPに記載した ※おまじない1、wp_localize_script()関数なんや!
wp_localize_script($handle, $name, $data); // $handle // 値を使用するJSのハンドル名 // (wp_enqueue_scriptで定義した名前) // $name // オブジェクト名を設定してね // (グローバルなオブジェクトになるのでユニークなヤツを) // $data // 渡す値 // (配列形式でどうぞ)
このおまじないによって、PHP側で書かれた変数の値をJSに引き渡せるので、心置きなく admin_url('admin-ajax.php') の値をJSに送ります。
jQuery(document).ready(function($){ $('#title').on('change', function(){ var post_id = $('#post_ID').val(); var post_title = $('#title').val(); var post_type = $('#post_type').val(); $.ajax({ url: NDT.endpoint, data: {search_id: post_id, search_title: post_title, search_type: post_type}, type: 'POST' }).done(function(response){ // 通信が成功した場合の処理が入る予定。 }); }); });
これで NDT.endpoint の中には admin_url('admin-ajax.php') の値が入ります。よかったですね。
続いてはPHP側での検索処理。の前に、、、。
今回は検索結果を即時表示させたいので、ajax通信と同時に検索を行って結果を返すという流れにしなければいけません。
ここでadmin-ajax.phpの真骨頂!
admin-ajax.phpに渡す値に「action: '本体PHPで用意したアクションフック名'」という値を含めると、いざajax通信を行うって時にそのアクションフックを起動してくれるのです!
function duplicate_checker(){ // ここに処理が入る予定。 } // アクションフック名 wp_ajax_duplicate_title_checker // 動かす関数 duplicate_checker add_action('wp_ajax_duplicate_title_checker', 'duplicate_checker');
jQuery(document).ready(function($){ $('#title').on('change', function(){ var post_id = $('#post_ID').val(); var post_title = $('#title').val(); var post_type = $('#post_type').val(); $.ajax({ url: NDT.endpoint, data: { search_id: post_id, search_title: post_title, search_type: post_type, // アクションフック名から「wp_ajax_」を抜いた文字列 action: 'duplicate_title_checker' }, type: 'POST' }).done(function(response){ // 通信が成功した場合の処理が入る予定。 }); }); });
これでやっと、本体PHPにて検索処理が実装できます。よかったですね。
function duplicate_checker(){ $search_id = $_POST['search_id']; $search_title = $_POST['search_title']; $search_type = $_POST['search_type']; $get_page_by_title_obj = get_page_by_title($search_title, OBJECT, $search_type); if($get_page_by_title_obj !== NULL && $get_page_by_title_obj->ID !== $search_id && $get_page_by_title_obj->post_title === $search_title) { // 重複してる場合の処理 } else { // ユニークな場合の処理 } exit; } add_action('wp_ajax_duplicate_title_checker', 'duplicate_checker');
get_page_by_title()はWordPressの関数で、タイトル文字列から記事があるか探してくれて、見つかったらその記事のオブジェクトを返すし、見つからなければNULLを返すってヤツ。
ただし大文字小文字の区別をつけてくれないので、そのへんはif文で対応するとして。
なので、NULLじゃない + 関数で取ってきた記事IDが自分自身じゃない + 関数で取ってきた記事のタイトル===検索対象タイトル = 重複しとるで!という切り分けをします。
あるなしの結果はJSでこれ以上加工することもないので、OKのときは「<div id="message" class="notice notice-success"><p>ユニークですよ</p></div>」、重複してるときはエラーっぽく「<div id="message" class="notice notice-error"><p>重複してます</p></div>」というid/classを指定したHTMLを素直にechoで出力しちゃいます。
function duplicate_checker(){ $search_id = $_POST['search_id']; $search_title = $_POST['search_title']; $search_type = $_POST['search_type']; $get_page_by_title_obj = get_page_by_title($search_title, OBJECT, $search_type); if($get_page_by_title_obj !== NULL && $get_page_by_title_obj->ID !== $search_id && $get_page_by_title_obj->post_title === $search_title) { echo '<div id="message" class="notice notice-error"><p>重複してます</p></div>'; } else { echo '<div id="message" class="notice notice-success"><p>ユニークですよ</p></div>'; } exit; } add_action('wp_ajax_duplicate_title_checker', 'duplicate_checker');
最後に、本体PHPでechoされた結果をそのまんまjQueryで吐き出します。
jQuery(document).ready(function($){ $('#title').on('change', function(){ var post_id = $('#post_ID').val(); var post_title = $('#title').val(); var post_type = $('#post_type').val(); $.ajax({ url: NDT.endpoint, data: { search_id: post_id, search_title: post_title, search_type: post_type, action: 'duplicate_title_checker' }, type: 'POST' }).done(function(response){ $('#message').remove(); $('#poststuff').prepend(response); }); }); });
あとはセキュリティのためにsanitize系の関数を使ったりnonce使ったりして、無事完成と相成りました!
というワケで、ご利用の機会がありましたら、ぜひとも!!
でも仕事の合間の良い息抜きになった!
年末進行に向けて、ファイトっ!!
関連する記事
同じカテゴリーの記事
smkn より:
2016/12/20 11:38 AM
> さきも さん
ご報告ありがとーございます!
ふつつかものですが可愛がってあげてください(*^^*)
マルス より:
2023/06/27 1:29 AM
素晴らしいプラグインです!
更新してほしいです、わがままですねコレ。(汗)
さきも より:
2016/12/20 7:20 AM
使わせていただきます。
素晴らしいプラグインをありがとうございますm・・m