Phalcon\Db のデフォルト値対応

  • KJ
  • 2014/08/06

Phalcon でデータベースマイグレーションを行う時にデフォルト値の指定ができなかったのでできるように Phalcon のデータベース関連のクラスを更新しました。

実装

Phalcon\Db\Adapter\Pdo\Mysql

Phalcon\Db\Adapter\Pdo\Mysql::describeColumns にてデフォルト値を行います。

describeColumns メソッドは Phalcon\Db\Dialect\Mysql::describeColumns の戻り値からフィールドの状態を設定するメソッドになります。

Phalcon\Db\Dialect\Mysql::describeColumns の戻り値は

  • Field Indexes: 0:name, 1:type, 2:not null, 3:key, 4:default, 5:extra

のようになっているので 5 番目の値をデフォルト値に設定します。

if typeof field[4] != "null" {
    let definition["default"] = field[4];
}

Phalcon\Db\Adapter\Pdo\Postgresql

Phalcon\Db\Adapter\Pdo\Postgresql::describeColumns にてデフォルト値を行います。

describeColumns メソッドは Phalcon\Db\Dialect\Postgresql::describeColumns の戻り値からフィールドの状態を設定するメソッドになりますが、デフォルト値が戻り値に含まれていなかったので先に Phalcon\Db\Dialect\Postgresql を修正します。

  • Phalcon\Db\Dialect\Postgresql

    describeColumns メソッドで発行している SQL を次のように変更します。

    SELECT DISTINCT
    c.column_name AS Field,
    c.data_type AS Type,
    c.character_maximum_length AS Size,
    c.numeric_precision AS NumericSize,
    c.numeric_scale AS NumericScale,
    c.is_nullable AS Null,
    CASE WHEN pkc.column_name NOTNULL THEN 'PRI' ELSE '' END AS Key,
    CASE WHEN c.data_type LIKE '%int%' AND c.column_default LIKE '%nextval%' THEN 'auto_increment' ELSE '' END AS Extra,
    c.ordinal_position AS Position,
    c.column_default
    FROM information_schema.columns c
    LEFT JOIN (
    SELECT kcu.column_name, kcu.table_name, kcu.table_schema FROM
    information_schema.table_constraints tc
    INNER JOIN information_schema.key_column_usage kcu on (
    kcu.constraint_name = tc.constraint_name AND kcu.table_name=tc.table_name AND
    kcu.table_schema=tc.table_schema
    ) WHERE tc.constraint_type='PRIMARY KEY'
    ) pkc ON (
    c.column_name=pkc.column_name AND c.table_schema = pkc.table_schema AND
    c.table_name=pkc.table_name
    ) WHERE c.table_schema='" . schema . "' AND c.table_name='" . table . "'
    ORDER BY c.ordinal_position";
    
    • 0:name, 1:type, 2:size, 3:numericsize, 4: numericscale, 5: null, 6: key, 7: extra, 8: position, 9 default

    10 番目の値がデフォルト値(information_schema.columns.column_default)になるようにします。

デフォルト値は 'test'::character varying のような形式になっているのでメソッド内でのデフォルト値の設定は次のようにしました。

if typeof field[9] != "null" {
    let definition["default"] = preg_replace("/^'|'?::[[:alnum:][:space:]]+$/", "", field[9]);
    if strcasecmp(definition["default"], "null") == 0 {
        let definition["default"] = null;
    }
}

Phalcon\Db\Adapter\Pdo\Sqlite

Phalcon\Db\Adapter\Pdo\Sqlite::describeColumns にてデフォルト値を行います。

describeColumns メソッドは Phalcon\Db\Dialect\Sqlite::describeColumns の戻り値からフィールドの状態を設定するメソッドになります。

Phalcon\Db\Dialect\Sqlite::describeColumns の戻り値は 5 番目の値にデフォルト値が入ってくるので次のようにしました。

if strcasecmp(field[4], "null") != 0 {
    let definition["default"] = preg_replace("/^'|'$/", "", field[4]);
}

