k-tanakaのエンジニア日記

Changing合同会社を設立しての仕事・生活、エンジニアとしてのブログ

#FuelPHPでコアクラスを拡張・置換してDBのorder_byでDE::exprを利用できるようにした話。(COLLATE utf8_unicode_ciしたかった)

追記 別に拡張しなくても解決しました−。

<?php
->order_by(DB::expr('`column` COLLATE utf8_unicode_ci'), 'asc');

とすれば良かっただけ(第一引数は、DB::exprに対応)でした。 一応、下記も残しておきます。


手軽に結果を出せる言語としてPHPを利用してきました。PHP7になってパフォーマンスも上がり、やや言語人気ランキングで下がってきているとはいえ、未だ高い人気のPHPです。そのPHP用のフレームワークも大量にあるのですが、私は動作の機敏さとシンプルさからずっとFuelPHPを利用しています。(昔、CakePHP1.3から初めてFuelPHPに移った時のパフォーマンスの差にはびっくりしました。)

そんなFuelPHPですが、コアクラスの拡張と置換がわりと自由という特徴も持っています。とはいえ、バージョンアップなどで問題を起こしかねないということもあり、あまりコアクラスの置換は行われず、拡張に留まるのがほとんどです。かくいう私も、拡張は何回か行っているものの置換えはあまり行ってきませんでした。しかし、行うことになったので、その内容を共有したいと思います。

きっかけは、order_by

varcharカラムで、半角数字と全角数字が混在するカラムについて、半角と全角を区別せず、数値順に並び替えを行いたかったのですが、ORDER BY column ASC では、上手くいきません。 調べてみると、 ORDER BY column COLLATE utf8_unicode_ci ASC であれば、行けることが判明しました。そこで、FuelPHPのDBクラスを用いて次のように書いてみたわけです。

<?php
//普通にDBクラスでDB::exprを使ってみる
$query = DB::select()->from('table')
                    ->order_by('column', DB::expr('COLLATE utf8_unicode_ci ASC'));

そうすると、上手く動作しません。ソースを見てみると、order_byの2つめの引数は、direction(方向)として「ASC」か「DESC」かのどちらかでないとダメで、ダメなら自動でASCになるようになっています。

で、上記を利用したくて、該当部分にDB::exprを利用できるようにしたくなったわけです。

コアクラスの拡張の仕方

公式ドキュメント(Core の拡張 - 概要 - FuelPHP ドキュメント)によると、コアクラスの拡張には、以下の2種類があります。

  • コアクラスの拡張
  • コアクラスを拡張して置換え

拡張は、文字通り拡張して利用するだけで、コア側からそのクラスを利用されることは無いが、置換えの場合にはその置き換えたクラスをコアも利用するということで問題が生じうるということでした。 今回拡張したいクラスは、DBクラスが、order_by部分を設定する関数「_compile_order_by()」が存在するDatabase_Query_Builderで、このクラスは、コアクラスであるDBクラスから呼び出されて利用されクエリを構成しているので、拡張したものを利用されないと意味がありません。そのため、「置換え」となります。

実際の拡張と置換え

ファイルをコピペ

fuel/core/classes/database/query/builder.php を fuel/app/classes にコピペ 置換えになるので、弄る部分以外はそのまま利用したいのでコピペしました。

関数を弄る

コピペしたbuilder.phpの、一番最初のコメントとnamespace Fuel\Core;を削除(不要?)

_compile_order_by()関数を以下に変更

<?php
     protected function _compile_order_by(\Database_Connection $db, array $columns)
     {
         $sort = array();

         foreach ($columns as $group)
         {
             list($column, $direction) = $group;

             if ($direction instanceOf Database_Expression)
             {
                 $direction = ' '. (string) $direction;
             }
             else {
                 $direction = strtoupper($direction);
                 if(!empty($direction)) {
                     // Make the direction uppercase
                     $direction = ' ' . ($direction == 'ASC' ? 'ASC' : 'DESC');
                 }
             }

             $sort[] = $db->quote_identifier($column).$direction;
         }

         return 'ORDER BY '.implode(', ', $sort);
     }

$directionが、DB::exprインスタンスであれば、スペースを付与して、文字列としてそのまま格納する、という変更ですね。

fuel/app/bootstrap.phpに行を追加

fuel/app/bootstrap.phpに以下のような行があるので、

<?php
\Autoloader::add_classes(array(
    // Add classes you want to override here
    // Example: 'View' => APPPATH.'classes/view.php',
));

当該配列に次のように追加

<?php
\Autoloader::add_classes(array(
    // Add classes you want to override here
    // Example: 'View' => APPPATH.'classes/view.php',
    'Database_Query_Builder' => APPPATH. 'classes/builder.php'
));

で、完了

これにて完了です。これで、無事に order by 出来ました。

違う話

はてなブログのMarkdown記法によるコードシンタックスハイライトは、```phpで囲むだけでは、足りず<?phpで始めないと行けないのですね。。。