firebase / flutterfire

🔥 A collection of Firebase plugins for Flutter apps.
https://firebase.google.com/docs/flutter/setup
BSD 3-Clause "New" or "Revised" License
8.72k stars 3.97k forks source link

🐛 [cloud_firestore_odm] sub-collections models generation which are a collection on its own is failing #9617

Closed Purus closed 2 years ago

Purus commented 2 years ago

Bug report

Describe the bug Sub-collection models from separate dart file does not seems to be working fine in the root model.

Steps to reproduce

Steps to reproduce the behavior:

The root model is below.

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:cloud_firestore_odm/cloud_firestore_odm.dart';
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';

import 'group.dart';

part 'app_user.g.dart';

@JsonSerializable(converters: firestoreJsonConverters)
class AppUser extends Equatable {
  const AppUser({
    required this.id,
    required this.name,
    required this.email,
    required this.photoUrl,
    required this.joinDate,
    this.birthDate,
    this.country,
    this.age = 0,
    this.isOnline = false,
    this.lastOnline,
    this.isAdmin = false,
    this.isActive = true,
    this.gender,
    this.tokens,
  });

  final String id;
  final String name;
  final String email;
  final String photoUrl;
  final Timestamp joinDate;
  final Timestamp? birthDate;
  final int age;
  final bool? isAdmin;
  final bool? isOnline;
  final Timestamp? lastOnline;
  final bool? isActive;
  final String? country;
  final String? gender;
  final List<String>? tokens;

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

  Map<String, dynamic> toJson() => _$AppUserToJson(this);

  @override
  List<Object?> get props => [name, email, photoUrl];
}

@Collection<AppUser>('users')
@Collection<AppUser>(
  'users/*/friends',
  prefix: 'Friends',
)
@Collection<Group>('users/*/groups')
final usersRef = AppUserCollectionReference();

The other model which has its own collection and also its part of the root model is shared below.

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:cloud_firestore_odm/cloud_firestore_odm.dart';
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';

part 'group.g.dart';

@JsonSerializable(converters: firestoreJsonConverters)
class Group extends Equatable {
  const Group({
    required this.id,
    required this.name,
    required this.description,
    this.image,
    required this.createdTime,
    this.isPinned = false,
    this.isActive = true,
    this.isPrivate = false,
    this.usersCount = 0,
  });

  final String id;
  final String name;
  final String description;
  final String? image;
  final Timestamp createdTime;
  final bool? isPinned;
  final bool? isActive;
  final bool? isPrivate;
  final int usersCount;

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

  Map<String, dynamic> toJson() => _$GroupToJson(this);

  @override
  List<Object?> get props => [name];
}

@Collection<Group>('groups')
final groupsRef = GroupCollectionReference();

Below is my build.yml content.

targets:
  $default:
    builders:
      cloud_firestore_odm_generator:
        enabled: true
        generate_for:
          include:
            - lib/*
            - lib/**
            - test_driver/integration/*
            - test_driver/integration/**/*
      json_serializable:
        enabled: true
        generate_for:
          include:
            - lib/*
            - lib/**
            - test_driver/integration/*
            - test_driver/integration/**/*
        options:
          create_field_map: true
          explicit_to_json: true
          create_factory: true
      source_gen|combining_builder:
        options:
          ignore_for_file:
            - 'type=lint'

Below is the exception after the generation.

image

Expected behavior

The models should be generated without any issues.


Purus commented 2 years ago

Some more details on my use case.. Group is a collection on its own. and it's also a sub-collection under AppUsers

/users
/groups
/users/*/groups
darshankawar commented 2 years ago

Thanks for the report.

/cc @rrousselGit

AlwinFassbender commented 2 years ago

The ability to define the class for a Firestore sub-collection in a separate file would be nice. Let's say we have two models, User and MedicalRecord.

Case 1 | Collections defined in one file. Generator runs without errors, but the generated file contains the error undefined Name '_$MedicalRecordFieldMap'.

part 'user.g.dart';

@Collection<User>('users')
@Collection<MedicalRecord>('users/*/medical_records')
@firestoreSerializable
class User {
    String name;
    int age;

    User(
        this.name,
        this.age,
     );
}
part 'medical_record.g.dart';

@firestoreSerializable
class MedicalRecord {
    Timestamp date;
    String description;

    MedicalRecord(
        this.date,
        this.description,
     );
}

Case 2 | Collections defined in two separate files. Generator doesn't run without errors

part 'user.g.dart';

@Collection<User>('users')
@firestoreSerializable
class User {
    String name;
    int age;

    User(
        this.name,
        this.age,
     );
}
part 'medical_record.g.dart';

@Collection<MedicalRecord>('users/*/medical_records')
@firestoreSerializable
class MedicalRecord {
    Timestamp date;
    String description;

    MedicalRecord(
        this.date,
        this.description,
     );
}

It would be awesome if one of the two options would work in the future, as one file with the classes for all sub-collections can get pretty large and complex. It would be neat to have one file for each class!

rrousselGit commented 2 years ago

About this, I don't think that's something that can fully be supported.

The ODM relies on private code generated by json_serializable for a bunch of things, with more to come.
We could have a nice error message about it. But supporting it, I'm not sure.


Case 1 | Collections defined in one file. Generator runs without errors, but the generated file contains the error undefined Name '_$MedicalRecordFieldMap'.

That's your mistake. The documentation mentions that a specific json_serializable flag needs to be enabled for the ODM to handle json_serializable

See https://github.com/firebase/flutterfire/blob/master/packages/cloud_firestore_odm/doc/defining-models.md#defining-models. Cf the createFieldMap: true, in @JsonSerializable

rrousselGit commented 2 years ago

As mentioned, I doubt there's a reasonable solution to this problem.

Maybe this could be done better once we have metaprogramming. Otherwise, this would require a very large refactoring of json_serializable for this.