サポート » 使い方全般 » ソートとページング

  • 解決済 amekuro

    (@amekuro)


    質問お願いします。
    カスタムフィールドでソートする方法とページングがよくわかりません。
    下記コードにarray_multisortを指定しカスタムフィールドの値でソートしたいと思っています。
    しかしarray_multisortをどこに記述してもソートしてくれません。

    最後のwp_reset_query();のあとに
    array_multisort($sample4, SORT_DESC, $sample2);
    print ‘

    ';
    var_dump($sample4);
    print '

    ‘;
    と記述するとちゃんとソートしてくれます。
    ただ、ページングがうまくできていないのかarray_multisortなど使用すると
    1ページ内でソートして全体ではソートしてくれません。

    例えば全部で25件の投稿があり1ページ10件表示と設定したとします。
    1ページ10件表示するのですがarray_multisortを使用すると1ページ10件のみソートし
    2ページ目は2ページ内でのみソートする感じです。
    (var_dumpで試しました)

    具体例※数字はサイズと仮定してください)
    値(5,8,7,1,9,11,54,65,43,39,4,16,19,3,42,87)
    投稿取得
    1ページ(5,8,7,1,9,11,54)
    2ページ(65,43,39,4,16,19,3)
    3ページ(42,87)
    array_multisort($sample4, SORT_DESC, $sample2);とvar_dumpで試すと
    1ページ(54,11,9,8,7,5,1)
    2ページ(65,43,39,19,16,4,3)
    3ページ(87,42)

    希望としては
    1ページ(87,65,54,43,42,39,19)
    2ページ(16,11,9,8,7,5,4)
    3ページ(3,1)
    と表示したいと思っています。

    ——–コード
    $catid=get_query_var(‘cat’);
    $cat=get_category($catid);
    $paged = (get_query_var(‘paged’)) ? get_query_var(‘paged’) : 1;
    query_posts($query_string .’&paged=’.$paged);
    if ( have_posts() ) : while ( have_posts() ) : the_post();

    $meta = get_post_custom_values(‘sample1’);
    $sample2[] = array(
    ‘id’ => $post->ID,
    ‘title’ => $post->post_title,
    ‘all’ => unserialize($meta[0]));
    foreach ($sample2 as $key=>$sample3){
    $sample4[$key]=$sample3[‘all’][‘size’];
    $sample5[$key]=$sample3[‘title’];
    $sample6[$key]=$sample3[‘id’];
    }
    print $sample4[$key];
    print $sample5[$key];
    print $sample6[$key];

    endwhile;endif;
    wp_reset_query();

    よろしくお願いします。

