Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generation is wrong when oneToOne and oneToMany on same object #30

Closed
jaumard opened this issue Jul 6, 2018 · 6 comments
Closed

Generation is wrong when oneToOne and oneToMany on same object #30

jaumard opened this issue Jul 6, 2018 · 6 comments

Comments

@jaumard
Copy link
Contributor

jaumard commented Jul 6, 2018

Here is what my model look like:

class Post {
  Post();
  Post.make(this.id, this.msg, this.stars, this.read, this.at, this.item, this.items);

  @PrimaryKey(autoIncrement: true)
  int id;
  @Column(nullable: true)
  String msg;
  @Column(nullable: true)
  bool read;
  @Column(nullable: true)
  double stars;
  @Column(nullable: true)
  DateTime at;
  @HasOne(ItemBean)
  Item item;
  @HasMany(ItemBean)
  List<Item> items;

  String toString() =>
      'Post(id: $id, message: $msg, stars: $stars, read: $read, at: $at, item: $item, items: $items)';

  static String get tableName => 'posts';
}

@GenBean()
class PostBean extends Bean<Post> with _PostBean {
  PostBean(Adapter adapter)
      : itemBean = ItemBean(adapter),
        super(adapter);

  final ItemBean itemBean;

  Future<int> updateReadField(int id, bool read) async {
    Update st = updater.where(this.id.eq(id)).set(this.read, read);
    return execUpdate(st);
  }

  /// Finds all posts
  Future<List<Post>> findAll() async => (await execFind(finder)).toList();
}

And this is what is generate:

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'post.dart';

// **************************************************************************
// BeanGenerator
// **************************************************************************

abstract class _PostBean implements Bean<Post> {
  String get tableName => Post.tableName;

  final IntField id = new IntField('id');

  final StrField msg = new StrField('msg');

  final BoolField read = new BoolField('read');

  final DoubleField stars = new DoubleField('stars');

  final DateTimeField at = new DateTimeField('at');

  Post fromMap(Map map) {
    Post model = new Post();

    model.id = adapter.parseValue(map['id']);
    model.msg = adapter.parseValue(map['msg']);
    model.read = adapter.parseValue(map['read']);
    model.stars = adapter.parseValue(map['stars']);
    model.at = adapter.parseValue(map['at']);

    return model;
  }

  List<SetColumn> toSetColumns(Post model, [bool update = false]) {
    List<SetColumn> ret = [];

    ret.add(id.set(model.id));
    ret.add(msg.set(model.msg));
    ret.add(read.set(model.read));
    ret.add(stars.set(model.stars));
    ret.add(at.set(model.at));

    return ret;
  }

  Future createTable() async {
    final st = Sql.create(tableName);
    st.addInt(id.name, primary: true, autoIncrement: true);
    st.addStr(msg.name);
    st.addBool(read.name);
    st.addInt(stars.name);
    st.addDateTime(at.name);
    return execCreateTable(st);
  }

  Future<dynamic> insert(Post model, {bool cascade: false}) async {
    final Insert insert = inserter.setMany(toSetColumns(model));
    var retId = await execInsert(insert);
    if (cascade) {
      Post newModel;
      if (model.item != null) {
        newModel ??= await find(model.id);
        itemBean.associatePost(model.item, newModel);
        await itemBean.insert(model.item);
      }
      if (model.items != null) {
        newModel ??= await find(model.id);
        model.items.forEach((x) => itemBean.associatePost(x, newModel));
        for (final child in model.items) {
          await itemBean.insert(child);
        }
      }
    }
    return retId;
  }

  Future<int> update(Post model,
      {bool cascade: false, bool associate: false}) async {
    final Update update =
        updater.where(this.id.eq(model.id)).setMany(toSetColumns(model));
    final ret = execUpdate(update);
    if (cascade) {
      Post newModel;
      if (model.item != null) {
        if (associate) {
          newModel ??= await find(model.id);
          itemBean.associatePost(model.item, newModel);
        }
        await itemBean.update(model.item);
      }
      if (model.items != null) {
        if (associate) {
          newModel ??= await find(model.id);
          model.items.forEach((x) => itemBean.associatePost(x, newModel));
        }
        for (final child in model.items) {
          await itemBean.update(child);
        }
      }
    }
    return ret;
  }

  Future<Post> find(int id, {bool preload: false, bool cascade: false}) async {
    final Find find = finder.where(this.id.eq(id));
    final Post model = await execFindOne(find);
    if (preload) {
      await this.preload(model, cascade: cascade);
    }
    return model;
  }

  Future<List<Post>> findWhere(Expression exp) async {
    final Find find = finder.where(exp);
    return await (await execFind(find)).toList();
  }

  Future<int> remove(int id, [bool cascade = false]) async {
    if (cascade) {
      final Post newModel = await find(id);
      await itemBean.removeByPost(newModel.id);
      await itemBean.removeByPost(newModel.id);
    }
    final Remove remove = remover.where(this.id.eq(id));
    return execRemove(remove);
  }

  Future<int> removeMany(List<Post> models) async {
    final Remove remove = remover;
    for (final model in models) {
      remove.or(this.id.eq(model.id));
    }
    return execRemove(remove);
  }

  Future<int> removeWhere(Expression exp) async {
    return execRemove(remover.where(exp));
  }

  Future preload(Post model, {bool cascade: false}) async {
    model.item =
        await itemBean.findByPost(model.id, preload: cascade, cascade: cascade);
    model.items =
        await itemBean.findByPost(model.id, preload: cascade, cascade: cascade);
  }

  Future preloadAll(List<Post> models, {bool cascade: false}) async {
    await PreloadHelper.preload<Post, Item>(
        models,
        (Post model) => [model.id],
        itemBean.findByPostList,
        (Item model) => [model.postId],
        (Post model, Item child) => model.item = child,
        cascade: cascade);
    models.forEach((Post model) => model.items ??= []);
    await PreloadHelper.preload<Post, Item>(
        models,
        (Post model) => [model.id],
        itemBean.findByPostList,
        (Item model) => [model.postId],
        (Post model, Item child) => model.items.add(child),
        cascade: cascade);
  }

  ItemBean get itemBean;
  ItemBean get itemBean;
}

There is a duplication of the bean that cause trouble in multiple places in the generated code

@tejainece
Copy link
Member

Different relations between same two models is not supported.

@tejainece
Copy link
Member

Isn't this, a very uncommon scenario?

@jaumard
Copy link
Contributor Author

jaumard commented Jul 6, 2018

In my previous work we had this a lot, where you can have like

Product: 
 Category masterCategory;
 List<Category> subCategories;

Or something like:

User:
  List<Tag> selfAddedTag
  List<Tag> adminAddedTag

Maybe it's uncommon but might be nice to have at some point, for now I don't know enough the data I'll nice to put in DB to know if I need this but if I need I might work on this :)
Should be only generation problem as both separately are handle correctly

@tejainece
Copy link
Member

Ok.

How do you differentiate if a row belongs to the scalar (masterCategory) or the list (subCategories) when fetching it?

@jaumard
Copy link
Contributor Author

jaumard commented Jul 6, 2018

OneToOne and OneToMany is not possible (or not easily)
but OneToOne and ManyToMany is "easy" because oneToOne will have the given id inside his table and subCategories will be under the pivot table

@tejainece
Copy link
Member

Ok :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants