第29回北海道開発オフに参加しました(Cake PHPでPHP QR Codeを使ってみた)

3月16日に開催された、第29回北海道開発オフに参加しました。
ちょっと体調を崩してしまい、14時からの参加。

今回は先月の北海道アイディアソン&ハッカソンで作りはじめたプロダクトの実装の続きです。

QRコードを出力できるようにする

今回のアプリでは、特定のURLを作成し(URLを作るところはこれから)、それをQRコードで画面に表示する機能が必要になります。
今まで仮でQRコード画像を貼付けていたところを、動的にQRコード生成するよう実装をしました。

プロダクトの選定

これは開発オフが始まる前からちらちら見ていたのだけど、最終的に使えそうなのはこの2つのどちらかかな、となった。

  • Image_QRCode
    [良い]2012年にも更新してる
    [不安]PEARライブラリ導入方法(vagrantの設定まで変えたくないなあ)
    [不安]Release state:alpha(0.1.2)
  • PHP QR Code
    [良い]サイトの印象が良かった
    [良い]使い方簡単そう、オプションも充分
    [良い]CakePHPへの導入も楽そう(Vendorの配下に入れれば大丈夫そう)
    [不安]最終更新が2010年頃

今回はPHP QR Codeを使う事にしました。

Cake PHPでPHP QR Codeを使ってQRコードを生成する

  1. ライブラリのダウンロードと配置
    まず、PHP QR Codeのページからソース一式をダウンロードしてきて、app/Vendor 配下にフォルダごと置いた。
  2. ライブラリのインポート
    qrlib.phpをincludeすれば、全ての機能が使えるようです。今回は使う場面が1箇所だけなので、該当のControllerに次の記述を追加。

    App::import('Vendor', 'phpqrcode/qrlib');
    
  3. Viewでの実装
    QRコードは動的にバイナリを生成して(画像は保存せずに)、表示が終わったら破棄される形にしたかった。
    PHP QR Codeでは、バイナリを直接出力してくれるメソッドがあったのでviewの中でそれを使用しました。

    ob_start();
    QRCode::png('https://www.google.co.jp/', null, 'H', 5, 2);
    $img_base64 = base64_encode( ob_get_contents() );
    ob_end_clean();
    echo $this->Html->div('qrcode', "<img src='" .sprintf('data:image/png;base64,%s', $img_base64). "'/>");
    

    ハマったところとしては

    • ob_start()-ob_end_clean()を使わなければ、バイナリが直接htmlに出力されてしまう
      これに長い事はまりました。最後はstackoverflowで解決。
      出力されたバイナリを内部バッファに保存しておき、ob_get_contents() を使って変数への代入をする必要があったのでした。
      内部バッファという概念ははじめてで、ちょっと理解が足りない部分がまだある。
    • オプションをつけたいけど第2引数がファイル名だ….
      第3引数以降が、サイズやクォリティのオプションなのだけど、第2引数をどう無視すればいいのか…(サンプルコードがあまりない)
      結局、第2引数にnullを入れたらあっさり解決してしまった。
    • Helperが使えない
      バイナリで出した画像はbase64形式にエンコードしてhtmlに表示させるのだけど(ここだけは最近業務でやった経験があったので、すんなり行けた)、HtmlHelperを使ってimageタグを作ると、”data:image/png;”の記述がエスケープされてしまった。
      たぶんエスケープを無視する方法があるのだろうけれど、直接imageタグを記述する事で回避してしまっている。

とりあえず、時間内にviewにQRコードを表示させる事ができたので満足です。

汚いソースコードをなんとかする

私は主にview側を担当して書いていたのですが、知識が足りないのもあり、ほぼ生のHTMLにphpのコードを埋め込んで書いていたのですね。
これが、見づらい。
他のソースを見る余裕もでてきて、Helperを使えばかなり綺麗になるだろうということで、@makiesさんに教えてもらいながらリファクタリングをしました。

例えば次のコード。これは「サイコロをユーザが振った後、出た目によってサイコロ画像と目的地を表示するjavascriptコードを出力している」ソースコードになります。

【before】
正直「動いたー!」の後、思考停止して何も修正していなかった状態。

echo("$('#dice').html('" . $this->Html->image("dice/choiced_dice_". $dice_num .".png", array('width' => '240px', 'height' => '240px')) . "');");
echo("$('#myModalLabel').html('目的地は<b style=\"font-size:20pt\">". $place ."</b>です。');");

【after】
文字列を決定する変数を別に定義する(jQueryのコードの可視性が上がる)、HtmlHelperを利用する(直接bタグ書かない、htmlタグの出力中に文字列連結しているのが汚い)をしたのが次のコード。

$str = sprintf(__('目的地は %s です。'), $this->Html->tag('b', $place, array('style' => 'font-size:20pt')));
$place_image = $this->Html->image(sprintf('dice/choiced_dice_%s.png', $dice_num), array('width' => '240px', 'height' => '240px'));
echo("$('#dice').html('{$place_image}');");
echo("$('#myModalLabel').html('{$str}');");

Helperのマニュアルを見て、適用できるところを直すのと、sprintfを使う事で、後から読んでも混乱しないソースコードに近づいた。

あとはローカライゼイションに対応できるように、静的テキストを「__(‘テキスト’)」の形式で記述するよう書き換えた。これはCakePHPがもっているローカライゼイションの機能を使えるようにするための修正。
【before】

<h3 id="myModalLabel">なにがでるかな</h3>

【after】

<h3 id="myModalLabel"><?php echo __('なにがでるかな'); ?></h3>

@makiesさんが書いているソースコードが生きたお手本なので、時間を見てリファクタリングも進めていきたいなー。
今は、postgreSQLからMySQLにDBを変更したのだけど、自分の環境でうまく切り替えられなくてはまってます。
4月も1回くらい集まってプログラミングできたらいいな。

4 comments:

  1. >たぶんエスケープを無視する方法があるのだろうけれど、直接imageタグを記述する事で回避してしまっている。

    こんな感じでいけると思いますよ。
    これはリンクですけど、imgも同じように出来ます。

    ストロングタグなどタグを一緒にくくってしまいたい時は、画像リンクを作成する方法と同じようにする

    Html->link(‘【再販】‘ . $item[‘Item’][‘name’], array(‘controller’=>’FrontItems’, ‘action’=>’detail’, $item[‘Item’][‘id’]), array(‘class’=>’link1’, ‘escape’=>false), null); ?>

  2. エンドウさん
    ありがとうございます!

    ‘escape’=>false

    というプロパティがポイントっぽいですね。
    試してみます!!

  3. Html->image ではエスケープに対応していないみたいでした(TT)
    Html->linkではエスケープできるのですね。

    cake/lib/Cake/Helper/HtmlHelper.php も確認してみたのですが、そういうオプションがない(= base64のバイナリを直接表示する方法には対応していない)ようです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください