welcart sort product

アーカイブページのループに在庫や価格でのソート・絞り込みを反映させる。

在庫がないのに商品が表示されているのは楽●の空売りみたいで鬱陶しい。商品数が多ければなおのこと。ところが、通常のqueryにwelcartの在庫を 反映させることはできません。一旦ループから離脱して再ループさせれば擬似的に実現できるが、ページ送りやレイアウト崩れの可能性を考えるとテンプレート ごとに細かな対応が必要で面倒臭い。

で、困った時のウェルカスタムさんで探したところ「queryに在庫情報を組み込む現実的な解決法」が掲載されていた。ここによく纏められているように、在庫状態ではなく在庫数で判別しているので縛りがあること、また、複数SKUを持つ商品の場合の処理など、ウェルカートのSKUがシリアライズされているので、価格でのソートなども含めてクエリでの絞り込みの実現は難しそうだ。

ということで、現状はカスタムフィールドにデータを持たせるのが唯一の解決策と思われたので、最終的に以下のようにカスタマイズすることにしました。

各商品記事のカスタムフィールドに必要な情報を書き込む処理

ソートすることを目的にするなら、独立したカスタムフィールドにクエリから参照しやすように数値で登録されていることがベストでしょう。

まずは、以下のようにsave_postにフックしてにソート用のカスタムフィールドを書き込むことにしました。ついでに usces_action_reg_orderdataという受注確定後に処理されるフックがあったので、それにも引っ掛けることにする。 usces_have_skus()はループ内で複数回使うとだめだったりと、扱いが難しいので、get_skusでpostIDからskuのデータ をすべて取得、直接在庫の有無を呼び出す方法を採用、skuを複数展開されていても大丈夫なように、ステータスを一旦配列に入れてから最小の値だけ取り出 すことにします。これで、在庫がなくなったら反映されるという基本要件を満たしてくれます。ちなみに最後のcurrent_screenへのフックはというと、商品マスターの「最新の情報に更新」ボタンを押すとすべての商品データのカスタムフィールドを設定してくれる、という我ながらなんという親切設計!!

/*
* WELCART PRODUCT SORT
* Plugin URI: //web.contempo.jp/weblog/tips/p5438
* Author: Mizuho Ogino 
* Version: 1.0
* License: //www.gnu.org/licenses/gpl.html GPL v2 or later
*/

add_action( 'save_post', 'usces_update_postmeta_for_sorting', 10 );
function usces_update_postmeta_for_sorting( $post_id ){ // 各カスタムフィールドの更新
    if ( !get_post_meta( $post_id, '_itemCode', true ) ) return;
    global $usces;
    $_itemStock = $_itemPrice = array();
    $skus = $usces->get_skus( $post_id );
    if ( $skus ): foreach ( $skus as $sku ): 
        $_itemStock[] = (int)$sku['stock'];
        if ((int)$sku['stock'] == 4 ) continue; // 廃盤を回避
        $_itemPrice[] = (int)$sku['price']; 
    endforeach; endif;
    if ( $_itemStock ) {
        update_post_meta( $post_id, '_itemStock', min( $_itemStock ) ); // 0:在庫あり 1:在庫僅少 2:売り切れ 3:入荷待ち 4:廃盤 の中で一番低いステータス
    }
    if ( $_itemPrice ) {
        update_post_meta( $post_id, '_itemPriceMin', min( $_itemPrice ) ); // SKU中の一番安い設定価格
        update_post_meta( $post_id, '_itemPriceMax', max( $_itemPrice ) ); // SKU中の一番高い設定価格
    }

    global $wpdb;
    $sold = 0;
    $datestr = substr(get_date_from_gmt(gmdate('Y-m-d H:i:s', time())), 0, 10);
    $yearstr = substr($datestr, 0, 4);
    $monthstr = substr($datestr, 5, 2);
    $daystr = substr($datestr, 8, 2);
    $order_table_name = $wpdb->prefix . "usces_order";
    $order_date = date('Y-m-d H:i:s', mktime(0, 0, 0, (int)$monthstr, ((int)$daystr-30), (int)$yearstr)); // 30日分のDBを検索
    $query = "SELECT order_cart FROM {$order_table_name} WHERE order_date >= '{$order_date}'";
    $dbres = $wpdb->get_col($query);
    if( $dbres ): foreach( (array)$dbres as $carts ):
        $rows = unserialize($carts);
        foreach( (array)$rows as $carts ){
            if ( $post_id == $carts['post_id'] ) $sold = $sold + $carts['quantity'];
        }
    endforeach; endif;
    update_post_meta( $post_id, '_itemPopular', $sold ); // 売上数を登録

    return $post_id;
}

add_action( 'usces_action_reg_orderdata', 'usces_action_reg_orderdata_update_postmeta' );
function usces_action_reg_orderdata_update_postmeta( $args ){ // 在庫に変動があればカスタムフィールドを更新
    extract( $args );
    foreach( $cart as $cartrow ){
        $post_id = $cartrow['post_id'];
        usces_update_postmeta_for_sorting( $post_id );
    }
}

add_action( 'current_screen', 'usces_add_actions_to_admin_current_screen' );
function usces_add_actions_to_admin_current_screen( $current_screen ){ // 「最新の情報に更新」ボタンですべてのカスタムフィールドを更新
    if( isset($_GET['page']) && 'usces_itemedit' == $_GET['page'] && isset($_REQUEST['refresh']) ){
        $get_posts = get_posts( array( 'meta_key' => '_itemCode', 'post_type' => 'post', 'numberposts' => -1 ) );
        if( $get_posts ): foreach ( $get_posts as $val ) : 
            usces_update_postmeta_for_sorting( $val->ID );
        endforeach; endif; 
    }
}

welcartにはget_bestseller_idsという関数があったので、当初は「人気順」もダイレクトにベストセラーの順位を登録するように設計してみました。しかしショップサイトの問題点として、「価格」や「在庫」が絶対的な情報であるのに対して、商品の「人気順」が相対的な数値ということがあります。つまり1点売り上げがあるとすべての順位に変動が生じてしまうので、一々すべての商品のカスタムフィールドを更新しなくてはならなくなり、延いてはユーザーサイドの表示速度に影響が出る可能性もあります。wp_cronなどを用いて定期更新にする手も考えましたが、それでも二段階更新になることには変わりないので、人気順の代わりにbestseller_idsも参照している1ヶ月の「売上数」をカウントしてカスタムフィールドに登録することにしました。これならば、売上があった際に該当商品を変更するだけで済みます。(とはいえ、在庫数の変動や記事の更新時にカウントされているので、しばらく売上がない商品は更新されないので完全とは言えない。厳密さを求める向きは手動反映かcronの設置をどうぞ。)

