kikukawa's diary

都内で活動するシステムエンジニアが書いてます。 興味を持った技術やハマったポイント、自分用メモをつけてます。 最近はweb中心

cakephpでsjisのデータベースを使う

データベースの文字コードsjisで、ソースコードはUTF8という状況です。
基本的にはdatabase.phpの設定でsjisの設定をするだけで使えます。

        'default' => array(
            'datasource' => 'Mysql'
            'persistent' => false,
            'host' => 'host',
            'login' => 'login',
            'password' => 'password',
            'database' => 'database',
            'prefix' => '',
            'encoding' => 'sjis',
        ),

ただし、このままだとphp5C問題に引っ掛かります。
特定の文字で文字化けを起こします。
回避するためにMysql.phpのconnectメソッドをオーバーライドします。
PDOを作る時のデータベース接続文字列にエンコードの指定をしてあげます。
継承するクラスは下記にあります。

Cake\Model\Datasource\Database\Mysql.php

phpは5.3,cakeは2.2というバージョンで動作確認していますが、
2系の最新のcakeでもMysql.phpのソースが変っていなかったので同じだと思います。

下記の場所にクラスを作って、database.phpのdatasourceで作成したクラスを指定します。
必要があれば、App::buildでパスを追加して下さい。
私の場合は、かなりディレクトリ構成を変えていたため、追加する必要がありました。

App\Model\Datasource\Database\MysqlCharaSet.php
<?php

App::uses('DboSource', 'Model/Datasource');
App::uses('Mysql', 'Model/Datasource/Database');

class MysqlCharaSet extends Mysql {

    public function connect() {

        $config = $this->config;
        $this->connected = false;
        try {
            $flags = array(
                PDO::ATTR_PERSISTENT => $config['persistent'],
                PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
            );
            if (!empty($config['encoding'])) {
                $flags[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES ' . $config['encoding'];
            }
            if (empty($config['unix_socket'])) {
                $dsn = "mysql:host={$config['host']};port={$config['port']};dbname={$config['database']}";
            } else {
                $dsn = "mysql:unix_socket={$config['unix_socket']};dbname={$config['database']}";
            }
            if (!empty($config['encoding'])) {
                $dsn .= ';charset='.$config['encoding'];
            }
            $this->_connection = new PDO(
                    $dsn, $config['login'], $config['password'], $flags
            );
            $this->connected = true;
        } catch (PDOException $e) {
            throw new MissingConnectionException(array('class' => $e->getMessage()));
        }

        $this->_useAlias = (bool) version_compare($this->getVersion(), "4.1", ">=");

        return $this->connected;
    }

}

この部分が追加した処理です。 PDO自体に使用する文字コードを教えてあげることで適切な処理をエスケープをしてくれるようになります。

            if (!empty($config['encoding'])) {
                $dsn .= ';charset='.$config['encoding'];
            }

あとは、通常通りModelから操作するだけで大丈夫なはずです。