isar / isar

Extremely fast, easy to use, and fully async NoSQL database for Flutter
https://isar.dev
Apache License 2.0
3.62k stars 347 forks source link

how to use isarlinks with freezed #197

Closed yyong37 closed 2 years ago

yyong37 commented 2 years ago

if allowed, please add a test. thx 🌺

https://github.com/isar/isar/blob/main/packages/isar_test/lib/freezed_model.dart

lhengl commented 2 years ago

Why was this closed? Did you find a solution?

Isar links doesn't work with freezed right now because it must be initialised with a non const/literal. Freezed does not allow this.

yyong37 commented 2 years ago

the reason as you say. it conflict.

simc commented 2 years ago

It should work with the latest Isar version. I didn't have time to write tests yet however...

yyong37 commented 2 years ago

@freezed
@Collection()
class TeacherModel with _$TeacherModel {
  const factory TeacherModel({
    int? id,
    required String subject,
  }) = MyTeacherModel;
}

@freezed
@Collection()
class StudentModel with _$StudentModel {
  const StudentModel._();
  // try 1. can not make gen links 
  // get teachers => IsarLink<TeacherModel>();
  const factory StudentModel({
    int? id,
    required String name,
    // 2 try 2. can not use non-constant value for required field
    // @Default(IsarLink<TeacherModel>()) IsarLink<TeacherModel> teachers,
  }) = MyStudentModel;
}

@leisim as this sample code, init Isarlink and gen it I have problem can not solved. cuz freezed can not @Default() with func exec

yyong37 commented 2 years ago

maybe have a link annotation ?

qoob23 commented 2 years ago

Hi! Kinda found a solution. It comes with a cost of losing const constructors and having to have common properties (if you don't use unions, it is not a disadvantage), but works:

import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:isar/isar.dart';
import 'package:path_provider/path_provider.dart';

part 'test.freezed.dart';
part 'test.g.dart';

@freezed
@Collection()
class Teacher with _$Teacher {
  const factory Teacher({
    int? id,
    required String name,
  }) = _Teacher;
}

@freezed
@Collection()
class Student with _$Student {
  Student._();
  factory Student({
    int? id,
    required String name,
  }) = _Student;

  final teachers = IsarLinks<Teacher>();
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final isar = await Isar.open(
    name: 'test',
    schemas: [TeacherSchema, StudentSchema],
    directory: (await getApplicationDocumentsDirectory()).path,
  );

  const teacher1 = Teacher(name: 'Ann');
  const teacher2 = Teacher(name: 'John');
  final student = Student(name: 'Joe');
  student.teachers.addAll([teacher1, teacher2]);

  await isar.writeTxn((isar) async {
    await isar.teachers.putAll([teacher1, teacher2]);
    await isar.students.put(student);
    await student.teachers.save();
  });

  final dbStudent = (await isar.students.where().findAll())[0];
  await dbStudent.teachers.load();
  print(dbStudent.teachers);
}

The output is {Teacher(id: 4, name: Ann), Teacher(id: 3, name: John)}

qoob23 commented 2 years ago

@leisim Isar.autoIncrement is a final property that has a const value assigned to it. It would be better to make it const to be able to use it in freezed's @Default() annotation instead of always making id nullable.

simc commented 2 years ago

Isar does not support freezed.

realitymolder commented 2 years ago

Isar does not support freezed.

will it support it in the future? many flutter devs depend on freezed

jannikrh commented 2 years ago

If you distinguish between the database models (Isar collections) in the data layer and business objects (freezed, ...) in the domain layer of your app and map them, Isar can also be used in an app with frezzed models in business and presentation layer.

neiljaywarner commented 1 year ago

Any update or further thoughts on this? freezed is really big.

FrankDomburg commented 1 year ago

I think I agree with what @jannikrh is saying.

This seems to be a difference between what should be in a DTO and what should happen in the domain model.

Not many clear examples out there (also writing this to validate my own thinking).

But from what understand, a DTO should be just the info you require to be persisted. A domain model could also contain information that is only relevant when running the app. A controller / use case / viewmodel then adds whatever is needed for display.

A (possibly oversimplified) example would be a timer.

Start / End recording would be DTOs.

In the domain model, you'd probably also want to insert the current time, and a function to determine the amount of time passed.

In the use case/ controller / viewmodel you'd stick something to format the time elapsed and stuff like whether or not to show a start/stop button, animate time elapsing, etc.

Did I get it right?

In that case, I do understand the confusion. For start/stop unions are very useful, and thus they become DTO as well as domain model quite often. (Certainly in getting started type stuff on internet).

You could argue that not supporting freezed helps clean architecture, at the cost of more structure for simpler applications.

masreplay commented 1 year ago

@simc @neiljaywarner @FrankDomburg

Isar has always been supported Freezed (but not Freezed Union)

sshadkany commented 10 months ago

for time travelers in the future! I add some code that works for me! :)

@freezed
@Collection(ignore: {'copyWith'})
class UserData with _$UserData {
  ///Don't Forget below line 
  const UserData._();

  const factory UserData({
    final LoginResponseData? loginResponseData,
  }) = _UserData;

  /// add this
  Id get id => Isar.autoIncrement;

  factory UserData.fromJson(Map<String, dynamic> json) => _$UserDataFromJson(json);
}
Andreigr0 commented 4 months ago

What about unions (sealed class) support?

definitelyme commented 1 month ago

Hi! Kinda found a solution. It comes with a cost of losing const constructors and having to have common properties (if you don't use unions, it is not a disadvantage), but works:

import 'package:flutter/material.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:isar/isar.dart';
import 'package:path_provider/path_provider.dart';

part 'test.freezed.dart';
part 'test.g.dart';

@freezed
@Collection()
class Teacher with _$Teacher {
  const factory Teacher({
    int? id,
    required String name,
  }) = _Teacher;
}

@freezed
@Collection()
class Student with _$Student {
  Student._();
  factory Student({
    int? id,
    required String name,
  }) = _Student;

  final teachers = IsarLinks<Teacher>();
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final isar = await Isar.open(
    name: 'test',
    schemas: [TeacherSchema, StudentSchema],
    directory: (await getApplicationDocumentsDirectory()).path,
  );

  const teacher1 = Teacher(name: 'Ann');
  const teacher2 = Teacher(name: 'John');
  final student = Student(name: 'Joe');
  student.teachers.addAll([teacher1, teacher2]);

  await isar.writeTxn((isar) async {
    await isar.teachers.putAll([teacher1, teacher2]);
    await isar.students.put(student);
    await student.teachers.save();
  });

  final dbStudent = (await isar.students.where().findAll())[0];
  await dbStudent.teachers.load();
  print(dbStudent.teachers);
}

The output is {Teacher(id: 4, name: Ann), Teacher(id: 3, name: John)}

Since the Linked property teachers is not in the constructor, how do you handle fromJson and toJson methods created automatically by freezed using the JsonSerializable package?