Phalcon\Db\Column

Phalcon\Db\Column クラスもデフォルト値が扱えるように変更します。

変更するのは __construct, __set_state メソッドの 2 つになります。

Phalcon\Db\Column::__construct

コンストラクタでは definition 引数に default の値があったらクラス変数のデフォルト値になるようにします。

if fetch defaultValue, definition["default"] {
    let this->_default = defaultValue;
}

Phalcon\Db\Column::__set_state

__set_state メソッドは data_default の値があったら definitiondefault に設定するようにします。

if fetch defaultValue, data["_default"] {
    let definition["default"] = defaultValue;
}

これで Phalcon のデータベース関連のクラスでもデフォルト値が扱えるようになりました。

動作サンプル

動作内容

Phalcon のバージョン 2.0.0 にマージされているので git clone で 2.0.0 ブランチを取得し、Zephir でビルドます。

$ git clone -b 2.0.0 --single-branch --depth=1 https://github.com/phalcon/cphalcon.git
$ cd cphalcon
$ ../zephir/bin/zephir compile

Phalcon の起動用に php.sh をスクリプト作成して、実行権限を付与します。

php.sh

#!/bin/sh
dir=$(cd $(dirname $0);pwd)
phplibdir=$dir/ext/modules
configextdir=`php-config --extension-dir`
if [[ "${configextdir}" =~ versions ]]; then
    extradir=`echo ${configextdir} | sed -e 's|/versions/|/pecl/|' -e 's|/\([0-9]*\.[0-9]*\)\.[0-9]*\([^/]*\)/.*$|/\1\2|g'`/modules
fi
extension_args=
for module in json.so mysqlnd.so mysqlnd_mysql.so pdo.so pdo_mysqlnd.so
do
    if [ -f ${configextdir}/${module} ]; then
        cp ${configextdir}/${module} ${phplibdir}/${module}
    elif [ -n "${extradir}" -a -f ${extradir}/${module} ]; then
        cp ${extradir}/${module} ${phplibdir}/${module}
    fi
    extension_args="${extension_args} -d extension=${module}"
done
PHP_INI_SCAN_DIR=$dir php -d extension_dir=$dir/ext/modules ${extension_args} -d extension=phalcon.so "$@"
$ chmod 755 php.sh

MySQL を実行して、デフォルト値の定義を含む適当なテーブルを作ります。

$ mysql -u root testdb
mysql> CREATE TABLE `personas` (
    ->  `cedula` char(15) COLLATE utf8_unicode_ci NOT NULL,
    ->  `nombres` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
    ->  `email` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
    ->  `fecha_nacimiento` date DEFAULT '1970-01-01',
    ->  `ciudad_id` int(10) unsigned DEFAULT '0',
    ->  PRIMARY KEY (`cedula`),
    ->) ENGINE=InnoDB;
mysql> desc personas;
+------------------+------------------+------+-----+------------+-------+
| Field            | Type             | Null | Key | Default    | Extra |
+------------------+------------------+------+-----+------------+-------+
| cedula           | char(15)         | NO   | PRI | NULL       |       |
| nombres          | varchar(100)     | NO   |     |            |       |
| email            | varchar(50)      | YES  |     | NULL       |       |
| fecha_nacimiento | date             | YES  |     | 1970-01-01 |       |
| ciudad_id        | int(10) unsigned | YES  |     | 0          |       |
+------------------+------------------+------+-----+------------+-------+

作成したテーブルを Phalcon\Db\Adapter\Pdo\Mysql::describeColumns メソッドで確認してみます。

desc.php

