[CakePHP3] JSONをDBテーブルへ格納時にbeforeMarshal()を活用してみた
2020/03/08
masyus
英語のmarshalは
- 《軍事》〔部隊を〕整列[集結]させる
- 〔考えや事実などを〕整理する、まとめる
という意味で使われます。CakePHP3のbeforeMarshal()
は2.の解釈で、
リクエストデータをpatchEntity()等でEntity化する直前に、リクエストデータの中身を加工できる
という目的でTableクラスもしくはBehaviorクラスで使うことが可能です。今回紹介する内容ではJSONを取り扱いたい時にbeforeMarshal()
を活用した事例を書きます。検証したCakePHPのバージョンは3.6.13、PHPは7.3、MySQLは5.7です。
CakePHP3のbeforeMarshal()の使いどころ
ブログCMSを0ベースで構築時、ブログ記事自体にexperimentalな特殊設定を加えたい要件がありました。そのためMySQLで記事テーブルとは別に記事特殊設定テーブルを作り、そこに特殊設定内容を保存しておくことになりました。experimentalなため特殊設定の内容自体が頻繁に追加/変更/削除がある状況でしたため、JSON型のデータを記事特殊設定テーブルへ格納する方法を取るとします。
テーブルにJSONを格納する際、
データ量低減の観点で、可能なら改行コードや半角スペースによるインデントを取り除いた上でinsertしたい
ニーズがあります。その上で、
ブラウザにJSONを表示/追加/編集する時は、予め改行やインデントで整形させた上で表示したい
と考えました。
整形表示はjson_encode()
で対応できますが、「JSON保存時はどのタイミングで改行コードやインデントを取るべきか?」と考えた結果、beforeMarshal()
が良さそうでした。後々入力値がJSON規格になっているかをValidationでチェックしたいため、Validationの前に実行されるbeforeMarshal()
のタイミングは最適でした。
beforeMarshal()のサンプルコード
前述の仕様を満たすコードは下記のようになりました。
<?php
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\Event\Event;
use ArrayObject;
class ArticleExtraSettingsTable extends Table
{
// ...省略
public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options)
{
// JSONをminify
if (isset($data['json_setting'])) {
$data['json_setting'] = json_decode($data['json_setting']);
$data['json_setting'] = json_encode($data['json_setting'], JSON_UNESCAPED_UNICODE);
}
}
}
一度json_decode()
でstdClass化したデータを再度json_encode()
することで、JSONをminifyした状態にすることができます。ちなみにminifyされたJSONを整形させる場合は
json_encode($json, JSON_PRETTY_PRINT);
のように、json_encode()
のオプションにJSON_PRETTY_PRINTを指定することで可能です。