WordPressの記事をAjaxで追加読み込みしたときの話

2014/02/06

Wordpressの記事をAjaxで追加読み込みしたときの話

この記事は2016年10月14日に更新されました。

2016年6月15日に下記の修正を行いました。

追加読み込みをAjaxを使って表示させたいなと、思いまして
いろいろ探してましたらいいのがありましたので今回はそのやり方を詳しく紹介したいと思います。※プラグインは使ってません。

参考にさせていただいたサイトは
WordPress ページングはやめてAjaxローディングにする
です。では始めたいと思います。

もし、急ぎで知りたい方はページの一番下のまとめのところをご覧下さい。

主要ファイル

今回使用するファイルは以下の3ファイルです。(ファイル名は任意で大丈夫です。)

  • ・index.php(追加読み込みしたい箇所があるファイル)
  • ・style.js(「more.php」で生成したHTMLを「index.php」に反映するファイル)
  • ・more.php(データベースから値を持ってきてHTMLの基礎を生成するファイル

では、index.phpから順番に説明していきたいと思います。

index.php

//タイトルの取得

//投稿時間の取得

//抜粋記事の取得

Read More

Not Found.

もっと表示する

WordPressをいじったことのある方ならわかると思いますが、記事をループして一覧表示するところのソースはですね。3行目のところですが、


ここで記事の表示数のことを決めて下さい。今だと6件表示するということになります。
ちなみに僕のサイトも始めは6件表示です。
今回は投稿タイトルと投稿時間、投稿記事の抜粋テキスト、この3つを表示したいと思います。

//タイトルの取得

//投稿時間の取得

//抜粋記事の取得

Read More

そして最後に今回の紹介では欠かせない「もっと表示する」ボタン。これをクリックして追加読み込みします。

もっと表示する

style.js

var now_post_num = 6; // 現在表示されている数
var get_post_num = 6; // もっと読むボタンを押した時に取得する数
 
jQuery("#more a").live("click", function() {
     
    jQuery("#more").html('');
    
    jQuery.ajax({
        type: 'post',
        url: 'http://sample_site.com/wp-content/themes/テーマ名/more.php',
        data: {
            'now_post_num': now_post_num,
            'get_post_num': get_post_num
        },
        success: function(data) {
            now_post_num = now_post_num + get_post_num;
            data = JSON.parse(data);
            jQuery(".main").append(data['html']);
            jQuery("#more").remove();
            if (!data['noDataFlg']) {
                jQuery(".main").append('

もっと表示する

'); } } }); return false; });

次はjQuery部分の説明をします。ここでAjaxでWordPressのデータベースとアクセスします。まずは上から説明していきますね。1行目と2行目の部分

var now_post_num = 6; // 現在表示されている数
var get_post_num = 6; // もっと読むボタンを押した時に取得する数

コメントにも書いてありますが、現在の記事の表示数と追加読み込みする時の追加記事件数をまず指定します。
4行目であるように「もっと表示するボタン」をクリックするとAjaxが作動します。

jQuery("#more a").live("click", function() {

次はAjaxの肝の部分になります。

jQuery.ajax({
    type: 'post',
    url: 'http://sample_site.com/wp-content/themes/テーマ名/more.php',
    data: {
        'now_post_num': now_post_num,
        'get_post_num': get_post_num
    },
    success: function(data) {
        now_post_num = now_post_num + get_post_num;
        data = JSON.parse(data);
        jQuery(".main").append(data['html']);
        jQuery("#more").remove();
        if (!data['noDataFlg']) {
            jQuery(".main").append('

もっと表示する

'); } } });

「type」とか「url」とかはAjaxをするときのお決まりパターンみたいなのでそのまま書いておきます。
※urlはフルパスでお願いします。

もし、Ajaxが成功したら「success」以降の処理が行われます。
class名「main」にdataのhtmlを追加しないさいという命令みたいですね。まぁこのくらいのことを理解していればいいと思います。(本人も詳しくは知らないですが、結果できてるので理解できていると思います。)
あとは件数がすべて出てしまったときのために、追加読み込みボタンの条件分岐が書かれています。

if (!data['noDataFlg']) {
    jQuery(".main").append('

もっと表示する

'); }

最後は、more.phpでデータベースとの接続部分について説明したいと思います。

more.php

posts.ID,
            $wpdb->posts.post_title,
            $wpdb->posts.post_content
        FROM 
            $wpdb->posts  
        WHERE 
            $wpdb->posts.post_type = 'post' AND $wpdb->posts.post_status = 'publish'
        ORDER BY 
            $wpdb->posts.post_date DESC 
        LIMIT %d, %d";
        // LIMIT $now_post_num, $get_post_num";

    $pre = $wpdb->prepare($sql,$now_post_num,$get_post_num); // 追記
    $results = $wpdb->get_results($pre);
    // $results = $wpdb->get_results($spl);
     
    $sql = "SELECT
            $wpdb->posts.ID, 
            $wpdb->posts.post_title, 
            $wpdb->posts.post_content 
        FROM 
            $wpdb->posts  
        WHERE 
            $wpdb->posts.post_type = 'post' AND $wpdb->posts.post_status = 'publish'
        ORDER BY 
            $wpdb->posts.post_date DESC 
        LIMIT %d, %d";
        // LIMIT $next_now_post_num, $next_get_post_num";

    $next_pre = $wpdb->prepare($sql,$next_now_post_num,$next_get_post_num); // 追記
    $next_results = $wpdb->get_results($next_pre);
    // $next_results = $wpdb->get_results($spl);
     
    $noDataFlg = 0;
    if ( count($results) < $get_post_num || !count($next_results) ) {
        $noDataFlg = 1;
    }

    $html = "";
     
    foreach ($results as $result) {
        $html .= '';
    }
    $returnObj = array();
    $returnObj = array(
        'noDataFlg' => $noDataFlg,
        'html' => $html
    );
    $returnObj = json_encode($returnObj);

    echo $returnObj;
?>

SQLインジェクション対策のため上記コードの17行目、32行目部分を編集しました。(コメントアウト部分が以前のコードです)
また17行目、32行目の編集に伴い、19行目、20行目と35行目、36行目も編集しました。

とりあえず、行数が長いっ!!しかもこのページはほんっと意味がわかりませんでした笑
ただちゃんと一つ一つ読み解いていくとわかってきます。
では、解読していきましょう!(なぜ?)

まず、ここですね!1行目。なぜここにアクセスするのか。まぁそこまで固着して見ずにこういうものだと思ってもいいと思うんですが気になって調べてみました。

require_once("../../../wp-config.php");

結論からいうとwp-config.phpを介することでどうやらその下のSQLの部分で書かれている「$wpdb」が使えるようになるみたいです。「$wpdb」とはWordPressのデータベースにあるすべてのテーブルからデータを読み出すために使われるグローバル変数みたいです。
では1つ目のSQLから見ましょうか。

SQLその一

$sql = "SELECT
        $wpdb->posts.ID,
        $wpdb->posts.post_title,
        $wpdb->posts.post_content
    FROM 
        $wpdb->posts  
    WHERE 
        $wpdb->posts.post_type = 'post' AND $wpdb->posts.post_status = 'publish'
    ORDER BY 
        $wpdb->posts.post_date DESC 
    LIMIT %d, %d";
    // LIMIT $now_post_num, $get_post_num";

$pre = $wpdb->prepare($sql,$now_post_num,$get_post_num); // 追記
$results = $wpdb->get_results($pre);
// $results = $wpdb->get_results($sql);

1つ目のSQLは指定した件数分の記事のタイトルと投稿時間と記事の内容を取得しています。その後、複数行データを取ってくる時に使う「get_results」関数を使い、「$results」に格納します。

SQLがわからない人のためにSQL文で意味を解説

ここの部分はSQL文で書かれています。このSQL文では
「公開している記事の中で現在表示していない記事6件(今回の場合だと)の記事IDとタイトルと投稿時間を取得してください。」
と命令しています。

2つ目のSQLも見ていきましょう。2つ目のSQLは1つ目のSQLとほとんど変わらないのですが
「limit」の部分だけ違っています。

SQLその二

$sql = "SELECT
        $wpdb->posts.ID, 
        $wpdb->posts.post_title, 
        $wpdb->posts.post_content 
    FROM 
        $wpdb->posts  
    WHERE 
        $wpdb->posts.post_type = 'post' AND $wpdb->posts.post_status = 'publish'
    ORDER BY 
        $wpdb->posts.post_date DESC 
    LIMIT %d, %d";
    // LIMIT $next_now_post_num, $next_get_post_num";

$next_pre = $wpdb->prepare($sql,$next_now_post_num,$next_get_post_num); // 追記
$next_results = $wpdb->get_results($next_pre);
// $next_results = $wpdb->get_results($sql);

ここでは、先ほどの「SQLその一」のときに取得する記事のその次の記事を取得をしています。次のターンの記事を読みに行っているということですね。何でかって?それは次のコードを見ればわかると思います。

$noDataFlg = 0;
if ( count($results) < $get_post_num || !count($next_results) ) {
    $noDataFlg = 1;
}

ここで「$noDataFlg」フラグを立てています。
『もし、読み込み件数が指定した数より少ないか次のターンの記事がない場合は$noDataFlgを立てる』って意味ですね。このフラグを立てることによって「styel.js」で「もっと表示する」ボタンが表示されなくなります。

HTML生成部分

foreach ($results as $result) {
    $html .= '';
}

ここが一番悩みました。(2日くらい…)何が悩むって「$html」うんたらかんたらのところです!見てみるとforeachをしてデータを取得しています。「get_permalink」すら普通に使えない。「$result->ID」とか何とか、一癖ありそうなのを書かないといけない。ここで私はいろいろ試しました。そこで気づいたことを書きます。

  • ・echoのコードが使えない
  • ・SQLで取得したものしか使えない
  • ・基本はPOST ID($result->ID)で賄う

この3つ目の「基本はPOST ID($result->ID)で賄う」に助けられました。例えば、
投稿時間を取得する場合、「index.php」では

という感じになりますが、「more.php」の場合では

$html .= '

'.get_post_time('M d, Y','false',$result->ID).'

';

という風にちょっと表記が違います。ここが難しかったです。
「get_post_time」には何個か引数があります。第三引数(カンマの3つ目)に「post_id」を入れる箇所があるのでそこに「$result->ID」を入れてあげることで取得できるようになります。
ここらへんのところはWordPress Codex 日本語版WordPress私的マニュアルなどを見まくってようやく理解できました。

まとめ

長くなりましたが、まとめますと、「index.php」「style.js」「more.php」の各ファイルを、themeフォルダの中に格納して下さい。(jsファイルは「js」フォルダの中に格納してください。)
以上!簡単!!
さすれば、index.phpのページの「もっと表示する」ボタンが表示されると思いますのでそれをクリックすると、Ajaxで追加読み込みができているはずです。

  • あれ、これだとSQLインジェクションできるような

    • koukitips

      ご指摘ありがとうございます。
      具体的にどのあたりでSQLインジェクションができるのかお教えいただければ幸いです。

    • koukitips

      junerさん 遅くなりましたがSQLインジェクション対応いたしました。
      ご指摘いただきありがとうございました。

      # まだおかしなところがある場合お教えいただけると幸いです。

  • shorarion

    とても参考になる記事をありがとうございます。

    当方は固定ページの親ページに子ページの一覧を表示して、その記事一覧をAjaxで追加読み込みしたいと思っているのですが、そのようなことは可能でしょうか。

    お手すきの時で構いませんので、ご教授いただければ幸いです。

    よろしくお願いいたします。

    • koukitips

      遅くなりすいません。
      sql文のwhereのところを編集すれば、取得できるのかなとは思います。
      例えば、
      $wpdb->posts.post_type = ‘page’ AND $wpdb->post_parent = ‘親ページ’ AND $wpdb->posts.post_status = ‘publish’
      などで取得できないでしょうか。
      (実際試していないので取得できるかはわかりませんが。。)

      遅くなってしまって申し訳ないのですが
      もしよろしければご参考にしてください。

      あと、コードも一部変更しましたので
      そのあたりもご確認いただければと思います。

  • aki

    koukitipsさんのサイトでは、画像や画像がない場合の画像等も、ajaxで読み込まれています。もしよろしければ、そのやりかたを教えて頂けないでしょうか?よろしくお願いします。
    ($htmlのところの書き方です。)