Line data Source code
1 : part of flutter_data; 2 : 3 : mixin _RemoteAdapterSerialization<T extends DataModel<T>> on _RemoteAdapter<T> { 4 1 : @override 5 : Map<String, dynamic> serialize(T model) { 6 3 : final map = localAdapter.serialize(model).filterNulls; 7 : 8 1 : final relationships = <String, dynamic>{}; 9 : 10 4 : for (final field in localAdapter.relationshipsFor(model).keys) { 11 1 : final key = keyForField(field); 12 : 13 1 : if (map[field] != null) { 14 2 : if (map[field] is HasMany) { 15 6 : relationships[key] = map[field].map((e) => e?.id).toList(); 16 : } 17 2 : if (map[field] is BelongsTo) { 18 4 : relationships[key] = map[field].value?.id; 19 : } 20 : } 21 1 : map.remove(field); 22 : } 23 : 24 2 : return map..addAll(relationships.filterNulls); 25 : } 26 : 27 1 : @override 28 : DeserializedData<T, DataModel<dynamic>> deserialize(dynamic data, 29 : {String key, bool init}) { 30 3 : final result = DeserializedData<T, DataModel<dynamic>>([], included: []); 31 : init ??= false; 32 : 33 1 : Object addIncluded(id, RemoteAdapter adapter) { 34 1 : if (id is Map) { 35 : final data = 36 1 : adapter.deserialize(id as Map<String, dynamic>, init: init); 37 1 : result.included 38 2 : ..add(data.model) 39 2 : ..addAll(data.included); 40 2 : return data.model.id; 41 : } 42 : return id; 43 : } 44 : 45 1 : if (data is Map) { 46 1 : data = [data]; 47 : } 48 : 49 2 : for (final mapIn in (data as Iterable)) { 50 1 : final mapOut = <String, dynamic>{}; 51 : 52 2 : final relationships = localAdapter.relationshipsFor(); 53 : 54 2 : for (final mapInKey in mapIn.keys) { 55 2 : final mapOutKey = fieldForKey(mapInKey.toString()); 56 1 : final metadata = relationships[mapOutKey]; 57 : 58 : if (metadata != null) { 59 1 : final _type = metadata['type'] as String; 60 : 61 2 : if (metadata['kind'] == 'BelongsTo') { 62 4 : final id = addIncluded(mapIn[mapInKey], adapters[_type]); 63 1 : mapOut[mapOutKey] = id == null 64 : ? null 65 2 : : graph.getKeyForId(_type, id, 66 1 : keyIfAbsent: DataHelpers.generateKey(_type)); 67 : } 68 : 69 2 : if (metadata['kind'] == 'HasMany') { 70 2 : mapOut[mapOutKey] = (mapIn[mapInKey] as Iterable) 71 2 : ?.map((id) { 72 3 : id = addIncluded(id, adapters[_type]); 73 : return id == null 74 : ? null 75 2 : : graph.getKeyForId(_type, id, 76 1 : keyIfAbsent: DataHelpers.generateKey(_type)); 77 : }) 78 1 : ?.filterNulls 79 1 : ?.toImmutableList(); 80 : } 81 : } else { 82 : // regular field mapping 83 2 : mapOut[mapOutKey] = mapIn[mapInKey]; 84 : } 85 : } 86 : 87 2 : final model = localAdapter.deserialize(mapOut); 88 : if (init) { 89 2 : model._initialize(adapters, key: key, save: true); 90 : } 91 2 : result.models.add(model); 92 : } 93 : 94 : return result; 95 : } 96 : 97 : /// A suffix appended to all [BelongsTo] relationships in serialized form. 98 : /// 99 : /// Example: 100 : /// 101 : /// ``` 102 : /// { 103 : /// "user_id": 1, 104 : /// "id": 1, 105 : /// "title": "delectus aut autem", 106 : /// "completed": false 107 : ///} 108 : ///``` 109 1 : @protected 110 : String get identifierSuffix => '_id'; 111 : 112 1 : Map<String, Map<String, Object>> get _belongsTos => 113 2 : Map.fromEntries(localAdapter 114 1 : .relationshipsFor() 115 1 : .entries 116 5 : .where((e) => e.value['kind'] == 'BelongsTo')); 117 : 118 : /// Transforms a [key] into a model's field. 119 : /// 120 : /// This mapping can also be done via `json_serializable`'s `@JsonKey`. 121 1 : @protected 122 : @visibleForTesting 123 : String fieldForKey(String key) { 124 2 : if (key.endsWith(identifierSuffix)) { 125 3 : final keyWithoutId = key.substring(0, key.length - 3); 126 3 : if (_belongsTos.keys.contains(keyWithoutId)) { 127 : return keyWithoutId; 128 : } 129 : } 130 : return key; 131 : } 132 : 133 : /// Transforms a model's [field] into a key. 134 : /// 135 : /// This mapping can also be done via `json_serializable`'s `@JsonKey`. 136 1 : @protected 137 : @visibleForTesting 138 : String keyForField(String field) { 139 3 : if (_belongsTos.keys.contains(field)) { 140 2 : return '$field$identifierSuffix'; 141 : } 142 : return field; 143 : } 144 : }