このエントリはPlay frameworkひとりアドベントカレンダーの19日目です。
今日は /artist/ を実装します。
controllerを移植
例によって元コードを見ます。
get '/artist/:artistid' => [qw(recent_sold)] => sub {
my ($self, $c) = @_;
my $artist = $self->dbh->select_row(
'SELECT id, name FROM artist WHERE id = ? LIMIT 1',
$c->args->{artistid},
);
my $tickets = $self->dbh->select_all(
'SELECT id, name FROM ticket WHERE artist_id = ? ORDER BY id',
$artist->{id},
);
for my $ticket (@$tickets) {
my $count = $self->dbh->select_one(
'SELECT COUNT(*) FROM variation
INNER JOIN stock ON stock.variation_id = variation.id
WHERE variation.ticket_id = ? AND stock.order_id IS NULL',
$ticket->{id},
);
$ticket->{count} = $count;
}
$c->render('artist.tx', {
artist => $artist,
tickets => $tickets,
});
};
- artist.idでartistからartistを取得
- artis.idでticketからticketを取得
- ticketごとにカウントを取得
さて、ticketごとのカウントをviewにどう渡すか困ってしまいました。。
とりあえず動けばいいので、力技ですがHashMapで渡すことにしましょう。
controllerとviewのあいだのインターフェースが決まったところでコーディングを進めましょう。
modelを作る
ticketのmodelを作りましょう。
CREATE TABLE IF NOT EXISTS isucon2.ticket ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, `artist_id` INT UNSIGNED NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB;
繰り返しですが、下記に注意しましょう。
/app/models/直下に作るplay.db.ebean.Modelを継承する@Entityアノテーションをつけるfinderを定義する
package models;
import javax.persistence.Entity;
import javax.persistence.Id;
import play.db.ebean.Model;
@Entity
public class Ticket extends Model {
@Id
public Integer id;
public String name;
public Integer artistId;
public static Finder find = new Finder(
Integer.class, Ticket.class);
}
view
artist.scala.html を書きましょう。
まずはこぴぺしてから、渡すデータ(1行目)とくるくる( @for )を書きましょう。
@(artist :Artist, ticketMaps :List[HashMap[String, String]])
(略)
<div id="content">
<h2>@artist.name</h2>
<ul>
@for(ticketMap <- ticketMaps){
<li class="ticket">
<a>@ticketMap("name")</a>残り<span class="count">@ticketMap("count")</span>枚
</li>
}
</ul>
</div>
(略)
controller
本邦初登場。 /conf/routes でパラメータを使ってみましょう。
GET /artist/:artistId controllers.Application.artist(artistId:Integer)
public static Result artist(Integer artistId) {
return TODO;
}
この状態でとりあえずブラウザで http://localhost:9000/artist/1 とかにアクセスすると、TODOの画面が出ます。
これでroutesが間違ってないか確認できるわけです。
TODO って便利!
ここからデータをごにょりましょう。
まずは Artist 。これは簡単。
Artist artist = Artist.find.byId(artistId);
これだけ。
次に Ticket をもとに List<HashMap> をつくりましょ。これはちょっとタイヘンです。
find.byId は簡単なのでokですね。
SQLを直接実行するのは、今回は参照クエリなのでEBeanのcreateSqlQueryでやりましょう。
parameter bindingが使えるので積極的に使いましょう。当然使うよね的な感じで。
public static Result artist(Integer artistId) {
Artist artist = Artist.find.byId(artistId);
List tickets = Ticket.find.where().eq("artist_id", artistId)
.orderBy("id").findList();
List<HashMap> ticketMaps = new ArrayList<HashMap>(
tickets.size());
SqlQuery query = Ebean
.createSqlQuery("SELECT COUNT(*) as cnt FROM variation"
+ " INNER JOIN stock ON stock.variation_id = variation.id"
+ " WHERE variation.ticket_id = :ticketId AND stock.order_id IS NULL");
for (Ticket ticket : tickets) {
HashMap ticketMap = new HashMap();
ticketMap.put("id", ticket.id.toString());
ticketMap.put("name", ticket.name);
List count = query.setParameter("ticketId", ticket.id)
.findList();
ticketMap.put("count", count.get(0).getInteger("cnt").toString());
ticketMaps.add(ticketMap);
}
return ok(views.html.artist.render(artist, ticketMaps));
}
ここまでできたら再度ブラウザでアクセスしてみましょう。
もしデータが空だったら、initial_data.sqlを使ってデータを再投入してくださいね。
デターヽ(`▽´)/
