FirebaseExtended / firestoreodm-flutter

BSD 3-Clause "New" or "Revised" License
33 stars 9 forks source link

[Question] How do I set `FieldValue.serverTimestamp()` for `createdAt` etc. when adding a document? #15

Closed ywake closed 4 months ago

ywake commented 4 months ago

I'm currently adding with a temporary value and then updating. Is there a better way?

final docRef = await tasksRef.add(
  Task(
    title: 'New Task',
    createdAt: DateTime.now(),
  ),
);
docRef.update(
  createdAtFieldValue: FieldValue.serverTimestamp(),
);
gmarizy commented 4 months ago

I solve this with a custom converter:

/// Use with @JsonKey(toJson: replaceWithServerTimestamp)
dynamic replaceWithServerTimestamp(DateTime dateTime) => FieldValue.serverTimestamp();

/// When setting FieldValue.serverTimestamp(), a listener might receive a null value
DateTime useNowIfNull(dynamic value) => value == null ? DateTime.now() : const FirestoreDateTimeConverter().fromJson(value as Timestamp);

class ServerTimestamp extends JsonKey {
  const ServerTimestamp() : super(toJson: replaceWithServerTimestamp, fromJson: useNowIfNull);
}

Usage:

@firestoreSerializable
class _Identification {
  Task({
   required this.title,
   this.createdAt = DateTime.now(),
  });

  final String title;
  @ServerTimestamp()
  final DateTime createdAt;
}
ywake commented 4 months ago

@gmarizy Thanks! Looks very good! In that code, the generator applied FirestoreDateTimeConverter incorrectly, so I modified it.

class ServerTimestamp extends JsonConverter<DateTime, dynamic> {
  const ServerTimestamp();

  @override
  DateTime fromJson(dynamic json) => json == null
      ? DateTime.now()
      : const FirestoreDateTimeConverter().fromJson(json as Timestamp);

  @override
  dynamic toJson(DateTime object) => FieldValue.serverTimestamp();
}

[!WARNING] Please note that using .set() will update the createdAt field to the current time. You will need to implement a condition to decide whether or not to update this field, as shown in the example below. ( Using .update() does not have this issue. )

@override
dynamic toJson(DateTime? object) => object == null
    ? FieldValue.serverTimestamp()
    : const FirestoreDateTimeConverter().toJson(object);