<?php
$connection = new Phalcon\Db\Adapter\Pdo\Mysql([
    'host' => 'localhost',
    'username' => 'root',
    'password' => '',
    'dbname' => 'testdb'
]);
var_dump($connection->describeColumns('personas'));
$ ./php.sh desc.php
array(5) {
  [0]=>
  object(Phalcon\Db\Column)#5 (14) {
    ["_name":protected]=>
    string(6) "cedula"
    ["_schemaName":protected]=>
    NULL
    ["_type":protected]=>
    int(5)
    ["_isNumeric":protected]=>
    bool(false)
    ["_size":protected]=>
    int(15)
    ["_scale":protected]=>
    int(0)
    ["_default":protected]=>
    NULL
    ["_unsigned":protected]=>
    bool(false)
    ["_notNull":protected]=>
    bool(true)
    ["_primary":protected]=>
    bool(true)
    ["_autoIncrement":protected]=>
    bool(false)
    ["_first":protected]=>
    bool(true)
    ["_after":protected]=>
    NULL
    ["_bindType":protected]=>
    int(2)
  }
  [1]=>
  object(Phalcon\Db\Column)#4 (14) {
    ["_name":protected]=>
    string(7) "nombres"
    ["_schemaName":protected]=>
    NULL
    ["_type":protected]=>
    int(2)
    ["_isNumeric":protected]=>
    bool(false)
    ["_size":protected]=>
    int(100)
    ["_scale":protected]=>
    int(0)
    ["_default":protected]=>
    string(0) ""
    ["_unsigned":protected]=>
    bool(false)
    ["_notNull":protected]=>
    bool(true)
    ["_primary":protected]=>
    bool(false)
    ["_autoIncrement":protected]=>
    bool(false)
    ["_first":protected]=>
    bool(false)
    ["_after":protected]=>
    string(6) "cedula"
    ["_bindType":protected]=>
    int(2)
  }
  [2]=>
  object(Phalcon\Db\Column)#6 (14) {
    ["_name":protected]=>
    string(5) "email"
    ["_schemaName":protected]=>
    NULL
    ["_type":protected]=>
    int(2)
    ["_isNumeric":protected]=>
    bool(false)
    ["_size":protected]=>
    int(50)
    ["_scale":protected]=>
    int(0)
    ["_default":protected]=>
    NULL
    ["_unsigned":protected]=>
    bool(false)
    ["_notNull":protected]=>
    bool(false)
    ["_primary":protected]=>
    bool(false)
    ["_autoIncrement":protected]=>
    bool(false)
    ["_first":protected]=>
    bool(false)
    ["_after":protected]=>
    string(7) "nombres"
    ["_bindType":protected]=>
    int(2)
  }
  [3]=>
  object(Phalcon\Db\Column)#7 (14) {
    ["_name":protected]=>
    string(16) "fecha_nacimiento"
    ["_schemaName":protected]=>
    NULL
    ["_type":protected]=>
    int(1)
    ["_isNumeric":protected]=>
    bool(false)
    ["_size":protected]=>
    int(0)
    ["_scale":protected]=>
    int(0)
    ["_default":protected]=>
    string(10) "1970-01-01"
    ["_unsigned":protected]=>
    bool(false)
    ["_notNull":protected]=>
    bool(false)
    ["_primary":protected]=>
    bool(false)
    ["_autoIncrement":protected]=>
    bool(false)
    ["_first":protected]=>
    bool(false)
    ["_after":protected]=>
    string(5) "email"
    ["_bindType":protected]=>
    int(2)
  }
  [4]=>
  object(Phalcon\Db\Column)#8 (14) {
    ["_name":protected]=>
    string(9) "ciudad_id"
    ["_schemaName":protected]=>
    NULL
    ["_type":protected]=>
    int(0)
    ["_isNumeric":protected]=>
    bool(true)
    ["_size":protected]=>
    int(10)
    ["_scale":protected]=>
    int(0)
    ["_default":protected]=>
    string(1) "0"
    ["_unsigned":protected]=>
    bool(true)
    ["_notNull":protected]=>
    bool(false)
    ["_primary":protected]=>
    bool(false)
    ["_autoIncrement":protected]=>
    bool(false)
    ["_first":protected]=>
    bool(false)
    ["_after":protected]=>
    string(16) "fecha_nacimiento"
    ["_bindType":protected]=>
    int(1)
  }
}