人気のPHP WEBアプリケーションフレームワークLaravelのTipsを記録していきます

Laravel で DBの数値型カラムが文字列型で返ってくる場合の対処法

● Laravel で DBの数値型カラムが文字列型で返ってくる場合の対処法

Laravelはもちろん(Laravel に限らず PHP で PDO を使っている場合)DBの数値型カラムが文字列型で返ってくる事があります。
なぜこのような自動型変換が起こるかというと、PHPのMySQL PDOドライバの仕様だそうです。

ただし、次の条件の時に

1. mysqlのドライバーが 「mysqlnd(MySQLNaitiveDriver)」である。
2. PDO::ATTR_EMULATE_PREPARES属性が false である。

自動型変換を回避することが可能です。

● 1. phpinfo() を使って mysqlnd ドライバーが使用されているかどうかチェックする

phpinfo() の出力から確認しましょう。

または次のコマンドで mysqlnd が表示されれば使用されています

php -m | grep mysqlnd

● 2. LaravelアプリのPHPのPDOオプションをチェックする

        $dbh = \DB::connection()->getPdo();

        $attributes = [
            "ATTR_CLIENT_VERSION",
            "ATTR_EMULATE_PREPARES",
            "ATTR_STRINGIFY_FETCHES",
            "ATTR_AUTOCOMMIT",
            "ATTR_ERRMODE",
            "ATTR_CASE",
            "ATTR_ORACLE_NULLS",
            "ATTR_PERSISTENT",
            "ATTR_PREFETCH",
            "ATTR_SERVER_INFO",
            "ATTR_SERVER_VERSION",
            "ATTR_TIMEOUT",
        ];

        foreach ($attributes as $val) {
            echo "PDO::{$val}: ";
            try {
                echo $dbh->getAttribute(constant("PDO::{$val}")) . "<br>\n";
            } catch (\Exception $e) {
                echo "error not supported !!! <br>\n";
            }
        }

結果例

PDO::ATTR_CLIENT_VERSION: mysqlnd 5.0.12-dev - 20150407 - $Id: 38fea24f2847fa7519001be390c98ae0acafe387 $
PDO::ATTR_EMULATE_PREPARES: 0

↑ この例の場合は(mysqlnd有り)(PDO::ATTR_EMULATE_PREPARES: 0)なので、数値型カラムの値は数値型で返ってきます。

レンタルサーバで(おそらく昔の)XSERVERとかは mysqlnd が入ってないので、自動型変換は必ず起きると思われます。

● mysqlnd が入っていないサーバーで MySQLの自動型変換をさせないようにする

Laravel の モデルには $casts プロパティがあり、これに型をセットすると、Eloquentが結果セットを返すときに、明示的にその方にキャストしてくれます。

    /**
     * 明示的なdb型変換
     *
     */
    protected $casts = [
        'id'                => 'int' ,
        'shop_id'           => 'int' ,
        'is_active'         => 'int' ,
        'price_no'          => 'int' ,
    ];

● いちいちデータベースの定義を見て、キャストを作成するのがめんどくさい

次のコードを実行してください。自動で生成します。( int のみ。) コントローラーから次のようなメソッドを実行します

    /**
     * Laravelのモデルの Casts を生成する
     *
     * [int, integer, real, float, double, string, bool, boolean, object, array, json, collection, date, datetime]
     *
     *
     */
    public function getModelCasts(string $table_name = '')
    {
        print "<hr>\n";
        echo "<strong>{$table_name}</strong>";
        print "<hr>\n";
        echo "<pre style='margin: 20px; border: solid #eee 1px;'>";

print <<< 'DOC_END'
/**
 * カラムの明示的なdb型変換
 *
 */
protected $casts = [

DOC_END;

        $query = "SHOW COLUMNS FROM {$table_name}";
        foreach (\DB::select($query) as $column) {
            if     (preg_match("{^int}", $column->Type)) {echo "'{$column->Field}' => 'int' ,\n";} 
            elseif (preg_match("{^tinyint}", $column->Type)) {echo "'{$column->Field}' => 'int' ,\n";} 
            elseif (preg_match("{^smallint}", $column->Type)) {echo "'{$column->Field}' => 'int' ,\n";} 
            elseif (preg_match("{^mediumint}", $column->Type)) {echo "'{$column->Field}' => 'int' ,\n";} 
            elseif (preg_match("{^bigint}", $column->Type)) {echo "'{$column->Field}' => 'int' ,\n";} 
            elseif (preg_match("{^(text|char|varchar)}", $column->Type)) {} 
            elseif (preg_match("{^}", $column->Type)) {} 
            else {
                dump($column);
            }
        }

print <<< 'DOC_END'
];
DOC_END;

        echo "</pre>";
        print "<hr>\n";
    }
No.1508
05/10 10:33

edit