プラグインなしで人気記事一覧ウィジェット化-全記事からの人気記事ランキングウィジェット

2018年2月11日

プラグインなしで人気記事一覧ウィジェット化

プラグインなしで人気記事一覧ウィジェット化の3回目、いよいよサイドバーに表示する全記事からの人気記事ランキングウィジェットを作成・登録する。

ウィジェットの管理画面での入力項目はウィジェットタイトルと人気記事ランキング表示数だけと、そんなに複雑なウィジェットではないので、WordPress ウィジェット APIのウィジェットの例などを参考にすればなんとなくできた。

追記 2018/04/21

全記事からの人気記事ランキング一覧のn番目にランダム広告を挿入する機能を追加したので各コードが変更されています。

追記2 2018/05/26

ひとつのショートコードで新着記事一覧、人気記事一覧、ランダムバナーなどの広告を一元管理するようにしたので各コードが変更されています。挿入するランダム広告リストを上下2箇所指定できるようにウィジェットオプションに追加。ショートコードひとつで広告管理・表示する詳細は下のページを参照。

例によっていつものfunctions.phpに書き込むことになるので、下のページの方法でまとめた方がよいのではないだろうか。

WP_Widgetクラスを継承しサブクラスを作成

/* 人気記事ランキングウィジェット */
class Ninki_Kiji extends WP_Widget {
public function __construct() {
parent::__construct(
'ninki_kiji',
'人気記事ランキング',
array( 'description' => 'PV数の多い順で人気記事をランキング' )
);
}

とりあえず難しいことは抜きにして、上のコードの色の変ってるところを書き換えればいいだろう。2行目のclassの後に任意のサブクラス名(Ninki_Kiji)、5行目にウィジェットのID(ninki_kiji)、6行目にウィジェットの名前(人気記事ランキング)、7行目のdescriptionにウィジェットの説明といった感じで書き換える。

Webページに出力するウィジェットの内容 widget()

/* ウィジェットの内容をWebページに出力 */
public function widget($args, $instance) {
extract($args);
echo $before_widget;
if(!empty($instance['title'])) {
$title = apply_filters('widget_title', $instance['title'] );
}
if ($title) {
echo $before_title . $title . $after_title;
} else {
echo '<h3>人気の記事とページ</h3>';
}
$number = $instance['number'] ;
?>
<aside class="ninki-kiji">
<ul>
<?php get_the_ID(); 
$args = array(
'meta_key'=> 'post_views_count',
'orderby' => 'meta_value_num',
'order' => 'DESC',
'post_type' => array('post','page'),
'post__not_in' => array(41),
'posts_per_page' => $number
);
$my_query = new WP_Query( $args );?>
<?php while ( $my_query->have_posts() ) : $my_query->the_post(); $rankcount++; ?>
<li>
<a href="<?php the_permalink(); ?>">
<span class="rank-count ranking-count<?php echo $rankcount; ?>">
<?php echo $rankcount; ?>
</span>
<?php if( has_post_thumbnail() ): ?>
<?php the_post_thumbnail('thumb100'); ?>
<?php endif; ?>
<div class="ninki-kiji-text">
<p><?php the_title(); ?></p>
<p><span class="pv-data">
<?php  echo 'アクセス数: ' .  get_post_views(get_the_ID()); ?>
</span></p>
</div>
</a>
</li>
<?php endwhile; ?>
<?php wp_reset_postdata(); ?>
</ul>
</aside>
<?php echo $after_widget;
}

前半部分、2行目public function widget($args, $instance)から14行目までは、ウィジェットのタイトルや表示記事数についてごにょごにょ書いてる。ウィジェットのタイトルの入力がなかったら、echo '<h3>人気の記事とページ</h3>';とか、サブループに渡す条件の、'posts_per_page'は、$number = $instance['number']だとか。

17行目からお馴染みのサブループに渡す条件の定義からサブループに入る。27行目の、$rankcount++がランキングづけのポイント。30行目のclass="rank-count ranking-count<?php echo $rankcount; ?>"で、cssのランキング表示クラスに、31行目でランキング表示している。

39行目、get_post_views(get_the_ID())でページビュー数を表示している。詳しくは、ページビューの取得コード get_post_views() 参照。45行目、wp_reset_postdata(); をわすれないでね。

追記 ショートコードひとつで広告管理・表示に変更後のWebページに出力するウィジェットの内容 widget() 2018/05/26

/* ウィジェットの内容をWebページに出力 */
public function widget($args, $instance) {
extract($args);
echo $before_widget;
if(!empty($instance['title'])) {
$title = apply_filters('widget_title', $instance['title'] );
}
if ($title) {
echo $before_title . $title . $after_title;
} else {
echo '<h3 class="side-title">人気の記事とページ</h3>';
}
$number = $instance['number'] ;
$add_top = $instance['add_top'];
$add_bottom = $instance['add_bottom'];
$add_type_top = do_shortcode('[add-list type='. $instance['add_type1'].']');
$add_type_under = do_shortcode('[add-list type='. $instance['add_type2'].']');
?>
<aside class="ninki-kiji">
<ul>	
<?php
get_the_ID(); 
$args = array(
'meta_key'=> 'post_views_count',
'orderby' => 'meta_value_num',
'order' => 'DESC',
'post_type' => array('post','page'),
'posts_per_page' => $number
);
$my_query = new WP_Query( $args );?>
<?php while ( $my_query->have_posts() ) : $my_query->the_post(); $rankcount++; ?>
<?php if ($rankcount == $add_top ): ?>
<?php echo $add_type_top; ?>
<?php endif; ?>
<li>
<a href="<?php the_permalink(); ?>">
<span class="rank-count ranking-count<?php echo $rankcount; ?>">
<?php echo $rankcount; ?>
</span>
<?php if( has_post_thumbnail() ): ?>
<?php the_post_thumbnail('thumb100'); ?>
<?php endif; ?>
<div class="ninki-kiji-text">
<p><?php the_title(); ?></p>
<p><span class="pv-data">
<?php  echo 'アクセス数: ' .  get_post_views(get_the_ID()); ?>
</span></p>
</div>
</a>
</li>
<?php if ($rankcount == $add_bottom ): ?>
<?php echo $add_type_under; ?>
<?php endif; ?>
<?php endwhile; ?>
<?php wp_reset_postdata(); ?>
</ul>
</aside>
<?php echo $after_widget;
}

スタイル CSS

ちなみにcssは下のような感じ。すぐ右を見れば表示されてるはず(PCで閲覧の場合)こんな感じでランキングが表示されているはず。テーマのスタイルシートに貼り付ける。

.ninki-kiji ul {
padding: 0;
list-style: none;
}

.ninki-kiji li {
position: relative;
margin-top: 10px;
padding: 0 5px 10px;
border-bottom: dotted 1px #ddd;
}

.ninki-kiji a {
display: inline-block;
text-decoration: none;
color: #AD3B32;
font-weight: bold;
}

.ninki-kiji a:hover {
background-color: #fafad2;
}

.ninki-kiji a:after {
display: block;
clear: both;
content: '';
}

.rank-count {
position: absolute;
top: 0;
left: 0;
padding: 1px 10px;
color: #fff;
background: rgba(66, 66, 66, .85);
}

.ranking-count1 {
background: rgba(189, 161, 82, .9);
}

.ranking-count2 {
background: rgba(152, 153, 173, .9);
}
.ranking-count3 {
background: rgba(156, 85, 34, .9);
}

.ninki-kiji img {
float: left;
width: 100px;
height: 100px;
}

.ninki-kiji-text {
font-size: .9em;
margin-left: 110px;
line-height:1.5em;
}
.pv-data {
font-size:12px;
font-weight:normal;
color:#333;
}

管理画面のウィジェット設定フォームを出力 form()

/* 管理画面のウィジェット設定フォームを出力 */
public function form($instance) {
?>
<p>
<label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('タイトル:'); ?></label>
<input type="text" class="widefat" id="<?php echo $this->get_field_id('title'); ?>"
name="<?php echo $this->get_field_name('title'); ?>"
value="<?php echo esc_attr( $instance['title'] ); ?>">
</p>
<p>
<label for="<?php echo $this->get_field_id('number'); ?>"><?php _e('記事表示件数:'); ?></label>
<input type="text" id="<?php echo $this->get_field_id('number'); ?>"
name="<?php echo $this->get_field_name('number'); ?>"
value="<?php echo esc_attr( $instance['number'] ); ?>" size="2">
</p>
<?php
}

詳しいことはわからないのでこのまま貼り付けよう。フィールドを追加する場合はそのとき考えましょ。8行目と14行目、esc attr、「alt、value、title などの HTML 要素(特にフォーム値)をエスケープする場合は必ず使用してください」とのことなのでつけましょう。(参照 関数リファレンス/esc attr

追記 ショートコードひとつで広告管理・表示変更後管理画面のウィジェット設定フォームを出力 form() 2018/05/26

/* 管理画面のウィジェット設定フォームを出力 */
public function form($instance) {
?>
<p>
<label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('タイトル:'); ?></label>
<input type="text" class="widefat" id="<?php echo $this->get_field_id('title'); ?>"
name="<?php echo $this->get_field_name('title'); ?>"
value="<?php echo esc_attr( $instance['title'] ); ?>">
</p>
<p>
<label for="<?php echo $this->get_field_id('number'); ?>"><?php _e('記事表示件数:'); ?></label>
<input type="text" id="<?php echo $this->get_field_id('number'); ?>"
name="<?php echo $this->get_field_name('number'); ?>"
value="<?php echo esc_attr( $instance['number'] ); ?>" size="2">
</p>
<p>
<label for="<?php echo $this->get_field_id('add_top'); ?>"><?php _e('N番目の上に広告 N='); ?></label>
<input type="text" id="<?php echo $this->get_field_id('add_top'); ?>"
name="<?php echo $this->get_field_name('add_top'); ?>"
value="<?php echo esc_attr( $instance['add_top'] ); ?>" size="2">
</p>
<p>
<label for="<?php echo $this->get_field_id('add_type1'); ?>"><?php _e('N番目の上に広告のリスト'); ?></label>
<input type="text" id="<?php echo $this->get_field_id('add_type1'); ?>"
name="<?php echo $this->get_field_name('add_type1'); ?>"
value="<?php echo esc_attr( $instance['add_type1'] ); ?>">
</p>
<p>
<label for="<?php echo $this->get_field_id('add_bottom'); ?>"><?php _e('N番目の下に広告 N='); ?></label>
<input type="text" id="<?php echo $this->get_field_id('add_bottom'); ?>"
name="<?php echo $this->get_field_name('add_bottom'); ?>"
value="<?php echo esc_attr( $instance['add_bottom'] ); ?>" size="2">
</p>
<p>
<label for="<?php echo $this->get_field_id('add_type2'); ?>"><?php _e('N番目の下の広告のリスト'); ?></label>
<input type="text" id="<?php echo $this->get_field_id('add_type2'); ?>"
name="<?php echo $this->get_field_name('add_type2'); ?>"
value="<?php echo esc_attr( $instance['add_type2'] ); ?>">
</p>
<?php
}

N番目の上にランダム広告を表示指定・N番目の上に表示する広告リスト指定・N番目の下にランダム広告を表示・N番目の下に表示する広告リスト指定するフォームが追加されている。ウィジェットフォームは下のような感じ。

全記事からの人気記事ウィジェットフォーム
全記事からの人気記事ウィジェットフォーム

ランダム広告リスト(PHPファイル)サンプル

<?php
$add_rand = array(

/* A8.net 賢威 */ 
'<li>
<a href="ここにアフィリエイトリンク" target="_blank" rel="nofollow">
<span class="new-kiji">
PR
</span>
<img width="100" height="100" src="バナー画像URL" alt="" />
<div class="ninki-kiji-text">
<p>賢威 2万人を超えるユーザーが選んだWordPress SEOテンプレート</p>
<p><span class="pv-data">アクセス数: ∞</span></p>
</div>
</a>
</li>',

/* A8.net Z.com WP */ 
'<li>
<a href="ここにアフィリエイトリンク" target="_blank" rel="nofollow">
<span class="new-kiji">
PR
</span>
<img width="100" height="100" src="バナー画像URL" alt="" />
<div class="ninki-kiji-text">
<p>Z.com WP 低価格なのに高速・多機能・高セキュリティ 月額400円(税別)から</p>
<p><span class="pv-data">アクセス数: ∞</span></p>
</div>
</a>
</li>',

);
$add_rand = $add_rand[mt_rand(0, count($add_rand)-1)];
return $add_rand;

2行目、配列を $add_rand に入れとく。配列の中身は ' ' の間に全記事からの人気記事リストの <li>要素 になるように定型された広告を、カンマ , 区切りで入れてある。4行目、18行目のように簡単な広告の説明をコメントアウトして入れておけば広告の管理が少しは楽かも。

33~34行目、実際にリロードされる度にランダムに広告を選んで表示させる処理をしているのがこの部分。$add_rand に入れた配列の要素の数をカウントして、何番目かをランダムに選んで選ばれた配列要素を表示させるという処理をしている。

広告がかぶらない様に配列要素におのおの違う広告を追加して作成した2つのファイルを、例えばlist1.phplist2.php として作成した場合、ウィジェットフォームでlist1とかlist2とかで広告リストの指定をする。

ショートコードやランダム広告リストについては、WordPress ショートコードひとつでランダムバナー広告管理 広告表示 を参照。

ウィジェットオプション データ検証して無害化して安全な値で保存 update()

/* ウィジェットオプション データ検証して無害化して安全な値で保存 */
public function update($new_instance, $old_instance) {
$instance = $old_instance;
$instance['title'] = sanitize_text_field($new_instance['title']);
$instance['number'] = ctype_digit($new_instance['number']) ? $new_instance['number'] : 5;
/* 記事表示件数11以上の場合10件表示 */
if( $instance['number'] == 0 || 10 < $instance['number']){$instance['number'] = 10 ; };
return $instance;
}
}/* class Ninki_Kiji 終わり */

2行目、update()でウィジェットオプションを変更した時にデータ検証して無害化して安全な値で保存する。4行目、sanitize_text_fieldで「ユーザーが入力、またはデータベースから取得した文字列を無害化します」そうです。(参照 関数リファレンス/sanitize text field

5行目ctype_digit、is_numericが多いみたいだけど、小数点以下やマイナスの数値もそのままなので、ctype_digitにした。小数点以下やマイナスの数値、タグやコードが表示記事数のフィールドに入力されたら、5件表示になる。ちなみにis_numericで-1なんて入力してみるとランキングに全投稿が表示される。

7行目、人気ランキング表示記事数の上限を10記事までにしている。ctype_digitで120なんて入力すると120件表示になる。ランキングて10位くらいまででいいんではないかな。ちなみに0と入力すると設定→表示設定で設定した記事件数がランキング表示される。このウィジェットの場合、0と入力っすると10件表示となる。まあ、個人で運営しているWordPressならデータの無害化 (サニタイズ)にそんなに気を配る必要があるのかは疑問だけど。

追記 ショートコードひとつで広告管理・表示変更後ウィジェットオプション データ検証して無害化して安全な値で保存 update() 2018/05/26

/* ウィジェットオプション データ検証して無害化して安全な値で保存 */
public function update($new_instance, $old_instance) {
$instance = $old_instance;
$instance['title'] = sanitize_text_field($new_instance['title']);
$instance['add_type1'] = sanitize_text_field($new_instance['add_type1']);
$instance['add_type2'] = sanitize_text_field($new_instance['add_type2']);
$instance['number'] = ctype_digit($new_instance['number']) ? $new_instance['number'] : 5;
/* 記事表示件数11以上の場合10件表示 */
if( $instance['number'] == 0 || 10 < $instance['number']){$instance['number'] = 10 ; };
/* 上広告未入力(0入力)で表示なし */
$instance['add_top'] = ctype_digit($new_instance['add_top']) ? $new_instance['add_top'] : 0;
/* 上広告記事表示件数より大きい場合一番上に */
if( $instance['add_top'] > $instance['number'] ){$instance['add_top'] = 1 ; };
/* 下広告未入力(0入力)で表示なし */
$instance['add_bottom'] = ctype_digit($new_instance['add_bottom']) ? $new_instance['add_bottom'] : 0;
/* 下広告記事表示件数より大きい場合一番下に */
if( $instance['add_bottom'] > $instance['number'] ){$instance['add_bottom'] =  $instance['number'] ; };
return $instance;
}
}/* class Ninki_Kiji 終わり */

N番目指定なし(0入力)でランダム広告を表示しない。人気記事表示件数よりN番目が大きい場合、上広告は一番上に下広告は一番下に挿入される。

ウィジェットの登録処理 PHP 5.3以上 widgets_init

/* ウィジェットの登録処理 */
add_action( 'widgets_init', function() {
     register_widget( 'Ninki_Kiji' );
});

PHP 5.3以上の場合、上のように書けばウィジェットの登録ができる。

以上、css以外の細切れになった5つのコードをfunctions.phpに追加すれば、人気記事ランキングウィジェットが、外観→ウィジェットの管理画面に現れる。

人気記事ランキングウィジェット
人気記事ランキングウィジェット

人気記事ランキングウィジェットを、Luxeritas(ルクセリタス)の、スクロール追従サイドバーウィジェットエリアに挿入して表示させている。

さて次回プラグインなしで人気記事一覧ウィジェット化4回目は、個別投稿ページ内にその記事が属するカテゴリーでの人気記事ランキングを表示するウィジェット、カテゴリー別人気記事ランキングウィジェットの作成・登録についての備忘録。

プラグインなしで人気記事一覧ウィジェット化1回目と2回目、PV計測保存とPV取得する関数コードやwp_schedule_eventをつかって擬似cron的に月初めにページビューをクリアする方法については下のページで説明してる。