ferry icon indicating copy to clipboard operation
ferry copied to clipboard

[Feature Request] Access properties using Maps (ferry_generator)

Open codakkk opened this issue 1 year ago • 3 comments

Accessing generated code members via a Map should be feasible. For example, in my current project, I'm utilizing 'proto_grid' for table management. This tool enables column filtering using various criteria such as "Greater than", "Contains", "Equals to", etc. Consequently, for each column that is available, I find myself manually scripting a filter like so:

Future<PlutoInfinityScrollRowsResponse> _fetch(
    PlutoInfinityScrollRowsRequest request,
  ) async {
    final filterBuilder = GVehiclesBoolExpBuilder();

    if (request.lastRow != null) {
      filterBuilder.id.G_gt = int.tryParse(request.lastRow?.cells['id']?.value);
    }

    if (request.filterRows.isNotEmpty) {
      final filters = FilterHelper.convertRowsToMap(request.filterRows);

      if (filters.containsKey('name')) {
          filterBuilder.name.G_eq = v[PlutoFilterTypeEquals.name];
          final containsValue = v[PlutoFilterTypeContains.name];
          final startsWithValue = v[PlutoFilterTypeStartsWith.name];
          final endsWithValue = v[PlutoFilterTypeEndsWith.name];
    
          if (containsValue != null) {
            filterBuilder.name.G_ilike = '%$containsValue%';
          } else if (startsWithValue != null) {
            filterBuilder.name.G_ilike = '$startsWithValue%';
          } else if (endsWithValue != null) {
            filterBuilder.name.G_ilike = '%$endsWithValue';
          }
    
          filterBuilder.name.G_gt = v[PlutoFilterTypeGreaterThan.name];
          filterBuilder.name.G_gte = v[PlutoFilterTypeGreaterThanOrEqualTo.name];
          filterBuilder.name.G_lt = v[PlutoFilterTypeLessThan.name];
          filterBuilder.name.G_lte = v[PlutoFilterTypeLessThanOrEqualTo.name];
       }
       
       if(filters.containsKey('companyOwner')) {
           // Same code as before but with filterBuilder.companyOwner
       }
    }
    
    final req = gqlClient.request(GVehicleListReq(
      (b) => b
        ..requestId = 'VehicleListReq'
        ..vars.where = filterBuilder
        ..vars.limit = 20
        ..fetchPolicy = FetchPolicy.NetworkOnly,
    ));

    final result = await req.first;
    final vehicles = result.data?.vehicles ?? BuiltList();

    return PlutoInfinityScrollRowsResponse(
      isLast: vehicles.length < 20,
      rows: vehicles.map(_createRowFromData).toList(),
    );
  }

Hence, the feature request aims to provide a Map<String, TComparisonExpBuilder> or a similar construct (like overriding the [] operator with a set of if-else statements) to enable this kind of access:

      for (final filter in filters) {
        filterBuilder[filter].G_eq = filters[filter][PlutoFilterTypeEquals.name];
        filterBuilder[filter].G_gt = filters[filter][PlutoFilterTypeGreaterThan.name];
        // etc.
      }

I understand this approach might lead to some type errors, but I am willing to consider a dynamic type and perform manual casting, like this:

    for (final filter in filters) {
        filterBuilder.get<GStringComparisonExpBuilder>(filter).G_eq = filters[filter][PlutoFilterTypeEquals.name];
        filterBuilder.get<GStringComparisonExpBuilder>(filter).G_gt = filters[filter][PlutoFilterTypeGreaterThan.name];
        // etc.
      }

An alternative, albeit less elegant, solution I've considered is the following, which aims to avoid manually setting all the fields:

extension GStringComparisonExpBuilderX on GStringComparisonExpBuilder {
  void buildFromMap(List<Map<String, String>>? map) {
    if (map == null) {
      return;
    }
    for (final v in map) {
      G_eq = v[PlutoFilterTypeEquals.name];
      final containsValue = v[PlutoFilterTypeContains.name];
      final startsWithValue = v[PlutoFilterTypeStartsWith.name];
      final endsWithValue = v[PlutoFilterTypeEndsWith.name];

      if (containsValue != null) {
        G_ilike = '%$containsValue%';
      } else if (startsWithValue != null) {
        G_ilike = '$startsWithValue%';
      } else if (endsWithValue != null) {
        G_ilike = '%$endsWithValue';
      }

      G_gt = v[PlutoFilterTypeGreaterThan.name];
      G_gte = v[PlutoFilterTypeGreaterThanOrEqualTo.name];
      G_lt = v[PlutoFilterTypeLessThan.name];
      G_lte = v[PlutoFilterTypeLessThanOrEqualTo.name];
    }
  }
}

However, this method still requires manual verification of each filter's existence, similar to the first example. I think it's possible to better use code generation for those use-cases. Thank you!

codakkk avatar Feb 04 '24 18:02 codakkk