読者です 読者をやめる 読者になる 読者になる

たけまるの日記

たけまるの日記です。web関係の技術ネタが多いですが、好きなことを適当に書いています。

CakePHPのMigrationでMySQLのmediumtextを扱う方法

PHP CakePHP

従来手動でAlter管理していたDBにCakePHPのMigrationを導入しようとした所、mediumtext/longtextを扱うのにハマったのでメモ。

MySQLのtext型は最長65,535byteの可変長文字列型ですが、UTF8換算で約2万文字となり、少し容量が足りずmediumtext(最長16MB)を使いたくなる場面があります。しかしCakePHPのMigration Pluginを使ってみると、どうもtext型のみのサポートのようでした。

以下の様なテーブルをまずは作成します。

mysql> CREATE TABLE testtable (id int, message text, medium_message mediumtext, long_message longtext);

これを元にMigrationファイルを作成します。

$ Console/cake Migrations.migration generate -f

結果作成されたMigrationファイルは

'id' => array('type' => 'integer', 'null' => true, 'default' => null, 'unsigned' => false, 'key' => 'primary'),
'message' => array('type' => 'text', 'null' => true, 'default' => null, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
'medium_message' => array('type' => 'text', 'null' => true, 'default' => null, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
'long_message' => array('type' => 'text', 'null' => true, 'default' => null, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),

となり、text/mediumtext/longtextを区別せず、すべてtext型として認識されてしまいました。

これでは困るのでCakePHPのコア部分に手を入れて対応することにしました。
とはいえコアファイルを直接編集するわけではなく、CakePHPのクラスオーバーライドの仕組みを利用します。これはapp/Libにコアファイルと同名のクラスファイルを置くと、該当クラスをロードする時にコアのクラスではなく、app/Lib側のクラスに置き換えて読み込む機能です。これでCakePHPのコア自体をアプリのレポジトリに入れなくても済みます。
詳しくは以下をご覧ください。
CakePHP のクラスをオーバーライドする

実際に書き換えた部分は以下です。
元となるMySQLクラスはCakePHP 2.5.8のものです。

## project_path/app/Lib/Model/Datasource/Mysql.php
    // 116行付近
    public $columns = array(
        'primary_key' => array('name' => 'NOT NULL AUTO_INCREMENT'),
        'string' => array('name' => 'varchar', 'limit' => '255'),
        'mediumtext' => array('name' => 'mediumtext'),
        'longtext' => array('name' => 'longtext'),
        'text' => array('name' => 'text'),
    
    // 777行付近
        if (strpos($col, 'mediumtext') !== false) {
            return 'mediumtext';
        }
        if (strpos($col, 'longtext') !== false) {
            return 'longtext';
        }
        if (strpos($col, 'text') !== false) {
            return 'text';
        }

全内容はこちら。
Gist : Mysql.php

これでmediumtext/longtextを利用することができるようになりました。

ちなみにmediumtext/longtextを公式サポートしない理由としては、CakePHPとしてはMySQLPostgreSQL等、DBMSが変わっても同じアプリケーションコードで動くことを目指しているため、DBMSによってかき分ける必要がないようにするのが困難なため、という所だと理解しました。
参考 : LONGTEXT fields on schema generate

今回修正したコードもMySQLを使うことだけを考慮したものですので、そのあたりご理解の上参考にしていただければと思います。