ラベル Twig の投稿を表示しています。 すべての投稿を表示
ラベル Twig の投稿を表示しています。 すべての投稿を表示

2014年6月5日木曜日

symfony2でCSV出力(ダウンロード)する

symfony2シリーズ第3弾です。
CSV出力、何かと言われて実装することが多いのではないでしょうか。
FuelPHPではRESTコントローラがCSV対応しており、配列渡すだけなので久しぶりに実装しました。
と言っても良くある実装なのでsymfony2でActionで呼ばれた時の場合でご紹介します。

public function exportAction()
    {
        $header = [
            'ID',
            '名前',

        ];
        $list[] = $header;
        //DBから呼び出した場合はgetArrayResultを使うか自分で$dataを整形して下さい。
        $list[] = [1,'hoge'];
        $list[] = [2,'fuga'];

        $csv = $this->convertArrayToCsv($list);

        $response = $this->render(
                "AcmeSampleBundle::export.html.twig", [
            'csv' => $csv,
                ]
        );

        //Excel対策でUTF-8からSJIS-winに変換
        $contents = mb_convert_encoding($response->getContent(), 'SJIS-win', 'UTF-8');

        //headerのSET
        $response->headers->set('Content-Type', "application/octet-stream; name=hoge.csv");
        $response->headers->set('Content-Disposition', "attachment; filename=hoge.csv");
        $response->setContent($contents);

        return $response;
    }


    //配列をCSVに変換。
    //文字列のエスケープをしてくれるのでfputcsv()を利用
    private function convertArrayToCsv($list)
    {
        $fp = fopen('php://temp', 'r+b');
        foreach ($list as $fields) {
            fputcsv($fp, $fields);
        }
        rewind($fp);
        $tmp = str_replace(PHP_EOL, "\r\n", stream_get_contents($fp));
        return $tmp;
    }

表示するTwig側(例ではexport.html.twig

{{ csv|raw }}

としてます。
rawを使わないとcsv内のクォートなどをエスケープするので忘れないでください。

CSVから配列にする場合のメソッドはこちらです。

PHPでCSVから配列を作る



2014年6月4日水曜日

twigとPHPとSQLでの年齢計算

年齢計算は良く出てくることなんだけどTwigでやる時のメモ。


Twig

TwigにはPHPの関数が使えないので計算で出します。
birthdayには日付型のデータが入っているとして

{% set age = "now"|date('Y') - birthday|date('Y') %}
{% if ("now"|date('m/d') < birthday|date('m/d')) %}
    {% set age = age -1 %}
{% endif%}

で出ます。

(今年 - 誕生日年)


をして年を外した日付で比較して誕生日が来てなかったら1引くというロジックですね。

PHP

PHPはtime()で引き算したりいろいろあるのですが個人的に一番好きな方法をご紹介します。
ついでにDateTimeクラスのdiff()は5.3以降のPHPじゃないと動きません。

$birthday = new \DateTime("'2014-06-04'");
$now = new \DateTime();
$interval = $now->diff($birthday);
$age = $interval->y;

MySQL

MySQLはTwigと同様に計算で出します。

SELECT
  (YEAR(CURDATE()) - YEAR(birthday)) - (RIGHT(CURDATE(), 5) < RIGHT(birthday, 5)) AS age
FROM
 member

PostgreSQL

PostgreSQLにはage関数があります。

日付/時刻関数と演算子

なので単純に

SELECT
 age(now(),birthday)
FROM
 hoge

で差分の日付型が帰ってくるので頭の年を取得して。

SELECT
 extract(year from age(now(),birthday)) AS age
FROM
 hoge

こんな感じです。
むしろPostgreSQLはFrom句が無くても動くので

SELECT extract(year from age(now(),date '2013-06-04'))  AS age

SELECT extract(year from age(now(),now() + '-10 year'))  AS age

とか動きます。
と言うことでよく使う年齢の計算方法でした。

2014年5月8日木曜日

Symfony2のTwigExtentionをからContainerやEntityManagerを呼び出す

Symfony2は重厚で大規模開発には非常に優秀だと思います。
そんなSymfony2ですがちょっと手の込んだ事を調べると日本語情報が少ない印象です。
英語の公式ドキュメントとstackoverflow.comを行ったり来たりした結果をメモとして残しておきます。

やりたいこと


  1. Symfony2でTwigExtentionを作る
  2. 作ったTwigExtentionのgetGlobals()からDBの呼び出しやサービスの呼び出しをする
  3. base.html.twigの変数にDBから取り出した値を使う。

この2.をするのに情報がなかったです。
1.については公式ドキュメントがありますのでそちらを参考にしてください。

カスタムTwig拡張の書き方


さて本題ですがTwigExtentionをサービスコンテナに登録しましょう。
その際に

# src/Acme/DemoBundle/Resources/config/services.yml
services:
    acme.twig.acme_extension:
        class: Acme\DemoBundle\Twig\AcmeExtension
        tags:
            - { name: twig.extension }

    arguments:
            em: "@doctrine.orm.entity_manager"
            container: "@service_container"

とargumentsとしてEntityManagerとContainerを渡します。
次にTwigExtentionクラスのコンストラクタに
public function __construct($em, $container)
    {
        $this->em = $em;
        $this->container = $container;
    }
と記載します。
あとはControllerと同様に各メソッドからDoctrine2のmodelを呼び出すだけです。
実際にbase.html.twigの初期値にDBから取り出したデータを使いたいときはgetGlobals()をオーバーライドします。
public function getGlobals()
    {

        $var = $this->container
                ->get('my_domain.hoge_repository')
                ->getHoge();
        return [
            'hoge' => $hoge,
            'fuga' => 'fuga',
        ];
    }
と言った感じです。
base.html.twigの拡張すればかなりDRYにすることが出来るます。
ただしTwigExtentionでDBを呼び出すと毎回呼ばれます。
ですので出来るだけ回数を減らす必要があると思います。

Symfony2シリーズ続くかわかりませんが現場からは以上です。