やりたいこと
freezed (というか内部の json_serializable) では toJson
, fromJson
時に変換する関数を定義することが出来る。(以下参照)
それを使って、プロパティにつけるだけで Firebase での updatedAt(書き込み時にタイムスタンプ更新)
と createdAt(作成時のみタイムスタンプ)
の挙動を実現したい。
@freezed
class Group with _$Group {
const factory Group({
// 作成日
@CreatedAtField() DateTime? createdAt,
// 更新日
@UpdatedAtField() DateTime? updatedAt,
// 名前
required String name,
}) = _Group;
const Group._();
factory Group.fromJson(Map<String, dynamic> json) => _$GroupFromJson(json);
}
要件
toJson 時に以下のような出力になればいい
-
CreatedAtField
はその値を見て-
null
の時はFieldValue.serverTimestamp()
を出力(初回時のみ書き込み) -
DateTime
の時はその値をTimestamp
に変換して出力(初回以降はその値のまま)
-
-
UpdatedAtField
は常にFieldValue.serverTimestamp()
を出力(毎回書き込み)
実装
以下のようなアノテーションを作れば OK
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
// プロパティにつけると変換時、そのプロパティが null なら FieldValue.serverTimestamp() に変換する
// これにより、クライアント側で作成したときのみサーバー側で時間が書き込まれるため、createdAt の挙動になる
class CreatedAtField implements JsonConverter<DateTime?, dynamic> {
const CreatedAtField();
@override
DateTime? fromJson(dynamic timestamp) {
timestamp as Timestamp?;
return timestamp?.toDate();
}
// nullの時は toJson 時 FieldValue.serverTimestamp() を返すことで、createdAt の挙動になる
@override
dynamic toJson(DateTime? dateTime) {
if (dateTime == null) return FieldValue.serverTimestamp();
return dateTime;
}
}
// プロパティにつけると変換時、必ず FieldValue.serverTimestamp() に変換される
// これにより、updatedAt の挙動になる
class UpdatedAtField implements JsonConverter<DateTime?, dynamic> {
const UpdatedAtField();
@override
DateTime? fromJson(dynamic timestamp) {
timestamp as Timestamp?;
return timestamp?.toDate();
}
@override
FieldValue toJson(DateTime? date) {
return FieldValue.serverTimestamp();
}
}
これで以下のように使えるようになりました!
// 作成日
@CreatedAtField() DateTime? createdAt,
// 更新日
@UpdatedAtField() DateTime? updatedAt,