Line data Source code
1 : part of flutter_data; 2 : 3 : /// An adapter interface to access local storage. 4 : /// 5 : /// Identity in this layer is enforced by keys. 6 : /// 7 : /// See also: [HiveLocalAdapter] 8 : abstract class LocalAdapter<T extends DataModel<T>> with _Lifecycle { 9 11 : @protected 10 22 : LocalAdapter(Reader read) : graph = read(graphNotifierProvider); 11 : 12 : @protected 13 : final GraphNotifier graph; 14 : 15 : FutureOr<LocalAdapter<T>> initialize(); 16 : 17 22 : String get internalType => DataHelpers.getType<T>(); 18 : 19 : // protected API 20 : 21 : /// Returns all models of type [T] in local storage. 22 : List<T>? findAll(); 23 : 24 : /// Finds model of type [T] by [key] in local storage. 25 : T? findOne(String? key); 26 : 27 : /// Saves model of type [T] with [key] in local storage. 28 : /// 29 : /// By default notifies this modification to the associated [GraphNotifier]. 30 : @protected 31 : @visibleForTesting 32 : Future<T> save(String key, T model, {bool notify = true}); 33 : 34 : /// Deletes model of type [T] with [key] from local storage. 35 : /// 36 : /// By default notifies this modification to the associated [GraphNotifier]. 37 : @protected 38 : @visibleForTesting 39 : Future<void> delete(String key, {bool notify = true}); 40 : 41 : /// Deletes all models of type [T] in local storage. 42 : @protected 43 : @visibleForTesting 44 : Future<void> clear(); 45 : 46 : // model initialization 47 : 48 11 : @protected 49 : @nonVirtual 50 : T initModel(T model, {Function(T)? onModelInitialized}) { 51 55 : model._key ??= graph.getKeyForId(internalType, model.id, 52 11 : keyIfAbsent: DataHelpers.generateKey<T>())!; 53 11 : _initializeRelationships(model); 54 11 : onModelInitialized?.call(model); 55 : return model; 56 : } 57 : 58 11 : void _initializeRelationships(T model) { 59 22 : final metadatas = relationshipMetas.values; 60 22 : for (final metadata in metadatas) { 61 22 : final relationship = metadata.instance(model); 62 11 : relationship?.initialize( 63 : owner: model, 64 11 : name: metadata.name, 65 11 : inverseName: metadata.inverseName, 66 : ); 67 : } 68 : } 69 : 70 : // public abstract methods 71 : 72 : Map<String, dynamic> serialize(T model, {bool withRelationships = true}); 73 : 74 : T deserialize(Map<String, dynamic> map); 75 : 76 : Map<String, RelationshipMeta> get relationshipMetas; 77 : 78 : // helpers 79 : 80 8 : Map<String, dynamic> transformSerialize(Map<String, dynamic> map, 81 : {bool withRelationships = true}) { 82 24 : for (final e in relationshipMetas.entries) { 83 8 : final key = e.key; 84 : if (withRelationships) { 85 24 : final ignored = e.value.serialize == false; 86 1 : if (ignored) map.remove(key); 87 : 88 16 : if (map[key] is HasMany) { 89 24 : map[key] = (map[key] as HasMany).keys; 90 16 : } else if (map[key] is BelongsTo) { 91 24 : map[key] = map[key].key; 92 : } 93 : 94 16 : if (map[key] == null) map.remove(key); 95 : } else { 96 2 : map.remove(key); 97 : } 98 : } 99 : return map; 100 : } 101 : 102 8 : Map<String, dynamic> transformDeserialize(Map<String, dynamic> map) { 103 : // ensure value is dynamic (argument might come in as Map<String, String>) 104 8 : map = Map<String, dynamic>.from(map); 105 24 : for (final e in relationshipMetas.entries) { 106 8 : final key = e.key; 107 16 : final keyset = map[key] is Iterable 108 8 : ? {...(map[key] as Iterable)} 109 16 : : {if (map[key] != null) map[key].toString()}; 110 24 : final ignored = e.value.serialize == false; 111 16 : map[key] = { 112 8 : '_': (map.containsKey(key) && !ignored) ? keyset : null, 113 : }; 114 : } 115 : return map; 116 : } 117 : 118 : // private 119 : 120 : // ignore: unused_element 121 : bool get _isLocalStorageTouched; 122 : 123 : void _touchLocalStorage(); 124 : }