えぐろぐ

https://twitter.com/eggpogg

FlutterでFirestoreのReferenceを取得する

FirestoreのReference指定している値も合わせて取得する時にハマったので、実装方法のメモ.

前提

シリアライズ処理は, json_serializableを使用する.

Firestore側の構造は以下.
itemsusersのコレクションを持っており, items.userusersのリファレンスが貼ってある.

itemsの取得時にのuserの値も合わせて取得する.

f:id:eggpogg:20200802160931p:plain

Modelの処理

Modelには処理は追加せずに, デフォルト.

NOTE: freezedが入っているが, 今回の処理には関係ない

// item.dart

import 'package:flutter/foundation.dart';
import 'package:flutter_mvvm/data/model/user.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

part 'item.freezed.dart';
part 'item.g.dart';

@freezed
abstract class Item with _$Item {
  const factory Item({
    @required String name,
    @required User user,
  }) = _Item;

  factory Item.fromJson(Map<String, dynamic> json) =>
      _$ItemFromJson(json);
}
// user.dart

import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';
part 'user.g.dart';

@freezed
abstract class User with _$User {
  const factory User({
    @required String name,
  }) = _User;

  factory User.fromJson(Map<String, dynamic> json) =>
      _$UserFromJson(json);
}

所得処理

今回はFirestoreのワンショットの取得方法での実装.
全体の処理は以下になる.

// item_repository.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_mvvm/data/model/item.dart';

class ItemRepository  {
  @override
  Future<List<Item>> getItems() async {
    return Firestore.instance
        .collection('items')
        .getDocuments()
        .then((result) {

      // 各userの取得部分Future関数でまとめる.
      return Future.wait(result.documents.map((it) async {

       // userドキュメントを再度取得する.
        var user = await it.data["user"].get();

       // 取得したuserを, itemをコピーした値に入れる.
       //
       // memo
       // it.data["user"] = user.data は x
       // it.dataはkey,valueの型が固定のlinkdedHashMapになっており,
       // 別の型で値を上書きできない.
       // Map.fromも同じなので, Map.ofを使用.
        var clone = Map.of(it.data)..["user"] = user.data;

        // マッピング処理をして返す.
        return Item.fromJson(clone);
      }));
    });
  }
}

まとめ

もっと良い方法はありそうだが, 自分では一応これで進める.

@JsonKeyとかを使って, モデル側にガリガリマッピング処理を書いてもいいけど, 以下の理由でデータ取得時に完結させている.

  • Firestoreをなるべく外に出したくなかった.
  • 全体的な書き換えがあってもRepository層の書き換えでいける.
  • モデルクラスはなるべく簡潔にしたかった.

実際の実装ではUseCase層とか用意したほうがいいかも.
デメリットとしては, Referenceが増えると, Repository層は地獄になる.

他にいい方法が教えてほしいです...orz

おわり.