やりたいこと
Welcartの商品一覧ページは、デフォルトでは投稿日時の降順。新しく登録した順番に表示されます。(ワードプレスのデフォルトどおり)
この並び順を、閲覧者がドロップダウンリストで選択して変更できるようします。
選択項目は「新着順(デフォルト)」 「価格が安い順」 「価格が高い順」の3つ。
調べてみると既出の方法は、いずれも、並び替え用の価格を独自カスタムフィールドに保存するやり方でした。
Welcart が標準で持つSKUの価格情報とは別にです。
つまり、同じ価格を2回入力する必要があります。
メンテナンスが面倒ですし、間違いの素になるでしょう。
そこで、SKU情報から一発で価格データを取得して、その値で並び替えるようにしました。
二度手間をなくし、データ量とデータベースへの問合せを減らすことができます。
概要
- 商品一覧ページ(category.php)に並び替え条件を選択するselectボックスを設置
- select optionのvalueに移動先ページのURLを設定
- 選択optionが切り替わると、valueに設定されたURLをlocation.hrefに代入してページ移動させる
- 移動先ページでは、urlに付けられたクエリ文字列に応じて商品を並び替える
- 並び替えは、WP_Query 内部で発行されるSQL文のORDER BY句を直接変更することで行う
- 並び替える価格の値は、Welcart が標準で持つSKUデータからMySQL関数を使って一発で取得する
コード
category.php
selectボックスを設置します。
記載場所はwordpressループの開始直前。
if ( have_posts() )とwhile ( have_posts() )の間です。
使用するテーマによっては、記載ファイルがitem_category.phpの場合もあります。
if ( have_posts() ) :
$sortset = (string)filter_input( INPUT_GET, 'sort' ); //$_GET['sort']の値を取得
?>
<div class="">並び替え <!-- 送信しないのでformタグでなくてもOK -->
<!-- 選択項目が切り替わるとそのvalue属性を読み取り、location.hrefに代入(ページ移動させる) -->
<select name="item_sort" onChange="location.href=value;">
<option value="<?php echo esc_url( add_query_arg( array( 'sort' => false ), get_pagenum_link() ) ); ?>"<?php if ( ! $sortset ) echo ' selected'; ?>>新着順</option>
<option value="<?php echo esc_url( add_query_arg( array( 'sort' => 'price_asc' ), get_pagenum_link() ) ); ?>"<?php if ( $sortset && $sortset === 'price_asc' ) echo ' selected'; ?>>価格が安い順</option>
<option value="<?php echo esc_url( add_query_arg( array( 'sort' => 'price_desc' ), get_pagenum_link() ) ); ?>"<?php if ( $sortset && $sortset === 'price_desc' ) echo ' selected'; ?>>価格が高い順</option>
</select>
</div>
<div class="item-archive">
<?php
while ( have_posts() ) : the_post(); //ループスタート
1番目のoptionはデフォルトで表示する新着順です。
add_query_arg()関数は、keyの値をfalseに設定するとURLにクエリ文字列が付きません。
デフォルトではURLパラメーターは付けないのでfalseにします。
functions.php
/**
* フックのあるfile : wp-includes/class-wp-query.php
* フックのあるfunction : get_posts
* @param string $orderby ORDER BY句
* @param WP_Query $query オブジェクト
* @return string $orderby ORDER BY句
*/
add_filter( 'posts_orderby', function( $orderby, $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return $orderby; //デフォのORDER BY句をreturnしないとサブループ等で正しく並び替えできなくなる
}
//カテゴリーページの場合、ORDER BY句を変更する
if ( $query->is_category() ) {
global $wpdb;
$sort = 'DESC'; //デフォの並び替えは降順(高い順)
if ( isset( $_GET['sort'] ) ) { //urlパラメーターに?sort=があれば価格順に
if ( $_GET['sort'] === 'price_asc' ) { //urlパラメーターがprice_ascなら
$sort = 'ASC'; //昇順(安い順)に変更
}
//priceの値をピンポイントで取得してその値でORDER BY句を生成する
$orderby = '(
SUBSTRING_INDEX(
SUBSTRING_INDEX(
SUBSTRING(
'. $wpdb->prefix. 'postmeta.meta_value,
( INSTR(
'. $wpdb->prefix. 'postmeta.meta_value,
CONCAT( "\"", "price", "\";" )
) + CHAR_LENGTH( "price" ) + 2
)
),
"\"",
2
),
"\"",
-1
) + 0
) '. $sort; //$sortの値はurlパラメーターにより'DESC'or'ASC'可変
} else { //urlパラメーターに?sort=がなければ
$orderby = $wpdb->prefix. 'posts.post_date DESC'; //新着順(デフォルト)
}
}
return $orderby;
}, 10, 2 );
27~41行目:SKUデータから価格の値だけをピンポイントで取得するSQL文です。
詳しい説明は次のページに投稿しています。
wordpressのシリアライズデータから目的の値をピンポイントで取得する(MySQL関数使用)
42行目:取得したpriceの値に+0を加算しているのは数値型に変換するため。
SUBSTRING_INDEX関数で取得した値は文字列型。
数値型にしないと数字で正しく並び替えされません。
ポイント
WP_Queryのパラメーターを設定する通常の方法では実現できない
Welcartの価格はsku情報に保存されています。
在庫数・在庫状態等と一緒に配列のシリアライズデータとしてです。
a:9:{s:4:"code";s:9:"WDP-CI-TE";s:4:"name";s:17:"チーク(TE)";s:6:"cprice";s:4:"2640";s:5:"price";s:4:"1500";s:4:"unit";s:0:"";s:8:"stocknum";s:3:"100";s:5:"stock";s:1:"0";s:2:"gp";s:1:"0";s:4:"sort";s:1:"0";}
保存場所はカスタムフィールド(postmetaテーブル)。
通常(値が単体)なら、WP_Queryのorderby, meta_key, meta_value等のパラメーターを設定することで並び替えできます。
ですが、上記データのように価格だけの単体データではないので、そのままでは並び替えできません。
posts_orderbyフィルターフックで並び替え条件を変更
WP_Queryの 内部で発行されるSQL文を直接変更する必要があります。
変更するのは並び替え。
なので、ORDER BY句に対するposts_orderbyフィルターフックを使って並び替え条件を書き換えます。
WelcartのSKUシリアライズデータからピンポイントで価格だけ取得
MySQLの関数を使います。
これによりピンポイントで価格の値を取得し、その値で並び替えできます。
wordpressのシリアライズデータから目的の値をピンポイントで取得する(MySQL関数使用)
add_query_arg()関数 でURLパラメーターを付与・削除
移動先ページのURLを取得するのはadd_query_arg()関数を使います。
URLにクエリ文字列を付けたり消したりする関数です。
関数リファレンス / add_query_arg – WordPress Codex 日本語版
コメントを残す