アーカイブページ内での商品一覧のソート処理

ループ全般にフックしてくれるpre_get_postという便利な関数を使用します。$_GET[‘instock’]で在庫の有無を、$_GET[‘sort’]で並び順を変更するという仕様。デフォルトで在庫がないものは通常ループ内で非表示に設定してあります。

つまり、セレクトボックスなどで、カテゴリページのリンクに以下の様に絞り込み用のクエリを設定してやれば、ループが自動的に読み込まれるわけです。例としては以下のようになります。

価格の安い順:
//www.example.com/category/item?sort=price_desc

価格の高い順:
//www.example.com/category/item?sort=price_asc

価格高い順、かつ在庫のない商品もすべて表示:
//www.example.com/category/item?sort=price_asc&instock=false

 

add_action( 'pre_get_posts', 'usces_custom_sort_item', 10, 1 );
function usces_custom_sort_item( $query ) {
    if ( is_admin() || !$query->is_main_query() ) return;
    if ( $query->is_search ) {
        set_query_var('post_type', 'post');
    }
    if ( $query->is_category() || $query->is_tag() ) {
        $args = array();
        $sort = isset($_GET['sort']) && is_string($_GET['sort']) ? $_GET['sort'] : '';
        if ( $sort ){
            if ( $sort == 'price_asc' ) { //価格の安い順 'price_asc'の値は任意に変更可
                $query->set( 'meta_key', '_itemPriceMin' );
                $query->set( 'orderby', array( 'meta_value_num' => 'ASC', 'date' => 'DESC' ) );
            } elseif ( $sort == 'price_desc' ) { //価格の高い順 'price_desc'の値は任意に変更可
                $query->set( 'meta_key', '_itemPriceMax' );
                $query->set( 'orderby', array( 'meta_value_num' => 'DESC', 'date' => 'DESC' ) );
            } elseif ( $sort == 'popular' ) { //人気順(一定期間の売上数の多い順) 'popular'の値は任意に変更可
                $query->set( 'meta_key', '_itemPopular' );
                $query->set( 'orderby', array( 'meta_value_num' => 'DESC', 'date' => 'DESC' ) );
            }
        }
        $instock = isset($_GET['instock']) && is_string($_GET['instock']) ? $_GET['instock'] : '';
        if ( $instock !== 'false' ) { //在庫の有無で抽出 'false'の値は任意に変更可
            $query->set( 
                'meta_query' , array(
                    'relation' => 'OR',
                    array(
                        'key' => '_itemStock',
                        'value' => array( 0, 1 ),
                        'compare' => 'IN',
                    ),
                    array(
                        'key' => '_itemStock',
                        'compare' => 'NOT EXISTS',
                    ),
                    $args
                )
            );
        } elseif ($args ) {
            $query->set( $args );
        }
    }
}

add_filter( 'paginate_links', 'usces_append_query_string', 10 );
add_filter( 'term_link', 'usces_append_query_string', 10 );
function usces_append_query_string( $url ) {// get_term_link // paginate_links のクエリ書き換え
    if ( isset( $_GET['instock'] ) ) $url = add_query_arg( 'instock', $_GET['instock'], $url );
    if ( isset( $_GET['sort'] ) ) $url = add_query_arg( 'sort', $_GET['sort'], $url );
    return $url;
}

言わずもがなですが、在庫がない場合 if ( $instock !== ‘false’ ) のところを if ( $instock === ‘true’ ) とかにしてやれば、trueが設定されている時に「在庫あり」のみを表示(在庫なしは表示しない)というように反転できます。

paginate_linksはその名の通りページネーションやカテゴリのリンク出力にフックしてくれるフィルターで、ページを送ったらソートが解除されると困るので設定してあります。term_linkはカテゴリを選択していってもクエリを継続させます。絞り込み系のサイト構成に向いています。入らなければコメントアウトしましょう。

 

リンクの実際の出力方法など

並べ替えボタンの出力例も書いておきます。

<?php
    $current_url = (empty($_SERVER["HTTPS"]) ? "http://" : "https://") . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
    $sort = isset($_GET['sort']) && is_string($_GET['sort']) ? $_GET['sort'] : '';
    foreach ( array( 'date'=>'新着順','price_asc'=>'安い順','price_desc'=>'高い順','popular'=>'人気順' ) as $key => $val ):
        $sort_option[ $key ] = "\t\t\t" .'<a class="sort-order'. ( $sort == $key ? ' selected': '' ).'" value="'.$key.'" href="'.( $key == 'date' ? remove_query_arg( 'sort', $current_url ) : add_query_arg( 'sort', $key, $current_url ) ).'" data-target="'.$key.'">' .$val. '</a>'. "\n";
    endforeach;
    $instock = isset($_GET['instock']) && is_string($_GET['instock']) ? $_GET['instock'] : '';
    foreach ( array( 'true'=>'在庫あり','false'=>'すべて表示' ) as $key => $val ):
        $sort_option[ $key ] = "\t\t\t" .'<a class="sort-order'. ( $sort == $key ? ' selected': '' ).'" value="'.$key.'" href="'.( $key == 'date' ? remove_query_arg( 'sort', $current_url ) : add_query_arg( 'sort', $key, $current_url ) ).'" data-target="'.$key.'">' .$val. '</a>'. "\n";
    endforeach;
?>

<select class="sort-group">
    <option value="date" data-url="<?php echo remove_query_arg( 'sort', $current_url ); ?>"<?php echo ( !$sort ? ' selected': '' ); ?>>新着順</option>
    <option value="price_asc" data-url="<?php echo add_query_arg( 'sort', 'price_asc', $current_url ); ?>"<?php echo ( $sort == 'price_asc' ? ' selected': '' ); ?>>価格の安い順</option>
    <option value="price_desc" data-url="<?php echo add_query_arg( 'sort', 'price_desc', $current_url ); ?>"<?php echo ( $sort == 'price_desc' ? ' selected': '' ); ?>>価格の高い順</option>
    <option value="popular" data-url="<?php echo add_query_arg( 'sort', 'popular', $current_url ); ?>"<?php echo ( $sort == 'popular' ? ' selected': '' ); ?>>人気順</option>
</select>
<select class="instock-group">
    <option value="true" data-url="<?php echo remove_query_arg( 'instock', $current_url ); ?>">在庫あり</option>
    <option value="false" data-url="<?php echo add_query_arg( 'instock', 'true', $current_url ); ?>">すべて表示</option>
</select>
<script type="text/javascript" charset="utf-8">
    jQuery( 'select.sort-group, select.instock-group' ).change(function() {
        window.location = jQuery( this ).find( 'option:selected' ).data( 'url' );
    });
</script>

sc 2015-05-12 19.19.04

