Taglibro de miyacorata

Vi fine falos en la marĉon.

THE@TER CHALLENGE!!のページを作った話

12月も中盤に入り、澄んだ空気を胸に詰め込んだらソワソワする季節になってまいりました。皆様ハッピーホリデイたるクリスマスは誰と過ごしますか? ぼくはバイトです。悲しい。

そんなわけでこの記事は 冬も元気にアイマスですよアイマスAdventCalendar2018 の12月15日の記事として書かれています。

[blogcard url="https://adventar.org/calendars/2973"]

🤔

まずは謝罪です。

約一週間の遅刻大変申し訳ございません。

なかなか暇がなかったのとすっぽり忘れてたのとここ最近体調を割と崩しがちなのが災いしました。ちゃんと書くので許して。

ところで12月14日のくにまりの記事も16日のはるかぜさんの記事もまだありませんそなたさま


本題:THE@TER CHALLENGE!!

というわけでみりっほー!みなさんミリシタやってますか? 最近まきくんや山葵を始めとする包囲網やアイマストドンの皆様のおかげで着実にミリオンライブ!にも染まっておりたいへん恵まれた尊みを得ております。Hearty!!の「地球規模で比べたらどってことないんじゃない?」っていうのがめっちゃすきです。座右の銘にしたい。

で、先日よりユニット投票企画「THE@TER CHALLENGE!!」が始まり、公職選挙法に縛られない自由な戦法を駆使した熾烈なファイトが繰り広げられているわけであります。

というわけで MillionLivePortal を運用しているぼくとしてはちょっとサイトでも取り上げたいな~という次第でして、なにしよっかな~と考えていたところこんなツイートが飛び込んできました。

matsurihi.me さん カードのイラスト見るときにお世話になっています

わお、APIができちゃった

ほんならやるしかないというわけで

https://millionlive.miyacorata.net/theater/challenge

はい、TC!!の得票状況を確認できるページ生やしました。

[blogcard url="https://millionlive.miyacorata.net/theater/challenge"]

アイコン込みで結構綺麗に作れたので気に入ってます。普通に一覧こんな感じで作り直そうか迷っています。

サーバサイド:データ取得

matsurihi.meさんのAPI Princessからは以下の情報が取得できます。たいへんすきなネーミングセンスです。見習いたいです。APIについて詳しくは本家サイトのドキュメントを参照してください。

  • GET /election 開催中の投票企画の概要と配役情報
  • GET /election/current 得票状況 各配役上位10名の名前と得票数が返却される

[blogcard url="https://api.matsurihi.me/docs/"]

とりあえず企画概要と配役情報は変わらないので手動保存したものを読ませます。得票状況は定期的(現時点では2分おき)に取得しJSONファイルとして保存するスクリプトをNode.jsで書いて裏で回しています。この記事を執筆している時点では鯖上に2000を軽く超えるファイルが格納されています。古いやつも差分表示とかで活用したいですね。

欠点として2分から5分程度データに遅延が生じることですが、まぁリアルタイムの情報欲しけりゃミリシタ出しっぱなしにしようという話であります。matsurihi.meにそんなに負荷をかける訳にも行きませんからね。

サーバサイド:PHP

ページはいつもどおりPHPで書いています。

JSONを所定の位置から取得しオブジェクトの配列に変換処理してforeach発射ァ!(松田亜利沙) エイッ!(配役情報のループの中で得票状況を読み出す) こっちもエイッ!(得票状況でループを回しその中でアイドル情報取得関数をコール) もひとつオマケにエイッ!(リストを生成しアイドル詳細ページへのリンクと得票数を表示) という感じです。

MLPは関数一つコールすればアイドルの情報全部入りの配列を得られるようにしていますので楽ちんです。ズボラ実装万歳。

個人的に得られた知見

まず前提条件として、このページでは以下の要領でデータを取り出しています。JSON読める人は読みながらのほうがいいかも(JSON読める人はこの節を読む必要がない説もある)

  • [配役情報から]ドラマごと(孤島・おとぎの国・近未来)でループ => $dramas as $drama
    • [配役情報から]各ドラマの各配役ごとでループ => $roles as $role
      • [得票状況から]配役ごとの得票状況

この順序でループを回すとドラマごとに分けて配役ごとの得票状況を出させることが可能になります、ということがご理解いただければ上出来です。

配役一覧($drama->roles[])でループを回してる中で、 得票状況(オブジェクトの配列)から配役IDが一致するものを出すためにキーとなる値を調べたい場合、次のコードで解決可能だということがわかりました。

$list = array_search($role->id,array_column($json,'id'));
var_dump($json[$list]);

配列から特定の値を持つ要素を取り出すには array_search関数 をコールします。ここで得票状況から取り出すべき配役IDを決定したいのですが、得票状況は上述の通りオブジェクトの配列になっておりうっわこれどうしたらええねんみたいなお気持ちになります。

そこで array_column関数 をコールします。これは多次元配列あるいはオブジェクトの配列の中から単一のカラムのみの配列を返すものです。
このコードでは配役情報全体から配役IDのみの配列を生成しており、配役情報から配役ごとのループを回す中で得票状況から取り出すべき配列のキーを決定するために用いています。

とかくこれでめでたく各配役ごとにランキング出して出力できるようになりましたとさ、めでたしめでたし、でした。絶対伝わってねえなこの節...

フロントエンド:JSでタブの実装

できれば得票状況は各ドラマごとにみたいですよね、スクロールめんどいし。ついでにリロード掛けても選択していたタブが保持されているとさらに素晴らしい。

できたので実装しました。

Vanilla.jsで頑張れねえかな~と思いましたが根負けしてjQueryで書きました。アーメン。

var tab = $('#tabControl a');
var tabc = $('#tabBody > .tabContent');
var tabs = ["孤島サスペンスホラー","おとぎの国の物語","近未来アウトサイダー"];

$(function() {
    tab.click(function(e) {
        var index = tab.index(this);
        tab.removeClass('selected');
        $(this).addClass('selected');
        tabc.removeClass('show').eq(index).addClass('show');
        location.hash = tabs[index];
        console.log("イベント発火ァ!["+index+"]");
        return false;
    });

    var hash = decodeURI(location.hash);
    console.log("ハッシュ:"+hash);
    for(i=0;i<tabs.length;i++){
        if(hash === "#"+tabs[i]){
            console.info("ハッシュ一致!");
            tab[i].click();
        }else{
            console.info("ハッシュ不一致!");
        }
    }
});

ズボラコードですね。CSSと連携して見えるようにしたり見えないようにしたりしていい感じにタブを実装しています。

で、ここでは location.hash でURLのハッシュ以降を取り出し・書き出しができるのを利用し、タブ切り替え時にはハッシュを書き換えます。またページ読み込み時に各タブに一致するハッシュがあった場合は一致するタブのクリックイベントを発火させ、見た目上タブが保持されているようにしました。

これで見た目と利便性を両立したぼくのかんがえたさいきょうのTC!!得票状況モニタが完成しました。

今後の展望

  • アイドル個別ページから得票状況を見られるようにしたい
  • 過去のデータを活用して差分を見られるようにしたい
  • #まほー使いまつり 見たい!

差分については設計が大方固まったので今週末でやりたい機運があります。急かすとちょっと頑張るかも知れない。

あ、あとこのページを気に入っていただければ1票だけでも #まほー使いまつり へ投票いただけるとぼくがうれしくなります。まほー使いまつりをよろしくお願いいたします。