【问题标题】:Write and Read nested Map to Firebase Document from Flutter从 Flutter 向 Firebase 文档写入和读取嵌套 Map
【发布时间】:2020-09-19 05:23:18
【问题描述】:

我正在努力从我的颤振应用程序中编写(并且还没有弄清楚如何阅读)firebase 中的嵌套地图。我正在编写一个费用跟踪器,其中类别列表存储在每个日志中。我可以在 firebase 之间映射、保存和检索原始字段,但我迷失的是尝试将类别和子类别的映射写入 firebase,然后如何读取它们。

解决类别和子类别本质上是相同的,所以我只介绍其中一个。另外,我目前将类别 ID 作为类别类本身的键和一部分,稍后我将从类本身中删除 Id,因为我认为这是不好的做法,它目前只是在其他地方帮助我。

我也一直在关注 BLOC 方法,所以我也有模型可以转换为实体来处理 firebase。

这是一个类别(我已经删除了不相关的信息,例如 props 和 to string):

<pre>
    class MyCategory extends Equatable {
  final String id;
  final String name;
  final IconData iconData;

  MyCategory({@required this.id, @required this.name, this.iconData});
  MyCategoryEntity toEntity() {
    return MyCategoryEntity(
      id: id,
      name: name,
      iconCodePoint: iconData.codePoint.toString(),
      iconFontFamily: iconData.fontFamily,
    );
  }

  static MyCategory fromEntity(MyCategoryEntity entity) {
    return MyCategory(
      id: entity.id,
      name: entity.name,
      iconData: IconData(int.parse(entity.iconCodePoint),
          fontFamily: entity.iconFontFamily),
    );
  }
}
</pre>

该类别的模型实体具有 JsonSerialization 并且还使用 Icon codepoint/fontFamily,因为从 firebase 来回传递似乎比直接传递 IconData 更友好。

<pre>
    @JsonSerializable()
class MyCategoryEntity extends Equatable {
  final String id;
  final String name;
  final String iconCodePoint;
  final String iconFontFamily;

  const MyCategoryEntity(
      {this.id, this.name, this.iconCodePoint, this.iconFontFamily});

  factory MyCategoryEntity.fromJson(Map<String, dynamic> json) =>
      _$MyCategoryEntityFromJson(json);

  Map<String, dynamic> toJson() => _$MyCategoryEntityToJson(this);
</pre>

我认为您不需要将 Log 模型视为直接与 firebase 对话的 LogEntity。这是我完全卡住的地方。我已经看过一些如何传递对象列表的示例,但我似乎无法弄清楚如何传递对象映射。我也不确定如何将它从 firestore 读回 LogEntity(如下面的两个 TODO 所示)。

<pre>
    class LogEntity extends Equatable {
  final String uid;
  final String id;
  final String logName;
  final String currency;
  final bool active;
  //String is the category id
  final Map<String, MyCategory> categories;
  final Map<String, MySubcategory> subcategories;


  const LogEntity({this.uid, this.id, this.logName, this.currency, this.categories, this.subcategories, this.active});


  static LogEntity fromSnapshot(DocumentSnapshot snap) {
    return LogEntity(
      uid: snap.data[UID],
      id: snap.documentID,
      logName: snap.data[LOG_NAME],
      currency: snap.data[CURRENCY_NAME],
      //TODO de-serialize categories and subcategories
      active: snap.data[ACTIVE],

    );
  }

  Map<String, Object> toDocument() {
    return {
      UID: uid,
      LOG_NAME: logName,
      CURRENCY_NAME: currency,
    //TODO serialize categories and subcategories, this does doesn't work
      CATEGORIES: categories.map((key, value) => categories[key].toEntity().toJson()).toString(),
      ACTIVE: active,
    };
  }
}
</pre>

我真的希望对此有所帮助,因为我能够理解的唯一示例是列表,而不是地图。另外我想在这个应用程序的几个地方使用类似的方法,所以弄清楚这一点至关重要。

Git:https://github.com/cver22/expenses2

【问题讨论】:

