FuelPHP SimpleAuth の対象テーブルで updated_at を DATETIME 型にすると発生する SQL エラーの回避方法
前提条件
SimpleAuth
FuelPHP の Auth パッケージに附属している SimpleAuth は、データベースのテーブルを使った認証システムを構築するのに便利な機能を持っていますので、そのまま利用しているケースも多いと思います。
  SimpleAuth は config/simpleauth.php で table_name に設定したテーブルを利用します。(デフォルトは "users"。)このテーブルの定義を作成するコマンドも Auth パッケージに含まれていて次のようにすることで migration ファイルが作成されます。
| 
					 1 2 3  | 
						$ oil refine migrate --packages=auth  | 
					
DATETIME 型
一方、FuelPHP のテーブル定義には created_at と updated_at という項目が含まれ、この型はデフォルトで int(11) となっています。これを MySQL の TIMESTAMPE 型、あるいは、DATETIME 型に対応させることが可能です。よく知られているように、対応するモデルの Model_Crud を継承したクラスで、 以下のようにプロパティを設定します。
| 
					 1 2 3 4 5 6 7 8 9  | 
						class Model_User extends Model_Crud {     protected static $_table_name = 'users';     protected static $_created_at = 'created_at';     protected static $_updated_at = 'updated_at';     //DBがunix_timestamp ではなく MySQL の timestamp/datetime 型にする     protected static $_mysql_timestamp = true;  | 
					
このように users テーブルの created_at, updated_at を DATETIME 型に変更しても、これまでは SimpleAuth を使えていました。
MaraiDB のバージョンを上げたらエラーが発生
  しかし、MaraiDB のバージョンを 10.1 系から 10.2 系に上げた際に、SimpaleAuth の create_user や update_user で SQL のエラーが発生するようになりました。エラーの内容は次のようなものです。
| 
					 1 2 3 4 5 6 7  | 
						SQLSTATE[22007]: Invalid datetime format: 1292 Incorrect datetime value:  '1545375841' for column 'updated_at' at row 1 with query:     "UPDATE `users` SET `password` = '************************', `updated_at` = 1545375841     WHERE `username` = '******'"   in /*********/fuel/core/classes/database/pdo/connection.php on line 223  | 
					
‘updated_at’ は DATETIME 型なのに int(11) の値をセットしようとしてエラーとなっています。
SQL モードの確認
これは、MaraiDB を 10.2 に上げたことで、sql_mode という変数の初期値が変わったことがきっかけで出ていました。SHOW VARIABLES で sql_mode の値を確認してみます。
MariaDB 10.1 (エラー無し)
| 
					 1 2 3 4 5 6 7 8 9  | 
						MariaDB [(none)]> SHOW VARIABLES LIKE "%sql_mode%"; +---------------+--------------------------------------------+ | Variable_name | Value                                      | +---------------+--------------------------------------------+ | sql_mode      | NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | +---------------+--------------------------------------------+ 1 row in set (0.00 sec)  | 
					
SQL モードに STRICT_TRANS_TABLES は含まれていません。
MariaDB 10.2 (エラーあり)
| 
					 1 2 3 4 5 6 7 8 9  | 
						MariaDB [(none)]> SHOW VARIABLES LIKE "%sql_mode%"; +---------------+-------------------------------------------------------------------------------------------+ | Variable_name | Value                                                                                     | +---------------+-------------------------------------------------------------------------------------------+ | sql_mode      | STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | +---------------+-------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)  | 
					
MariaDB 10.2 の初期設定で SQLモードに STRICT_TRANS_TABLES が加わったことで、これまで UNIX タイムスタンプの int 型が自動的に DATETIME 型に置き換わっていたケースが、エラーになるようになったようです。
MariaDB 10.2 だけでなく、MySQL 5.6 でも sql_mode の初期値は STRICT_TRANS_TABLESです。
SQL モードの変更で回避
MaraiDB の設定ファイル /etc/my.cnf を編集して、sql_mode を変更します。
| 
					 1 2 3 4 5  | 
						[mysqld] ... sql_mode='NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'  | 
					
MariaDB サーバーを再起動すればエラーが出なくなります。なので手っ取り早く解決するにはこの方法で逃げることができます。
エラーの原因
今回のケースは SimpleAuth の中で、created_at や updated_at を int 型(UNIX タイムスタンプを保存)で決め打ちしてコーディングされていることです。
| 
					 1 2 3  | 
							$update['updated_at'] = \Date::forge()->get_timestamp();  | 
					
Model_Crud の場合は update_at の更新のために、以下のように $_mysql_timestamp によってセットする値を変えています。
| 
					 1 2 3 4 5 6 7 8 9 10  | 
						if(isset(static::$_mysql_timestamp) and static::$_mysql_timestamp === true) {     $vars[static::$_updated_at] = \Date::forge()->format('mysql'); } else {     $vars[static::$_updated_at] = \Date::forge()->get_timestamp(); }  | 
					
SimpleAuth でも、同じ対応をすれば解決しそうです。
SimpleAuth の改良
対応方針としては、config/simpleauth.php に table_mysql_timestamp のような追加項目を設け(デフォルトは false)て、Model_Crud のように処理を分岐するようにします。
具体的には created_at, updated_at および last_login をセットする部分の前に
| 
					 1 2 3 4 5 6 7 8 9 10  | 
								if(\Config::get('simpleauth.table_mysql_timestamp')) 		{ 			$update['updated_at'] = \Date::forge()->format('mysql'); 		} 		else 		{ 			$update['updated_at'] = \Date::forge()->get_timestamp(); 		}  | 
					
のようなコードを入れて、その値をクエリビルダーでセットするようにそれぞれ修正しました。
これで、 MariaDB サーバーの sql_mode を元通り STRICT_TRANS_TABLES に戻しても、エラーを出さないようになりました。
ITエンジニア募集中!
キュアコード株式会社はITエンジニアを募集しております。少人数の職場なので、上流・下流やサーバー・クライアント対応の垣根なく、あなたの強みを活かしながら いろいろなことにチャレンジ可能です。エンジニアとしての未経験の方、経験が少ない方も歓迎しています。
下記よりITエンジニア募集の採用情報をご覧いただけます。
キュアコード公式インスタグラム
キュアコード株式会社の新サービス情報や、オフィスライフの素敵な瞬間まで。私たちの日々の営みをご紹介します。