ちゃんとcsvダウンロードして、手元で売上げやら何やら管理してれば割とどうでも良いかもですが…、やっぱりウェブ上でも見れた方が良いよね! …というときに。

以下の関数をfunctions.phpに記述します。アンチョコはusceshop.class.php より、get_order_num() & get_order_amount() の両関数。usces_action_admintop_box2にフックしています。「受注数・金額」と「商品登録情報」の間に表示したい場合は、usces_action_admintop_box1にフックして下さいな。

(言うまでもありませんが、画像の数字はダミーです。)

 

code: transition of amount of 12 months


/////////////////////// 受注金額リスト /////////////////////// 

add_action( 'usces_action_admintop_box2', 'amount_of_12_months', 10 );
function amount_of_12_months (){
    global $wpdb;
    $datestr = substr(get_date_from_gmt(gmdate('Y-m-d H:i:s', time())), 0, 10);
    $yearstr = substr($datestr, 0, 4);
    $monthstr = substr($datestr, 5, 2);
    $daystr = substr($datestr, 8, 2);
?>
<h4>過去の受注数・金額</h4>
<div class="usces_box">
<table class="dashboard">
<tr>
<th><?php _e('Currency','usces'); ?> : <?php usces_crcode(); ?></th><th><?php _e('number of order', 'usces'); ?></th><th><?php _e('amount of order', 'usces'); ?></th>
</tr>
<?php 
    
    for ($num = 0; $num < 12; $num++){
        $date = date('Y年n月', mktime(0, 0, 0, (int)$monthstr+$num, 1, (int)$yearstr-1));
        $startdate = date('Y-m-01 00:00:00', mktime(0, 0, 0, (int)$monthstr+$num, 1, (int)$yearstr-1));
        $enddate = date('Y-m-d 23:59:59', mktime(0, 0, 0, (int)$monthstr+1+$num, 0, (int)$yearstr-1));
        $table_name = $wpdb->prefix . 'usces_order';    
        $query = $wpdb->prepare("SELECT COUNT(ID) AS ct FROM $table_name WHERE order_date >= %s AND order_date <= %s AND 0 = LOCATE(%s, order_status) AND 0 = LOCATE(%s, order_status)", $startdate, $enddate, 'cancel', 'estimate');
        $number = $wpdb->get_var($query);

        $query = $wpdb->prepare("SELECT SUM(order_item_total_price) AS price, SUM(order_usedpoint) AS point, SUM(order_discount) AS discount, SUM(order_shipping_charge) AS shipping, SUM(order_cod_fee) AS cod, SUM(order_tax) AS tax 
                                 FROM $table_name WHERE order_date >= %s AND order_date <= %s AND 0 = LOCATE(%s, order_status) AND 0 = LOCATE(%s, order_status)", $startdate, $enddate, 'cancel', 'estimate');
        $res = $wpdb->get_row($query, ARRAY_A);
        if( $res !== NULL ){
            $amount = $res['price'] - $res['point'] + $res['discount'] + $res['shipping'] + $res['cod'] + $res['tax'];
            echo '<tr><td>'.$date.' : </td><td class="bignum">' .number_format($number). '</td><td class="bignum">'.usces_crform( $amount, true, false, 'return' ). '</td></tr>'."n";
        }
    }
?>
</table>
</div>
<?php 
}

welcartネタで細かいカスタマイズをひとつ。

welcartの配送希望日の表示がずらっと数字の羅列でちょっと見にくい。曜日とか欲しい。ついでに土日、平日指定なんかも欲しい。

sc-2015-03-05-22.34

 

/////////////////////// 配送希望日のカスタマイズ /////////////////////// 

add_filter( 'usces_delivery_after_days_script', 'my_usces_delivery_after_days_script' );
function my_usces_delivery_after_days_script( $delivery_after_days_script ){
return $delivery_after_days_script = "
 option += '<option value=\"".__('No preference', 'usces')."\">指定しない(一番早い営業日に送付)</option>';
 option += '<option style=\"background-color: #FFF0F5;\" value=\"土日\">土日</option>';
 option += '<option style=\"background-color: #F0FFF1;\" value=\"平日\">平日</option>';
 for(var i = 0; i < delivery_after_days; i++) {
 var newdate = new Date( date[\"year\"]+\"/\"+date[\"month\"]+\"/\"+date[\"day\"] ),
 getday = newdate.getDay(),
 daystyle = '',
 weekday = [\"日\",\"月\",\"火\",\"水\",\"木\",\"金\",\"土\"];
 date_str = date[\"year\"]+\"年\"+parseInt(date[\"month\"])+\"月\"+parseInt(date[\"day\"])+\"日(\"+weekday[getday]+\")\";
 if ( getday == 0 ){
 daystyle = 'background-color: #FFF0F5;'; // 日曜日
 } else if ( getday == 6 ) {
 daystyle = 'background-color: #F0F8FF;'; // 土曜日
 }
 if(date_str == selected_delivery_date) {
 option += '<option style=\"' + daystyle + '\" value=\"' + date_str + '\" selected>' + date_str + '</option>';
 } else {
 option += '<option style=\"' + daystyle + '\" value=\"' + date_str + '\">' + date_str + '</option>';
 }
 date = addDate(date[\"year\"], date[\"month\"], date[\"day\"], 1);
 }";
}

add_filter( 'usces_filter_order_edit_delivery_days_select', 'my_usces_filter_order_edit_delivery_days_select', 10, 3 );
function my_usces_filter_order_edit_delivery_days_select( $delivery_days_select, $data, $delivery_after_days ){
 $delivery_days_select = '<option value="'.__('Non-request', 'usces').'">指定しない(一番早い営業日に送付)</option>';
 $value = '土日';
 $selected = (isset($data['order_delivery_date']) && $data['order_delivery_date'] == $value) ? ' selected="selected"' : '';
 $delivery_days_select .= '<option value="'.$value.'"'.$selected.'>'.$value.'</option>';
 $value = '平日';
 $selected = (isset($data['order_delivery_date']) && $data['order_delivery_date'] == $value) ? ' selected="selected"' : '';
 $delivery_days_select .= '<option value="'.$value.'"'.$selected.'>'.$value.'</option>';
 $data_order_date = explode(" ", $data['order_date']);
 $order_date = explode("-", $data_order_date[0]);
 for($i = 0; $i < $delivery_after_days; $i++) {
 $timestamp = mktime(0,0,0,$order_date[1],$order_date[2]+$i,$order_date[0]);
 $weekday = array( "日", "月", "火", "水", "木", "金", "土" );
 $value = date( 'Y年n月j日', $timestamp ).'('.$weekday[date( 'w', $timestamp )].')';
 $selected = (isset($data['order_delivery_date']) && $data['order_delivery_date'] == $value) ? ' selected="selected"' : '';
 $delivery_days_select .= '<option value="'.$value.'"'.$selected.'>'.$value.'</option>';
 }
 return $delivery_days_select;
}

以上をfunctions.phpに記載して下さい。「usces_delivery_after_days_script」フックはカート画面の表記を変更します。「usces_filter_order_edit_delivery_days_select」フックは受注リスト内の配送希望日セレクタを変更しています。こういう細部のフックもきっちと用意されている当たり、welcartはカスタマイズすることを考えて設計されているんだな、と関心させられますね。

welcartの「基本設定」ページ内の「ショップ設定」には、「振込先口座情報」を書く欄があります。同じく「基本設定」ページ内の「支払方法」で決済種別「振込」を選ぶと購入完了時に送られるサンキューメールに挿入される情報ですね。しかしこれ、「銀行振込」「郵便振替」「後払い」等々複数の決済方法がある場合は、改行してだらだらと書き連ねることになるから、たいへん可読性が悪い。

ウェブショップの要は一にも二にもユーザビリティ!! というわけで、お客さんが「支払方法」で選んだ特定の「振込」方法と同じ「振込先口座情報」だけをメールに表示しよう、というわけです。

で、usces_filter_mail_transfereeというフックを使いまして、ちょっと無理くりなフィルタリングで実現したのが以下。

 

functions.phpにフィルターを設置

<?php
// 受注メール内容(振込先口座情報)のフィルタリング
add_filter('usces_filter_mail_transferee', 'my_filter_mail_transferee',10,2);
function my_filter_mail_transferee( $transferee, $payment ){
   $paymentname = preg_quote( $payment['name'] );
   $pattern = "/<$paymentname>(.*?)</$paymentname>/su";
   preg_match_all($pattern, $transferee, $matches, PREG_SET_ORDER);
   if ($matches):
       $new_transferee = '';
       foreach ($matches as $match) $new_transferee .= $match[1]."n";
       $transferee = $new_transferee;
   endif;
   return $transferee;
}
?>

※ もろもろ表記がまずかったのでエスケープや条件分岐などを加えて書き直しました。(2015/11/06)

 

振込先口座情報の内容を下記の方法で分割

「支払方法」と同じ名称をタグ括弧でくくり表示した情報をマークアップします。

<支払い名>
メールに出したい情報
</支払い名>

これでマークアップされている箇所のみがサンキューメールに記載されます。


以上です。何ぶんウェブショップの肝の箇所なので、ご使用の際は充分にテストを重ねてから自己責任でお試しください

wordpressの先祖&子孫タクソノミを取得する関数

wordpressを使い始めたころはカスタム投稿というものがなく、ブログもニュースも商品もカテゴリだけで分岐させていたから、カテゴリツリーは複雑な構成になりがちでした。カテゴリスラッグでCSSを充てたくて、子孫カテゴリや先祖カテゴリを調べる関数を見よう見まねで作りました。カスタムタクソノミに対応するように書き直したりしたものの、そもそも階層の深いカテゴリを使わなくなってしまい、お蔵入りに。カテゴリを複雑にするよりも、カスタム投稿タイプのが分かりよいからです。

で、welcart関連のカスタマイズを調べていたら【プラグインなしで直近のカテゴリーだけから関連商品(記事)を表示させる方法】という記事で、最下層カテゴリを取得するために子孫カテゴリーを持たないカテゴリーだけを抽出するくだりに行き当たりました。ネットショップだとカテゴリが複雑になりがちだから、まだ使えるかもしれない。(そんな大きな規模のネットショップとか作りたくないですけどね。)というわけで、何かの役に立つこともあるかと思いなおし、備忘録も兼ねてまとめておくことにしました。

(実用的な応用として、bodyクラスに投稿/カテゴリの所属スラッグをすべて付与する方法も書きましたのでどうぞ。)

 


 

子孫カテゴリを取得する

記事が所属するカテゴリのうち最下層のものだけを取得する

よくあるケースだと、同一カテゴリツリーの階層を複数選択している場合があります。丁寧なユーザーは中間カテゴリや最上位カテゴリにもチェックを入れてしまうことがあるからです。この場合、記事内でget_categoryしてもどのカテゴリが取得されるか分かりません。なので、記事が所属するカテゴリのうち最下層のものだけを取得するようにしてやります。

ちなみに複数ツリーに分岐してセレクトされているときは…

複数ツリーに分岐してカテゴリがセレクトされている場合は、最下層のカテゴリを配列で取得します。

cat_sample2_2

下記関数群をfunctions.phpに記載します

function get_term_descendants ( $post_id ='', $tax_name = 'category' ) {
  $terms = get_the_terms( $post_id, $tax_name );
  if ( empty( $terms )) return false; 
  $candidate = $terms;
  $count = count( $terms );
  if ( $count > 1 ): foreach( $terms as $key => $term ):
    foreach( $terms as $term2 ):
      if ( term_is_ancestor_of( $term->term_id, $term2->term_id, $tax_name ) ) { // 比較対象IDが親系の場合
        unset( $candidate[$key] ); // 比較対象IDを配列から削除
        break;
     }
    endforeach;
  endforeach; endif;
  return $candidate;
}

 

出力の仕方

get_term_descendants( $post->ID, ‘カスタムタクソノミ名’ )とすると、該当カテゴリの配列が返ってきます。カスタムタクソノミ名をなしにすると、通常のカテゴリが出力されます。

$descends = get_term_descendants(); 
if ( $descends ): foreach ( $descends as $descend ):
echo '<!-- タームID:'. $descend->term_id .' ターム名:'. esc_html( $descend->name ) .' タームスラッグ:'. $descend->slug .'-->'. "n";
endforeach; endif;

 

先祖カテゴリを取得する

最上層のカテゴリIDやスラッグを取得する場合

今度は逆に、カテゴリページを表示しているときに、その一番上の階層のカテゴリIDやスラッグを取得したいときがあります。カテゴリ毎にCSSでデザインを変更するときなどですね。(2015/02/04 … get_ancestorsという大変便利な関数の存在をスルーしていたので、シンプルに書き直しました。)

cat_sample3

関数は二組セットでget_term_ancestor_by_term()はカテゴリから祖先カテゴリを求める関数、get_term_ancestor()は投稿ループの中から所属カテゴリを取得するところからスタートし、関数内でget_term_ancestor_by_term()を呼び出しています。

下記関数群をfunctions.phpに記載します

function get_term_ancestor_by_term ( $sbjcat = '', $tax_name = 'category' ) { 
// [$sbjcat = カテゴリIDまたはスラッグ],
// [$tax_name = タクソノミ名]
    if ( empty( $sbjcat ) && is_category() ) {
        $sbjcat = get_query_var( 'cat' ); //カテゴリページ
    } elseif ( empty( $sbjcat ) && is_tax() ) {
        $sbjcat = get_query_var( 'term_id' );
        $tax_name = get_query_var('taxonomy'); //カスタムタクソノミページ
    } elseif ( !is_numeric( $sbjcat ) ) {
        $sbjcat = get_term_by( 'slug', $sbjcat, $tax_name )->term_id; //スラッグからIDを取得
    }
    if ( !$sbjcat ) return false; //カテゴリ取得不可

    $ancestors = array_reverse( get_ancestors( $sbjcat , $tax_name ) ); 
    if( isset( $ancestors[0] ) ) $ancestor_id = $ancestors[0]; else $ancestor_id = $sbjcat;
    return get_term( $ancestor_id , $tax_name );
}

function get_term_ancestor ( $post_id = '', $extree = '', $tax_name = 'category' ) { 
// [$post_id = 投稿ID],
// [$extree = 含めないカテゴリツリーのIDまたはスラッグ],
// [$tax_name = タクソノミ名]
    if ( empty ( $post_id ) ) $post_id = get_post()->ID;
    if ( $extree && !is_numeric( $extree ) ) $extree = get_term_by( 'slug', $extree, $tax_name )->term_id; 
    $terms = get_the_terms( $post_id, $tax_name );
    if ($terms) : foreach ( $terms as $current_term ) : 
        if ( $current_term->term_id !== $extree && !isset( $tia ) ) { // $extreeが親ではない場合
            $req_id = $current_term->term_id;
            break;
        }
    endforeach; endif;
    if ( $req_id ) return get_term_ancestor_by_term( $req_id, $tax_name );
    else return false;
}

 

 

カテゴリから先祖カテゴリを取得する場合

祖先は一つしか返さない仕様になっている(複数の祖先を求めるケースは考えにくいため)のでループする必要はありません。第二引数にカスタムタクソノミ名(省略時はカテゴリ)を指定できます。

$ancestor = get_term_ancestor_by_term ( get_query_var('cat') );
echo '<!-- タームID:'. $ancestor->term_id .' ターム名:'. esc_html( $ancestor->name ) .' タームスラッグ:'. $ancestor->slug .'-->';

 

投稿から先祖カテゴリを取得する場合

ほぼ同様です。ループの必要はありません。

$ancestor = get_term_ancestor( $post->ID );
echo '<!-- タームID:'. $ancestor->term_id .' ターム名:'. esc_html( $ancestor->name ) .' タームスラッグ:'. $ancestor->slug .'-->';

 

複数の先祖に属するカテゴリが設定されている投稿から特定の先祖カテゴリを取得する場合

投稿から先祖カテゴリを取得する場合、複数の先祖に跨がることがあります。

除外したいカテゴリツリーがあることも。たとえば、セール商品や更新情報などのサブカテゴリなどのケースが考えられます。
cat_sample3_2
この場合、「食べ物」か「飲み物」のどちらが取得されるかは、投稿IDの若さに左右されます。それでもいいのですが、特定のカテゴリを除外したい場合もあります。

出力の仕方

たとえば[nomimono]というカテゴリスラッグをもつカテゴリツリーを除外し、かつ先祖カテゴリを取得したい場合は以下の通り。第二引数に除外カテゴリ、第三引数にカスタムタクソノミ名(省略時はカテゴリ)を指定します。

$ancestor = get_term_ancestor( $post->ID, 'nomimono' );
echo '<!-- タームID:'. $ancestor->term_id .' ターム名:'. esc_html( $ancestor->name ) .' タームスラッグ:'. $ancestor->slug .'-->';

ちなみにこの関数でexcludeできるカテゴリID/slugはひとつだけです。複数のツリーを除外したければ適宜改変してください。

 


おまけ:第二階層の先祖カテゴリを取得する

ウェルカートの場合、第二階層のカテゴリが取得したい

サイトの構成によっては、第一階層のカテゴリよりも第二層の方が取得したいということもあります。たとえばウェルカートプラグインの場合、先祖カテゴリが自動的にitemになってしまいますので、分岐させたい場合、第二階層が欲しいです。
cat_sample4

下記関数群をfunctions.phpに記載します

この場合、[item]が取得されてはまずいので、[飲み物]カテゴリが取得されるようにする必要があります。で、やはり除外したいカテゴリツリーがある場合が想定されますすので、その処理も含めるようにします。

function get_welcart_ancestor_by_cat ( $sbjcat = '' ) { 
// [$sbjcat = カテゴリIDまたはスラッグ]
    if ( empty( $sbjcat ) ) 
        $sbjcat = get_query_var('cat'); //カテゴリページ
    elseif ( !is_numeric( $sbjcat ) ) 
        $sbjcat = get_term_by( 'slug', $sbjcat, $tax_name )->term_id;
    if ( !$sbjcat ) return false; //カテゴリ取得不可
    $ancestors = array_reverse( get_ancestors( $sbjcat , 'category' ) ); 
    if ( !$ancestors || get_term_by( 'term_id', $ancestors[0], 'category' )->slug !== 'item' ) return false; //非welcart
    if( isset( $ancestors[1] ) ) $ancestor_id = $ancestors[1]; else $ancestor_id = $sbjcat;
    return get_term( $ancestor_id , 'category' );
}

function get_welcart_ancestor ( $post_id = '', $extree = '' ) { 
// [$post_id = 投稿ID],
// [$extree = 除外するカテゴリツリーのIDまたはスラッグ]
    if ( !$post_id ) $post_id = get_post()->ID;
    $item_ancestor = get_term_by( 'slug', 'item', 'category' );
    $item_ancestor_id = $item_ancestor->term_id; //「アイテム」カテゴリのID
    if ( $extree && !is_numeric( $extree ) ) { 
        $get_extree = get_term_by( 'slug', $extree, 'category' );
        if ( $get_extree ) $extree = $get_extree->term_id; 
    }
    $cats = get_the_terms( $post_id, 'category' );
    if ($cats): foreach ( $cats as $current_cat ) :
        if ( $current_cat->term_id !== $extree && !term_is_ancestor_of ( $extree, $current_cat->term_id, 'category' ) && $current_cat->term_id !== $item_ancestor_id ) {
        // $extreeではないカテゴリで、$extreeが親ではないカテゴリ、かつitemではないカテゴリ、かつuncategorizedではないカテゴリ
            $sbjcat = $current_cat->term_id;
            break;
        }
    endforeach; endif;
    if ( empty( $sbjcat ) ) return $item_ancestor; // 該当カテゴリがない場合、「アイテム」カテゴリを返す
    return get_welcart_ancestor_by_cat( $sbjcat );
}

 

商品シングルテンプレートから呼び出す場合

特定の[sale]というスラッグを持つ商品カテゴリとその子孫を除いて、先祖カテゴリを取得したい場合の出力方法は以下のようになります。

$ancestor = get_welcart_ancestor( $post->ID, 'sale' ); 
echo '<!-- タームID:'. $ancestor->term_id .' ターム名:'. esc_html( $ancestor->name ) .' タームスラッグ:'. $ancestor->slug .'-->';

 

商品カテゴリテンプレートから呼び出す場合

$ancestor = get_welcart_ancestor_by_cat( get_query_var('cat') ); 
echo '<!-- タームID:'. $ancestor->term_id .' ターム名:'. esc_html( $ancestor->name ) .' タームスラッグ:'. $ancestor->slug .'-->';

 

Image Uploader for Welcart

Image Uploader for Welcart

Image Uploader for Welcartwordpress.orgから最新版をダウンロードしてください。
「Image uploader for Welcart」はコルネ株式会社さんが提供されているプラグイン「Welcart e-commerce」専用のイメージアップローダーです。お使いのwordpressに「Welcart e-commerce」をインストールしてからご利用ください。最新版同士※1でのご使用を推奨しております。なお、不具合等がありましたら、ここのコメント欄かWP公式のサポートスレッドにてご報告※2ください。

※1 WordPress4.5.4からメディアのアップロード時のサニタイズ仕様が変更になりました。Ver4.5.4+で使用する場合は必ずwelcart1.9+ Image Uploader1.4+をご使用ください。
※2 Welcartのフォーラムでは当プラグインに関する質問はお答えできません。

 

Welcart専用の画像アップローダーを商品情報編集ページに設置する

welcartは画像の名前の持ち方が制限があり、[商品コード–.jpg]としなくてはならない。CSVで管理したり一気に流し込むときにはとても便利なのだけど、ちまちま手作業で更新する小規模ウェブショップだとちょっと面倒。ましてCMSとして納品する場合、ウェブショップの運営者が不慣れだったり、担当者が変更になって引き継ぎが上手くいかなかったり…、と言うのはよくある話。何はともあれ、間違いが起きにくいように「画像あげたいな→ボタンがある→こんな感じかな→よしオッケー♪」という流れにしたい。
つまり…

商品情報編集ページから画像をアップロードできるようにする

アップロードした画像のタイトルをwelcart仕様に自動リネームし商品画像として登録する

編集画面上でドラッグで表示順を並べ替えられるようにする

…でも

welcartのメディアライブラリによる一括アップロード&紐付け機能は維持

ということが実現したいわけです。

 

商品情報編集画面でのアップロード&操作方法

rfw-01

設置すると上図のように、デフォルトの商品画像ボックスが削除され新たに専用のメタボックスが表示されます。(メタボックスはサイド表示を想定していますが、ノーマル表示やタブレットなどの小さい画面などwordpressのメタボックスソートやレスポンシブデザインにも対応しています。)

 

rfw-02

画像をアップロードします。

① 画像追加ボタンを押すとアップローダーが立ち上がります。

② 画像をアップロードします。

③ 「商品画像を選択」ボタンを押します。(複数まとめてどうぞ)

④ 選択された画像がメタボックスに表示されます。

 

rfw-03

⑤ サムネイルをドラッグして並べ替えることで、welcartの画像並びに反映されます。一番上に来る画像がメイン画像として設定されます。

⑥ 画像右上のデリートボタンを押すと商品画像の登録を解除できます。
デリートした画像は、投稿に添付された状態でライブラリに残ります。これがけっこうミソで、在庫でバリエーションがあったり無かったりの時に画像をアップロードしたり削除したりしなくても、すぐに元にもどせるようになります。一時的にカラーやサイズのバリエーションなどの写真だけ隠す、というような使い方をするときに便利です。

⑦ 商品を公開/更新します。画像が自動でリネームされ、welcartに登録(または登録解除)されます。メイン画像はアイキャッチ画像にも自動的に登録されます。
上の図では、3つの画像はそれぞれTEST-PRODUCT(一番目のメイン画像), TEST-PRODUCT–1(サブ画像1), TEST-PRODUCT–2(サブ画像2)と自動でリネームされています。自動で設定されるが好ましくない場合は、以下のコードをfunctions.php等に記載することでオート機能をオフにしてください。なお、オート機能をオフにしていても、アイキャッチ画像が選択されていない時は、自動的にトップの画像が設定されます。

add_filter('iu4w_auto_thumbnail', '__return_false');

 

メディアライブラリからまとめてアップロードする

welcartの形式通りアップロードすればwelcartに反映されます

メディアライブラリの画像アップローダーから商品コード名の画像がアップされた場合、商品情報ページを開いたときに自動的に商品投稿をアップロード先として紐付けされます

SKU–数字.jpg形式のものを重複して上げてしまっても個別にインデクスされますので、形式さえ合っていれば適当なナンバリングで問題ありません。(WP4.5.4からファイルのサニタイズ仕様が変更になっているためWP4.5.4+を仕様する場合は必ずwelcart1.9+ Image Uploader1.4+を使用してください。詳細はwelcart公式の案内をご覧ください。)

rfw-04
画像が反映されているはずですので、並べ替えなどを行って更新して下さい。

 

設置方法&出力方法

プラグインとして読み込んで有効化すれば、自動的に設置されます。

画像の呼び出し方はwelcartの機能のままですのでテーマに手を入れる必要はありません。

 

Change log

  • 1.4.12017/02/24レイアウトを改善するとともに代替テキストやキャプションを入力可能に。wp.media.jsで起きていたTypeErrorをfix。
  • 1.4.02016/10/01welcart1.9以降のsubimage_ruleに対応。
  • 1.3.92016/09/19バグフィクス。
  • 1.3.82016/09/11プラグインの記述をクラス対応に変更。
  • 1.3.72016/05/30テキストドメインをglotpress対応に変更。レスポンシブ時のCSSを修正。
    1.3.72016/05/30テキストドメインをglotpress対応に変更。レスポンシブ時のCSSを修正。
  • 1.3.6.12015/11/24商品多数時の保存・並び替えバグをフィクス。
  • 1.3.62015/11/20UIを変更、それに伴いJS等の処理を修正、画像無し保存時のエラーをデバッグ。
  • 1.3.52015/10/17バグフィクス。
  • 1.3.42015/09/12コンフリクト回避のためコードを変更。
  • 1.3.22015/01/18メディアライブラリから一括でアップロードした画像が重複したpost_titleを持つ場合もプラグインが認識して読み込むように変更。
  • 1.32015/01/07正式版1.3としてプラグインをリリース。極力DBを汚さないようにカスタムフィールドを使う仕様をやめました。更新の混乱を避けるためここで公開していたコードは消去。
  • 1.2.12014/12/11画像をセレクト後「商品画像を選択」ボタンを押さずにウィンドウを閉じた場合でも画像をセレクトしてしまう動作を修正。
  • 1.22014/10/08レスポンシブデザイン化。WP4.0のメディアアップローダの仕様に併せて記述変更、アップローダ内での画像削除に対応。※WP3.9以前は旧バージョン1.2をご使用ください。
  • 1.1.12014/05/24wp_update_postで商品の複製ができる問題に対処。画像アップ後にSKUを変更してもリネームされなかった問題に対処。SKU変更に応じて画像も自動リネームされるように。
  • 1.12014/04/25アップローダーの仕様を変更。よりシンプルにしました。
  • 1.02014/04/23公開。

welcartのSKU入力欄を拡張する

sc-2014-04-12-0.19.00-786x400

SKUにカスタム入力項目を追加しました。functions.php内で完結しているのでwelcart本体に手をつけなくていいので安心。
※こちらで紹介している記述ではAuto Deliveryとバッティングします。定期購入機能の利用者はカスタマイズが必要です。

参考にしたサイト

今回のカスタマイズはこちらを参考にしました。

【Welcart】でSkuに新規項目を追加する方法|ゴーゴーウェブマーケット技術BLOG

こうやって情報を共有していただけるのは本当にありがたいことです。変更箇所は以下2点。

●更新時にSKUフィールドが消えないようにフィルターをひとつ追加。
●項目が一つしか追加できなさそうなのでtdを結合。

CODE : Adding a new field to SKUs on welcart

<?php
/////////////////////// WELCART SKU内に項目を追加 /////////////////////// 

add_filter( 'usces_filter_sku_meta_form_advance_title', 'add_new_sku_meta_title'); //項目を追加
function add_new_sku_meta_title(){
	return '<th colspan="2">カスタム項目</th>';
}

add_filter( 'usces_filter_sku_meta_form_advance_field', 'add_new_sku_meta_field'); //フィールドを新規追加
function add_new_sku_meta_field(){
	return '<td colspan="2" class="item-sku-zaikonum"><input name="newskuadvance" type="text" id="newskuadvance" class="newskuadvance metaboxfield" /></td>'."n";
}

add_filter( 'usces_filter_sku_meta_row_advance', 'add_new_sku_meta_row_advance',10,2); //フィールドを追加
function add_new_sku_meta_row_advance( $default_field, $sku ){
	$metaname = 'itemsku[' .$sku["meta_id"]. '][skuadvance]';
	return '<td colspan="2" class="item-sku-zaikonum"><input name="' .$metaname.'" type="text" id="' .$metaname. '" class="newskuadvance metaboxfield" value="' .$sku["advance"]. '"/></td>'."n";
}

add_filter( 'usces_filter_add_item_sku_meta_value', 'add_new_sku_meta'); //新規項目を作成
function add_new_sku_meta($value){
	$skuadvance = isset($_POST['newskuadvance']) ? $_POST['newskuadvance'] : '';
	$value['advance'] = $skuadvance;
	return $value;
}

add_filter( 'usces_filter_up_item_sku_meta_value', 'up_new_sku_meta'); //項目を変更
function up_new_sku_meta($value){
	$skuadvance = isset($_POST['skuadvance']) ? $_POST['skuadvance'] : '';
	$value['advance'] = $skuadvance;
	return $value;
}

add_filter( 'usces_filter_item_save_sku_metadata', 'save_new_sku_meta',10,2 ); //項目を保持
function save_new_sku_meta( $skus, $mid ){
	$skuadvance = isset($_POST['itemsku'][$mid]['skuadvance']) ? $_POST['itemsku'][$mid]['skuadvance']: '';
	$skus['advance'] = $skuadvance;
	return $skus;
}
?>

商品情報ページのループ内でuscesを呼び出し、$usces->itemsku[‘advance’] をechoすれば出力できます。

<?php
	global $usces;
	echo $usces->itemsku['advance'];
?>

 


 

*追記(2014/4/27 )

SKUの入力項目を複数追加したい

sc 2014-04-27 18.21.14

ここまできたらどうあっても複数の値を持たせたい。で、最初に思ったのは、inputを複数設置して配列にして渡せばええんちゃうん?ということで、これはやってみたものの、半分成功で半分失敗という結果に。保存や公開はできるのに、welcartの「更新」や「skuを追加する」ボタンによるajax処理では値が正しく受け渡されなかった。これに嵌ってかなり時間を費やした。

welcart本体に手を加えたく無いので、phpだけで実現するのはあきらめ、jQueryでいったん分割したinputをセパレータを挟んで一つの項目に戻すという邪道な手で処理することにしたのが以下。上記関数群の2番目と3番目のフィルターを下記に変更します。inputにclass名を付与し、次に読み込ませたCSSとjqueryで処理しています。値の数などは任意に変更できます。

CODE : Adding new multiple fields to SKUs on welcart

add_new_sku_meta_fieldとadd_new_sku_meta_row_advanceの関数を以下のように変更します。それ以外は、上記と同じです。

<?php
add_filter( 'usces_filter_sku_meta_form_advance_field', 'add_new_sku_meta_field'); //フィールドを新規追加
function add_new_sku_meta_field(){
?>
<script type="text/javascript">
jQuery.fn.splitfield = function(){
    return this.each(function(){
        if ( jQuery(this).is(":visible") ) { //オートセーブなどのときに重複処理しないように
            var field = jQuery(this).hide(),
                separator = "##", //好きなセパレータを設定
                setval = field.val()?field.val().split(separator) : [],
                input = [],
                value = "",
                splits = 3, //項目数に応じて設定
                before = ["縦"," × 横"," × 高さ"], //個別に項目名が必要であれば設定
                after = ["cm","cm","cm"]; //単位などが必要であれば自由に設定
            for (var i = 0; i < splits; i++) {
                value = (setval[i])?setval[i] : "";
                input.push(  '<p class="smallfield"><span>' + before[i] + '</span><input type="text" style="ime-mode:disabled;" onkeydown="return OnlyNumber(event)" value="' + value + '"/><span>' + after[i] + '</span></p>' );
            } // 半角指定部(styleとonleydown)は削除可
            var div = jQuery( "<div>" + input.join("") + "</div>" ).children().each( function(){
                var values = [];
                jQuery(this).children( "input" ).blur(function(){ //フォーカスアウト
                    jQuery(this).parent( "p" ).siblings().andSelf().each(function(){
                        values.push( jQuery(this).children( "input" ).val());
                    });
                    field.val(values.join(separator)); //各値をinput.spiltfield本体に代入
                });
            }).end();
            field.after( div );
        }
    });
};
jQuery( ".splitfield", "#itemsku" ).splitfield();
jQuery( "#itemsku" ).ajaxSuccess(function() { // welcartのajax処理後、再splitする。
    jQuery( "#newsku" ).find( ".splitfield" ).show().next().remove();
    jQuery( "#itemsku" ).find( ".splitfield" ).splitfield();
});
</script>
<style type="text/css">
    .item-sku-advance { padding:0;}
    .item-sku-key { width: 30%; }
    .item-sku-cprice { width: 16%; }
    .item-sku-price { width: 20%; }
    .item-sku-zaikonum { width: 16%; }
    .item-sku-zaiko { width: 16%; }
    #itemsku .smallfield { display:inline-block; margin:0 8px 0 2px; }
    #itemsku .smallfield span { font-size:0.85em; }
    #itemsku .smallfield:last-child { margin:0 0 0 2px; }
    #itemsku .smallfield input { line-height:1em; width:40px; font-size:1em; }
</style>
<td colspan="2" class="item-sku-advance"><input name="newskuadvance" type="text" id="newskuadvance" class="newskuadvance metaboxfield splitfield" /></td>
<?php
}
 
add_filter( 'usces_filter_sku_meta_row_advance', 'add_new_sku_meta_row_advance',10,2); //フィールドを追加
function add_new_sku_meta_row_advance( $default_field, $sku ){
    $metaname = 'itemsku[' .$sku["meta_id"]. '][skuadvance]';
    return '<td colspan="2" class="item-sku-advance"><input name="' .$metaname.'" type="text" id="' .$metaname. '" class="newskuadvance metaboxfield splitfield" value="' .$sku["advance"]. '"/></td>'."n";
}
?>

ここではセパレータはダブルシャープ(##)にしてますが、何でも良いんで好きに変えて下さい。ただし、welcartはCSVでカンマ分割してるので、コンフリクトを避けるためにカンマは止した方が良いかもしれません。呼び出し方は以下の通り。[‘advance’]の値を取得しexplodeしています。値の数が2、3個だとループさせるほどでもないので、個別に呼び出せばいいと思います。例えばですが、こんな感じで。

<?php
$advance = $usces->itemsku['advance'];
if ($advance){
	$adv = '';
	$advance = explode( '##', $advance ); //セパレータで分割
	if ( $advance[0] ){
		$adv = '縦'.$advance[0].'cm';
	}
	if ( $advance[1] ){
		if ($adv) $adv .= ' × ';
		$adv .= '横'.$advance[1].'cm';
	}
	if ( $advance[2] ){
		if ($adv) $adv .= ' × ';
		$adv .= '高さ'.$advance[2].'cm';
	}
	echo '<p>このアイテムのサイズは【'.$adv.'】です。</p>';
}
?>

*追記(2015/9/01 )

カートページの商品名に追加項目の表記を反映する

質問があったので、カートの中で反映させる方法も書いておきます。tableの列を増やすには、wc_cart_page.phpやwc_confirm_page.phpの一部も書き換える必要があるので、ここでは簡単に商品名の下に表記することにします。advanceが複数の場合は上の表記を合わせてカスタマイズしてください。


/////////////////////// カートテーブルの変更 /////////////////////// 
function my_usces_filter_cart_row( $row, $cart, $materials ){
 global $usces;
 $post_id = $materials['post_id'];
 $cartItemName = esc_html( $materials['cartItemName'] );
 $skus = $usces->get_skus( $post_id );
 $advance = $skus[0]['advance'];
 $row = str_replace( $cartItemName, $cartItemName.'<br/>'.$advance, $row);
 return $row;
}
add_filter( 'usces_filter_cart_row', 'my_usces_filter_cart_row',10,3); 
add_filter( 'usces_filter_confirm_row', 'my_usces_filter_cart_row',10,3); 

 

rankings

 

welcartの商品情報に売上げ順位を集計して表示する

投稿内で順位を表示できれば、売上げランキング・トップ10にだけバナーを表示するとか、jQueryなんかのソートするプラグインを使うときの値に使用したりと、何かと便利なわけです。template_func.php内にbestsellerのIDを参照してる箇所があるので、その関数を利用します。

 

CODE:Get the usces sales ranking

<?php
// 投稿内で売上げ順位の登録と取得 // 
global $usces;
$bsids = $usces->get_bestseller_ids( '90' ); // 引数は統計を遡る日数を指定
$ranking = $count = 0;
if ($bsids): foreach( $bsids as $bsid ):
    $count++;
    if ( $bsid === $post->ID ){
        $ranking = $count;
        break;
    }
endforeach; endif;

if ( $ranking && $ranking <= 3 ) {
    echo '<p>今売れています!</p><p>当店売上げランキング<strong>第'.$ranking.'位</strong></p>';
}
?>

投稿やカテゴリのテンプレート内で呼び出して下さい。

 

カテゴリ内限定の順位が知りたい場合

上の関数は、商品毎にサイト全体での売上げ順位を表示してくれるわけですが、「〜〜」部門第1位とかやりたいこともあるかもしれません。そういう場合は以下のようにしてやります。

 

投稿テンプレートで商品のカテゴリ内売上げ順位を表示したい場合

<?php
global $usces;
$bsids = $usces->get_bestseller_ids( '90' ); // 引数は統計を遡る日数を指定
$ranking = $count = 0;
$cat = 'カテゴリのID';
if ($bsids): foreach( $bsids as $bsid ):
    if ( in_category( $cat, $bsid ) ){
        $count++;
        if ( $bsid === $post->ID ) {
            $ranking = $count;
            break;
        }
    }
endforeach; endif;
if ( $ranking && $ranking <= 3 ) {
    echo '「'.get_category($cat)->name.'」部門売上げ<strong>第'.$ranking.'位</strong>';
}
?>

 

カテゴリ・テンプレートで商品のカテゴリ内売上げ順位を表示したい場合

商品数が少なければ、上の関数内の$catに直接カテゴリIDを代入しても良いのですが、流石に商品ひとつ毎にbestsellerを呼び出すのも効率が悪いので、以下のようにループ外で一度順位を取得してしまいます。

<?php
//ループ外での処理
global $usces;
$bsids = $usces->get_bestseller_ids( '90' ); // 引数は統計を遡る日数を指定
$count = 1;
$cat = get_query_var('cat');
if ($bsids): foreach( $bsids as $bsid ):
    if ( in_category( $cat, $bsid ) ) $popular[ $bsid ] = $count++;// 順位を配列に入れる
endforeach; endif;

//ループ内での処理
if ( $popular[ $post->ID ] && $popular[ $post->ID ] <= 10 ) {
    echo '「'.get_category($cat)->name.'」部門売上げ<strong>第'.$popular[ $post->ID ].'位</strong>';
}
?>