STAFF BLOG スタッフブログ

FuelPHPでUTF8絵文字をMySQLに保存する方法

UTF8の絵文字に嫌悪感を憶える吉田です。
スマホでもPCでも絵文字を簡単に入力できるようになってしまったせいで世界中がガラケー時代に戻ったかと勘違いする状況です。(Twitterやらインスタやら)
そんなこんなでCMSでも絵文字の対応が必須になって来たので今回は絵文字を扱う場合の注意点を紹介します。

STEP1: データベースとテーブルの「CHARACTER SET utf8mb4」にする

今までは絵文字のことを考慮していなかったのでDBを作成する際に「CHARACTER SET utf8」だったのですが、このままSQLでINSERTすると当然エラーになるのでutf8mb4で作成します。
ついでに実験用のテーブルも作成。
CREATE DATABASE IF NOT EXISTS emoji DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
USE emoji;

CREATE TABLE IF NOT EXISTS posts (
  id int(11) NOT NULL,
  title varchar(200) NOT NULL,
  contents text NOT NULL,
  created_at datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE posts ADD PRIMARY KEY (id);

ALTER TABLE posts MODIFY id int(11) NOT NULL AUTO_INCREMENT;

STEP2: コントローラーの作成

<?php
class Controller_Emoji extends Controller_Hybrid
{
	public $template = '';
	protected $format = 'json';

	public function action_index()
	{
		// 記事の一覧を取得
		$data = array(
			'list' => array(),
		);
		$data['list'] = DB::select('*')->from('posts')->as_object()->execute();
		if ( ! $data['list'] )
		{
			$data['list'] = array();
		}

		// 画面を表示
		return Response::forge(View::forge('emoji/index', $data));
	}

	public function post_insert()
	{
		$json = array(
			'res' => 'NG',
			'msg' => '',
		);

		// 入力チェック
		$title    = Input::post('title', '');
		$contents = Input::post('contents', '');
		if ( strlen($title) <= 0 || strlen($contents)  response($json);
		}

		// 記事の登録
		DB::insert('posts')->set(
			array(
				'title'      => $title,
				'contents'   => $contents,
				'created_at' => Date::forge()->format('%Y-%m-%d %H:%M:%S'),
			)
		)->execute();

		$json['res'] = 'OK';
		return $this->response($json);
	}
}
action_indexは登録されているレコードを一覧表示するだけ、post_insertはPOST送信されてきたデータをDBに保存するだけの簡単な処理内容です。
実験なのでバリデーション処理などは省略しています。

STEP3: ビューの作成

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>絵文字テスト</title>
	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
	<div class="container">
		<div class="row">
			<div class="col-md-12">
				<h2>記事の入力</h2>
				<form id="blog_form">
					<div class="form-group">
						<label for="title">タイトル</label>
						<input type="text" name="title" class="form-control" id="title">
					</div>
					<div class="form-group">
						<label for="contents">本文</label>
						<textarea name="contents" id="contents" class="form-control" rows="3"></textarea>
					</div>
					<button id="post_btn" class="btn btn-success">記事の登録</button>
				</form>
			</div>
		</div>
		<hr>
		<div class="row">
			<div class="col-md-12">
				<h2>記事の一覧</h2>
				<table class="table table-striped table-bordered">
					<tr>
						<th>ID</th>
						<th>タイトル</th>
						<th>本文</th>
						<th>作成日時</th>
					</tr>
				<?php
					foreach ( $list as $v )
					{
				?>
					<tr>
						<th><?php echo $v->id;?></th>
						<th><?php echo $v->title;?></th>
						<th><?php echo $v->contents;?></th>
						<th><?php echo $v->created_at;?></th>
					</tr>
				<?php
					}
				?>
				</table>
			</div>
		</div>
	</div>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
	$('#post_btn').on('click', function(){
		$.ajax({
			url  : "/emoji/insert.json",
			data : $('#blog_form').serialize(),
			type : 'POST',
			dataType : 'json'
		}).done(function(json){
			if(json.res == 'OK')
			{
				window.location.reload();
			}
			else
			{
				alert(json.msg);
			}
		}).fail(function(json){
			alert('ajaxエラー!');
		});
	});
});
</script>
</body>
</html>
記事の入力フォームと登録済みの記事一覧を表示するtableだけの簡単な構成です。
[記事の登録]のボタンをクリックすることでフォームの内容を/emoji/insert/にajax送信して「OK」のレスポンスが帰ってきたら画面をリロードします。

STEP4: DBの接続設定をutf8mb4に

config/db.phpのcharsetにutf8mb4を指定する。
return array(
	'default' => array(
		'connection'  => array(
			'dsn'        => 'mysql:host=localhost;dbname=emoji',
			'username'   => '{DBユーザ}',
			'password'   => '{DBパスワード}',
		),
		'charset' => 'utf8mb4',
	),
);

STEP5: 記事を投稿してみる

http://localhost/emoji/にアクセスしてフォームに絵文字をふんだんに入力してから[記事の登録]をクリックする。
無事登録され一覧に追加されます。

入力中

%e7%b5%b5%e6%96%87%e5%ad%971

登録後

%e7%b5%b5%e6%96%87%e5%ad%972

おまけ

当然のように「CHARACTER SET utf8」のテーブルに無理やり絵文字を登録しようとするとSQLエラーが発生してしまいます。
今後WEBサービスで利用するテーブルはutf8mb4で作成しておかないといけませんね。(MySQL5.5.3以上必須)
INDEXブログ一覧へ
CONTACTお問い合わせはこちら