結構根が深くていやになったのだが、表題の問題。symfonyの(というかPropelの問題なので以降Propelの)Criteriaの記述で、集約関数を書きたくて調べていたのだが、addSelectColumnという関数で下記のように定義してやればいいっぽい。

$c->addSelectColumn('SUM('.TablePeer::TARGET_COLUMN.')');

このとき、これだけなら問題はあるもののひとまずエラーにはならない(後述)。だが、たとえば時刻の差分(秒)の合計をとりたい場合、SUM(UNIX_TIMESTAMP(col1) – UNIX_TIMESTAMP(col2))のようにしたくなる。それを書いてみたのが下記。

$c->addSelectColumn('SUM(UNIX_TIMESTAMP('.TablePeer::FINISHED_AT.')-'.
  
                        'UNIX_TIMESTAMP('.TablePeer::STARTED_AT.'))');

これが見事に刺さる。

調べてみると、見事にPropelのBUGで、#425 (Bug in BasePeer::createSelectSql) – Propel – TracによればPropel本体としては既にfixedというステータス。これがsymfony側には反映されていなくて刺さるということのようだ。回避策は今のところこのTracに記述されているworkaroundをそのまま実行するしかない。念のため書き写しておくとこうなる。

577 $selectClause[] = $columnName; // the full column name: e.g. MAX(books.price)
578
579 $parenPos = strrpos($columnName, '(');
580 $dotPos = strpos($columnName, '.', ($parenPos !== false) ? $parenPos : 0);

下線部が追加部分。strRposにするのと、その下の行を追加する。これでひとまずエラーはなくなる。

エラーは無くなるのだが、addSelectColumnを使ったクエリ、明示的にクリアしていないにもかかわらず選択カラムがリセットされてしまって、addSelectColumnで追加したカラムのみになってしまうという問題がある。これが仕様なのかはともかく、doSelectしたときにhydrateできなくて刺さったり、いろいろと問題がある。この辺の本当の意味での正しいクエリの投げ方、どうすればいいんだろう?

追記:とりあえずメソッドを眺めていたら、TablePeer::addSelectColumns(Criteria $c)というメソッドを見つけた。とりあえず、addSelectColumnの先頭でaddSelectColumns($c)とかやってやると一通り追加される模様。

Something to say?