【CakePHP3】jsonデータをDBへ格納する際にbeforeMarshal()を活用してみた

Bake-with-CakePHP Logo

英語のmarshalは

  1. 《軍事》〔部隊を〕整列[集結]させる
  2. 〔考えや事実などを〕整理する、まとめる

という意味で使われる。CakePHP3のbeforeMarshal()は2.の解釈で、

「リクエストデータをEntity化させる前にデータをごにょごにょする」

という目的で、TableもしくはBehaviorクラスで使うことが可能だ。今回はそんなbeforeMarshal()を、jsonデータを取り扱いたい時に活用した事例を書く。検証したCakePHPのバージョンは3.6.13。

MySQL5.7以降では、SQLでjsonデータを取り扱える

MySQL5.7以降ではcolumnにjsonデータを格納すると、json内のキーを対象にSQLで検索できるようになった。

https://dev.mysql.com/doc/refman/5.7/en/json.html

テーブルの正規化とか考えたらjsonデータをDBに保存させるのはイマイチな感はあるが、まだ正規化できる余地のない未知なデータをどうしてもDBに持たせたいときには有効な選択肢。

僕の実例では、とあるテーブルに様々な特殊設定を加えたい要件があり、しかも設定自体が随時追加/変更/削除が頻繁にあるようなものだったため、jsonデータを格納する方法を使ってみている。

CakePHP3のbeforeMarshal()の使いどころ

beforeMarshal()の挙動は具体的にいうと、

リクエストデータをpatchEntity()等でEntity化する直前に、リクエストデータの中身を加工できる

というもの。CakePHP2でいうところのbeforeValidate()に近い動きだ。

MySQLのテーブルにjsonデータを格納する際、可能なら改行コードや半角スペースによるインデントを含まずに格納しておきたいニーズがある。だが、画面からjsonデータを追加/編集したい時には整形させた上で表示したいのが心情。

 

整形表示はjson_encode()等でやれるが、保存時はどのタイミングで改行コードやインデントを取るべきかと考えた時、beforeMarshal()が良さそうだと考えついた。json規格になっているかをvalidationでチェックしたいニーズを汲むと、beforeMarshal()のタイミングが適切かなと。具体的にはこんな感じ:

一度json_decode()でstdClass化したデータを再度json_encode()することで、整形されたjsonデータをminifyした状態にできる。

 

余談だが、minifyされた1行のjsonデータを整形させるには

という書き方で、json_encode()のオプションにJSON_PRETTY_PRINTを指定することで可能。