13件の返信を表示中 - 1 - 13件目 (全13件中)
  • トピック投稿者 amekuro

    (@amekuro)

    訂正
    コードの
    $catid=get_query_var(‘cat’);
    $cat=get_category($catid);
    は必要ないです。

    モデレーター jim912

    (@jim912)

    amekuroさん、こんにちは。

    カスタムフィールドの値でソートするには、並び順・並べ替え引数 の meta_value(meta_keyの指定必須)で実現可能です。ただし、上記のコードから察するに sizeのデータはシリアライズされて格納されているようで、これだと正しく動作しない可能性があります。

    ですので、要望通りの並び順で表示するには、データの保存の仕方自体を変更する必要があります。

    トピック投稿者 amekuro

    (@amekuro)

    jim912さん
    回答ありがとうございます。
    本当に感謝しています。

    query_postsのmeta_key指定など色々試してみましたが推測していただいたように
    sizeがシリアライズされている為、meta_keyの指定などができませんでした。
    (自分の知識や能力不足なのでもしかしたら指定する方法があるかもですが・・・)

    色々調べ、結局行き着いたのが先程のコードです。
    色々試しているうちに【デフォルトのパーマリンク】でのみ
    array_multisortを使用したソート、ページングが成功し希望通りにできたのです。
    しかしパーマリンクをデフォルト以外に設定するとうまくページングができない症状に悩まされます。
    (かなりコードがグチャグチャしてしまってなにがなにやらと言う感じです)
    ソートはできるが2ページ目に移行できないなど。

    『データの保存の仕方自体を変更する』にあたり何か参考になるサイトやヒントになるようなサイトはないでしょうか?

    自分が参考にしたサイトは
    http://tenderfeel.xsrv.jp/wordpress/322/
    などですがこのサイトに紹介されているコードで試しても
    デフォルト以外のパーマリンクを設定すると機能しないようです。

    紹介されているコードを色々と編集し【デフォルトのパーマリンク】でのみ希望通りにできました。

    何か良い知恵をお貸しください。
    よろしくお願いします。

    トピック投稿者 amekuro

    (@amekuro)

    コードを単純にしarray_multisortでソートできるようになりましたが
    やはりページングがうまくいきません。

    sizeについてはunserializeを使用しなくても取得できることが分かりましたので
    除外しました。

    例)
    値(5,8,7,1,9,11,54,65,43,39,4,16,19,3,42,87)

    $paged = (get_query_var(‘paged’)) ? get_query_var(‘paged’) : 1;
    query_posts($query_string .’&paged=’.$paged);
    if ( have_posts() ) : while ( have_posts() ) : the_post();
    $meta = get_post_meta($post->ID, ‘sample1’, true);
    $sample2[]=$meta[“size”];
    foreach($sample2 as $key=>$sample3){
    $sample4[$key]=$sample3;
    }
    array_multisort($sample4, SORT_DESC ,$sample2);
    endwhile;endif;wp_reset_query();
    print’

    ';
    print_r($sample4);
    print'

    ‘;

    結果—–
    1ページ目
    Array
    (
    [0] => 54
    [1] => 11
    [2] => 9
    [3] => 8
    [4] => 7
    [5] => 5
    [6] => 1
    )
    2ページ目
    Array
    (
    [0] => 65
    [1] => 43
    [2] => 39
    [3] => 19
    [4] => 16
    [5] => 4
    [6] => 3
    )
    3ページ目
    Array
    (
    [0] => 87
    [1] => 42

    )

    となってしまい希望通りのページングができません。
    array_multisortを消して
    query_postsにorder=ASCなどとすると全てのページが対象となりソートできます。

    jim912さんが言っていたデータの保存方法が今一分かりません。
    よろしくお願いします。

    まず、jim912さんの言っている方法はこういうことです。

    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
    
    // orderby=meta_value_num を指定する時は、クエリーでmeta_keyを指定する必要があります。
    query_posts($query_string .'&paged='.$paged . '&meta_key=size&orderby=meta_value_num$order=DESC');
    
    if ( have_posts() ) :
        while( have_posts() ) :
            the_post();
            // 表示処理
        endwhile;
    endif;

    「orderby=meta_value」と指定すると、jim912さんのご指摘通り、正しくソートできない可能性があります。
    「orderby=meta_value」と指定すると、文字列順でソートするためです。

    例)
    ・並べ替え前「2, 13, 8, 9, 11, 1, 5」
    ・並べ替え後「1, 11, 13, 2, 5, 8, 9」

    よって、「orderby=meta_value_num」と指定する必要があります。「orderby=meta_value_num」ならば数値として並び替えが可能です。
    例)
    ・並べ替え前「2, 13, 8, 9, 11, 1, 5」
    ・並べ替え後「1, 2, 5, 8, 9, 11, 13」

    amekuroさんが参考にされたページでは、get_posts()を使用しています。query_posts()ではありません。
    http://tenderfeel.xsrv.jp/wordpress/322/ のサンプルでは、get_posts()で全記事を取得してきて、
    並び替えてから1ページ分に切り詰めています。

    一方、amekuroさんのコードでは、query_posts()でpagedを指定しています。
    これは指定ページの記事だけを取得してくる設定です。
    つまり、amekuroさんは n ページ目を取得してきて、そのページ内で並び替えているのです。


    参考
    http://ja.forums.wordpress.org/topic/1851
    http://ja.forums.wordpress.org/topic/3846

    トピック投稿者 amekuro

    (@amekuro)

    sigeyamaさん回答ありがとうございます。
    ご指導通り書き換えてみましたが結果は変わらずでした。

    データベースを見るとmeta_keyはsample1で
    meta_valueは
    Array
    (
    [id] => 1
    [name] => 名前
    [size] => 12
    )

    Array
    (
    [id] => 2
    [name] => 名前
    [size] => 54
    )
    …..となります。
    (meta_valueはシリアライズされています)

    meta_valueのsizeでソートしたいと考えています。

    query_posts($query_string .’&meta_key=sample1&meta_value=size&orderby=meta_value_num&order=DESC’);
    だと何も表示されなくなります。

    どうやらmeta_valueを指定すると取得できなくなるようです。

    query_posts($query_string .’&meta_key=sample1&orderby=meta_value_num&order=DESC’);
    とすると取得ができ全体でソートしますが希望するソートができません。

    最初は参考サイトのように試していたのですが
    パーマリンクを変更するとページングがうまくいかず
    上記のコードに変更しました。
    (全体でソートができたが1ページ目と同じ内容が表示されるなどが原因)

    よろしくお願いします。

    分かりました。
    meta_keyがsizeなのだと思ってましたが、違ったのですね。

    その場合、query_postsのオプションでソートすることはできませんので、get_postsを使って実装してください。

    多分、以下のような感じのコードになります。(以下のコードは未検証です。)

    $meta_key = 'sample1';
    // カスタムフィールド"sample1"が設定されている投稿を全件取得
    $my_posts = get_posts('numberposts=-1&meta_key=' . $meta_key);
    
    foreach($my_posts as $post) {
    	// カスタムフィールドの値を取得
    	$meta = get_post_meta($post->ID, $meta_key, true);
    	// 出力したい項目をまとめる
    	// ここでは投稿IDと投稿のタイトル、ソートキーの3つのみ。
    	$out[] = array('post_id'=>$post->ID, 'post_title'=>$post->post_title, 'size'=>meta['size'], 'slug'=>$post->post_name);
            // ソートキー
    	$sort_key[] = meta['size'];
    }
    array_multisort($sort_key, SORT_NUMERIC,SORT_DESC, $out);
    
    // 並べ変えた投稿をページングに対応させる
    $ppp = get_option('posts_per_page');
    
    // 現在表示のページ番号を取得
    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
    
    // $start 件目から $max 件目までの投稿を取得
    $start = ($paged-1)*$ppp;
    $max = $start + $ppp;
    $out = array_slice($out, $start, $max);
    
    // 一覧を表示
    echo '<ul>';
    foreach($out as $item) {
    	echo '<li><a href="' . $item['slug'] .  '">' . $item['post_title'] . '</a></li>';
    }
    echo '</ul>';

    query_posts($query_string .’&meta_key=sample1&meta_value=size&orderby=meta_value_num&order=DESC’);
    だと何も表示されなくなります。

    ちなみにこの指定は「meta_keyがsample1の値がsizeのものだけを絞り込む」という指定ですので、何も取得できません。

    モデレーター jim912

    (@jim912)

    amekuroさん、sigeyamaさん

    全件取得すれば、array_multisort でのソートは可能ですが、難点が2点あります。

    1. 全件取得するために多くのメモリが必要となる。環境次第ではあるが、数百件でメモリーエラーとなる可能性有
    2. ページングのリンク生成も自分で実装する必要がある

    jim912さん
    おっしゃる通りです。

    1. のメモリエラーについては、環境次第なので何とも言えませんが、2. のページングについては全くその通りで、amekuroさんの提示されたサイトでもページナビゲーションを作成しています。

    sample1という一つのカスタムフィールドに、配列をシリアライズして格納するのではなく、id、name、sizeという三つのカスタムフィールドに分解して格納するのがベストかと思います。

    例えば、以下のような関数を持つプラグインを作って、カスタムフィールドを分割するなど。

    /**
    * 指定のカスタムフィールドの値を分割する
    * @param string $meta_key
    * @param int $offset 先頭から何件スキップするか
    * @param int $posts_per_page 何件読み込むか
    * @return void
    */
    function split_custom_field($meta_key = 'sample1', $offset = 1, $posts_per_page = 100) {
        // カスタムフィールド"sample1"が設定されている投稿を取得
        $my_posts = get_posts('numberposts=-1&meta_key=' . $meta_key . '&posts_per_page=' . $posts_per_page . '&offset=' . $offset);
    
        foreach($my_posts as $post) {
            // カスタムフィールド"sample1"の値をそれぞれ"id"、"name", "size"というカスタムフィールドに変換
            $meta = get_post_meta($post->ID, $meta_key, true);
            foreach($meta as $key=>$value) {
                add_post_meta($post->ID, $key, $value);
            }
        }
    }
    トピック投稿者 amekuro

    (@amekuro)

    sigeyamaさん、jim912さん回答ありがとうございます。
    さっそくget_postsを使い実装した所、希望通りのソートができました。

    ですが2ページ以降で不都合を発見しました。
    wordpressの管理画面の表示設定で3件表示と指定した場合、以下のようになります。

    全25件を1ページ3件表示
    1ページ(100,99,98)
    2ページ(97,96,95,94,93,92)
    3ページ(94,93,92,91,90,89,88,87,86)
    4ページ(91,90,89,88,87,86,85,84,83,82,81)
    5ページ(88,87,86,85,84,83,82,81)
    6ページ(85,84,83,82,81)

    となってしまいます。
    増えていって減っていく感じです。

    $start = ($paged-1)*$ppp;
    $max = $start + $ppp;
    $out = array_slice($out, $start, $max);
    の設定でしょうか?
    参考サイトの記述でも分からなく挫折していた場所です。

    ページ番号の取得は以下のコードになります。

    global $wp_rewrite,$wp_query;
    $paginate_base = get_pagenum_link(1);
    if(($wp_query->max_num_pages) > 1):
    if (strpos($paginate_base, '?') || ! $wp_rewrite->using_permalinks()) {
    $paginate_format = '';
    $paginate_base = add_query_arg('paged', '%#%');
    } else {
    $paginate_format = (substr($paginate_base, -1 ,1) == '/' ? '' : '/') .
    user_trailingslashit('page/%#%/', 'paged');;
    $paginate_base .= '%_%';
    }
    $result = paginate_links( array(
    'base' => $paginate_base,
    'format' => $paginate_format,
    'total' => $wp_query->max_num_pages,
    'mid_size' => 5,
    'current' => ($paged ? $paged : 1),
    ));
    echo '<div class="page">'."\n".$result."\n</div>\n";
    endif;

    jim912さんがご指摘されましたメモリですが全く考えてもいませんでした。
    確かにjim912さんが言われるように件数が増えていくと思うとゾッとします。
    sigeyamaさんがご提示して頂いた『分解して格納』のコードが解決策でしょうか?
    大変申し訳ないのですが以下質問に回答して頂けたらと思います。
    1.functions.phpに記述した場合、どのように使用すれば良いでしょうか?
    2.値は既にシリアライズされ格納されている状態ですがこの場合でも有効なのでしょうか?

    お手数をおかけしますがよろしくお願いします。

    トピック投稿者 amekuro

    (@amekuro)

    sigeyamaさん、jim912さんお世話になります。
    2ページ以降の不都合ですが
    $max = $start + $ppp;を消去し
    $out = array_slice($out, $start, $ppp);
    とした所、希望通りにソートとページングができました。
    $max = $start + $ppp;を消去しても大丈夫でしょうか?
    パーマリンクなど変更してテストしましたが正常に機能しているようです。

    ただ、jim912さんが指摘されたメモリの件が気になります。
    予定ではカテゴリ毎に約2000件ほど投稿しようと思っています。
    カテゴリ一覧にはタイトルや画像など出力しますのでかなり不安です。

    sigeyamaさんの提示いただいた分解して格納のコードが気になり調べてはいるのですが
    今の所仕組みが分かっていません。

    メモリを気にしないで希望通りにソートとページングができる方法はないものでしょうか?

    2ページ以降の不都合ですが
    $max = $start + $ppp;を消去し
    $out = array_slice($out, $start, $ppp);
    とした所、希望通りにソートとページングができました。
    $max = $start + $ppp;を消去しても大丈夫でしょうか?

    そうですね、それが正しいコードだと思います。

    sigeyamaさんの提示いただいた分解して格納のコードが気になり調べてはいるのですが
    今の所仕組みが分かっていません。

    メモリを気にしないで希望通りにソートとページングができる方法はないものでしょうか?

    分解して格納のコードは説明が足りませんでした。

    現在、すでにWeb上に公開されているWordPressサイトならば、カスタムフィールドを分解し、複数のカスタムフィールドに値を格納して、運用してはどうですか? という意味でした。

    もし、まだWordPressのテーマファイルを作っているような段階で、Web上にサイトを公開していないのであれば、最初からカスタムフィールドを3つ作ればいいだけです。


    カスタムフィールド「sample1」にどうやってシリアライズされた値を格納していますか?

    トピック投稿者 amekuro

    (@amekuro)

    sigeyamaさん回答ありがとうございます。
    カスタムフィールドにどうやってシリアライズされているかは
    知識がなく自分では仕組みが分かりません。

    確かに自分でカスタムフィールドを作成してソートしたらどんなに楽かと考えた事もありましたが
    既に作成されているカスタムフィールド(機能)なので苦労しています。

    とりあえずはsigeyamaさん案のget_postsでいこうと思います。
    sigeyamaさんやjim912さんには大変感謝しています。

    今現在はさらにサイズで絞り込みができないか奮闘中です。
    if文を使い絞り込みしているのですがこれもページングがうまくいっていません。
    (もっと勉強しないと・・・)
    こちらのトピックは一旦、解決済みとさせて頂きます。
    また、質問などのトピックを見かけた際にはお知恵を貸して頂けたらと思います。
    本当にありがとうございました。
    心から感謝しております。

13件の返信を表示中 - 1 - 13件目 (全13件中)
  • トピック「ソートとページング」には新たに返信することはできません。