  • 您是否尝试过使用Map&lt;String, MyCategory&gt;.from()Map&lt;String, MySubcategory&gt;.from() 从快照数据中获取地图?
  • 此外,Firebase 仅支持有限数量的类型(如数字、字符串、数组等)。您需要对您的类别和子类别进行.map() 调用,以将它们转换为Map&lt;String, Map&lt;...&gt;&gt;,例如categories.map((key, value) =&gt; MapEntry(key, &lt;call your toJson here from value&gt;))
  • 我刚刚注意到您的 toDocument() 类别末尾有一个 toString()。除非它作为字符串存储在 firebase 中,否则您不应该拥有它。此外,categories[key] 是多余的;简单地使用价值。
  • 谢谢@GregoryConrad!我设法通过以下方法将数据导入firebase,但我仍然不知道如何检索它。 CATEGORIES: categories.map((key, value) =&gt; MapEntry(key, value.toEntity().toJson())),SUBCATEGORIES: subcategories.map((key, value) =&gt; MapEntry(key, value.toEntity().toJson())),
  • 当然。我会把这一切都放在一个答案中。

标签: firebase flutter


【解决方案1】:

导出数据

Firebase 需要Map&lt;String, dynamic&gt;,其中动态是其支持的类型之一(如字符串、数字、映射、数组等)。因此,如果您有一个自定义对象,例如您的示例中的MyCategory,您需要将其.map() 设置为一种受支持的类型(在这种情况下还有另一个Map&lt;String, dynamic&gt;)。因此,您可以使用&lt;map that you need to convert&gt;.map((key, value) =&gt; MapEntry(key, &lt;method to convert each value to a Map&lt;String, dynamic&gt;&gt;))。您的示例几乎是正确的,但它有一个 toString() 它将使 Firebase 将其存储为字符串,而 not 作为地图(这不是所需的行为)。正如 cmets 中所讨论的,删除 toString() 应该可以解决该问题。

检索数据

与导出数据类似,Firebase 会为您提供Map&lt;String, dynamic&gt;,其中动态是 Firebase 支持的类型之一,类别和子类别再次为 Map&lt;String, dynamic&gt;s。因此,您需要将动态的snap.data[KEY] 转换为代码中相应类型的Map&lt;String, YourObject&gt;。解析这个的步骤是这样的:

  1. 从快照中获取动态数据
  2. 将此动态数据转换为地图
  3. .map() 将此数据转换为您自己的类型

因此,类似以下的内容应该可以工作:

    return LogEntity(
      uid: snap.data[UID],
      id: snap.documentID,
      logName: snap.data[LOG_NAME],
      currency: snap.data[CURRENCY_NAME],
      categories: (snap.data[CATEGORIES] as Map<String, dynamic>).map((key, value) => MapEntry(key, MyCategory.fromEntity(MyCategoryEntity.fromJson(value)))),
      // todo subcategories should follow the same form as categories; I will leave that one up to you
      active: snap.data[ACTIVE],

    );

检索嵌套的原始类型

我也想在这里做一个说明,以防有人在搜索如何解析使用 Firebase 中的原始类型的嵌套地图/列表(如 Map&lt;String, String&gt;Map&lt;String, int&gt; 或 @987654340)时发现此问题@ 仅举几例)。在这种情况下,请查看 Map&lt;String, PrimitiveType&gt;.from()List&lt;PrimitiveType&gt;.from() 构造函数;假设 PrimitiveType 是 Firebase 支持的类型之一,他们可能会完全按照您的意愿行事。如果没有,请参阅上面的答案。


还有一件小事:我没有查看你所有的代码,但我不禁注意到你的static LogEntity fromSnapshot()。在 Dart 中,您通常根据用例使用 factorynamed 构造函数,但我绝不是专家。

【讨论】:

  • 非常感谢,在我的应用程序的很多部分中,我一直坚持这个问题已经很久了。要回答您关于 static LogEntity fromSnapshot() 的问题,我明白您使用工厂来实现此目的是什么意思。我最初是通过 Bloc 教程构建的,现在我的理解是这将是 JSON 工厂的理想案例。尽管如此,我很高兴能够更好地了解实际发生的事情以及事情是如何来回传递的。
猜你喜欢
  • 2020-12-19
  • 1970-01-01
  • 2020-11-05
  • 2020-08-21
  • 2021-05-17
  • 1970-01-01
  • 2020-04-01
  • 2021-10-09
  • 1970-01-01
相关资源
最近更新 更多