feat(crud)!: move crud firestore implementation into his own package
This commit is contained in:
@@ -14,5 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
export 'enums/operation_type.dart';
|
||||
export 'enums/where_query_type.dart';
|
||||
export 'mixins/operation.dart';
|
||||
export 'model_identifier.dart';
|
||||
export 'model_mapper.dart';
|
||||
|
||||
+8
-2
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Copyright (C) 2023 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
@@ -14,4 +14,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
export 'crud_repository.dart';
|
||||
enum OperationType {
|
||||
query,
|
||||
create,
|
||||
read,
|
||||
update,
|
||||
delete,
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Copyright (C) 2023 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -15,20 +15,28 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/enums/operation_type.dart';
|
||||
|
||||
/// Defines every write operation in CRUD.
|
||||
mixin CreateOperation<Model extends ObjectModel, Out>
|
||||
on AsyncUseCase<Model, Out> {}
|
||||
mixin CreateOperation<In, Out> on AsyncUseCase<In, Out> {
|
||||
/// {@macro create}
|
||||
OperationType get operationType => OperationType.create;
|
||||
}
|
||||
|
||||
/// Defines every read operation in CRUD.
|
||||
mixin ReadOperation<Model extends ObjectModel, In, Out>
|
||||
on AsyncUseCase<In, Out> {}
|
||||
mixin ReadOperation<In, Out> on AsyncUseCase<In, Out> {
|
||||
/// {@macro read}
|
||||
OperationType get operationType => OperationType.read;
|
||||
}
|
||||
|
||||
/// Defines every update operation in CRUD.
|
||||
mixin UpdateOperation<Model extends ObjectModel, In>
|
||||
on AsyncUseCase<In, void> {}
|
||||
mixin UpdateOperation<In> on AsyncUseCase<In, void> {
|
||||
/// {@macro update}
|
||||
OperationType get operationType => OperationType.update;
|
||||
}
|
||||
|
||||
/// Defines every delete operation in CRUD.
|
||||
mixin DeleteOperation<Model extends ObjectModel, In>
|
||||
on AsyncUseCase<In, void> {}
|
||||
mixin DeleteOperation<In> on AsyncUseCase<In, void> {
|
||||
/// {@macro delete}
|
||||
OperationType get operationType => OperationType.delete;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (C) 2023 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
class ModelIdentifier<Model> {
|
||||
const ModelIdentifier({
|
||||
required this.getIdentifier,
|
||||
});
|
||||
|
||||
final String Function(Model model) getIdentifier;
|
||||
|
||||
String? getIdentifierOrNull(Model? model) {
|
||||
if (model == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getIdentifier(model);
|
||||
}
|
||||
|
||||
bool isIdentifier({
|
||||
required Model? model,
|
||||
required String identifier,
|
||||
}) {
|
||||
if (model == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getIdentifier(model) == identifier;
|
||||
}
|
||||
|
||||
bool areEqual(
|
||||
Model? model1,
|
||||
Model? model2,
|
||||
) {
|
||||
if (model1 == null && model2 == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (model1 == null || model2 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getIdentifier(model1) == getIdentifier(model2);
|
||||
}
|
||||
}
|
||||
+10
-3
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Copyright (C) 2023 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
@@ -14,5 +14,12 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
export 'local/crud_in_memory_data_source_impl.dart';
|
||||
export 'remote/crud_firestore_data_source_impl.dart';
|
||||
class ModelMapper<Model> {
|
||||
const ModelMapper({
|
||||
required this.fromJson,
|
||||
required this.toJson,
|
||||
});
|
||||
|
||||
final Model Function(Map<String, dynamic>? json) fromJson;
|
||||
final Map<String, dynamic> Function(Model model) toJson;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Copyright (C) 2023 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
@@ -14,5 +14,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
export 'data_sources/data_sources.dart';
|
||||
export 'repositories/repositories.dart';
|
||||
export 'data_sources/crud_data_source.dart';
|
||||
export 'data_sources/crud_data_source_in_memory_impl.dart';
|
||||
export 'repositories/crud_repository_impl.dart';
|
||||
|
||||
+32
-25
@@ -15,47 +15,54 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/enums/operation_type.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
|
||||
|
||||
/// {@template crud_data_source}
|
||||
/// A [BaseDataSource] that provides SCRUD operations.
|
||||
/// {@endtemplate}
|
||||
abstract class CrudDataSource<Model> extends BaseDataSource {
|
||||
abstract class CrudDataSource extends BaseDataSource {
|
||||
/// {@macro crud_data_source}
|
||||
const CrudDataSource();
|
||||
|
||||
/// Creates a new [Model] object.
|
||||
Future<void> create(Model object, {String? id});
|
||||
|
||||
/// Gets a [Model] object by its [id].
|
||||
Future<Model?> get(String id);
|
||||
|
||||
/// Gets all [Model] objects.
|
||||
Future<List<Model?>> getAll();
|
||||
|
||||
/// Updates a [Model] object by its [id].
|
||||
Future<void> update(
|
||||
String id, {
|
||||
Model? object,
|
||||
Map<String, dynamic>? raw,
|
||||
const CrudDataSource({
|
||||
required this.id,
|
||||
});
|
||||
|
||||
/// Updates all [Model] objects.
|
||||
final String? Function(
|
||||
OperationType operationType,
|
||||
Map<String, dynamic> object,
|
||||
) id;
|
||||
|
||||
/// Creates a new object.
|
||||
Future<void> create(Map<String, dynamic> object, {String? id});
|
||||
|
||||
/// Gets a object by its [id].
|
||||
Future<Map<String, dynamic>?> get(String id);
|
||||
|
||||
/// Gets all objects.
|
||||
Future<List<dynamic>?> getAll();
|
||||
|
||||
/// Updates a object by its [id].
|
||||
Future<void> update(
|
||||
String id, {
|
||||
Map<String, dynamic>? object,
|
||||
});
|
||||
|
||||
/// Updates all objects.
|
||||
Future<void> updateAll(Map<String, Object?>? data);
|
||||
|
||||
/// Deletes a [Model] object by its [id].
|
||||
/// Deletes a object by its [id].
|
||||
Future<void> delete(String id);
|
||||
|
||||
/// Deletes all [Model] objects.
|
||||
/// Deletes all objects.
|
||||
Future<void> deleteAll();
|
||||
|
||||
/// Queries [Model] objects by [conditions].
|
||||
Future<List<Model?>> query(List<QueryInterface> conditions);
|
||||
/// Queries objects by [conditions].
|
||||
Future<List<dynamic>?> search(List<Query> conditions);
|
||||
|
||||
/// Streams [Model] objects by [conditions].
|
||||
Stream<List<Model?>> stream({
|
||||
/// Streams objects by [conditions].
|
||||
Stream<List<dynamic>?> stream({
|
||||
String? id,
|
||||
List<QueryInterface>? conditions,
|
||||
List<Query>? conditions,
|
||||
bool includeMetadataChanges = false,
|
||||
});
|
||||
}
|
||||
+74
-40
@@ -16,28 +16,55 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:wyatt_crud_bloc/src/core/enums/operation_type.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/data_sources/data_sources.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/data/data_sources/crud_data_source.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
/// {@template crud_in_memory_data_source_impl}
|
||||
extension _ModelExtension on Map<String, dynamic> {
|
||||
Map<String, dynamic> merge(
|
||||
Map<String, dynamic>? other, {
|
||||
bool skipNulls = false,
|
||||
}) {
|
||||
if (other == null) {
|
||||
return this;
|
||||
}
|
||||
final Map<String, dynamic> result = {};
|
||||
for (final key in keys) {
|
||||
result[key] = other[key] ?? this[key];
|
||||
}
|
||||
for (final key in other.keys) {
|
||||
if (!containsKey(key)) {
|
||||
result[key] = other[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (skipNulls) {
|
||||
result.removeWhere((key, value) => other[key] == null);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template crud_data_source_in_memory_impl}
|
||||
/// A [CrudDataSource] that stores data in memory.
|
||||
/// {@endtemplate}
|
||||
class CrudInMemoryDataSourceImpl<Model extends ObjectModel>
|
||||
extends CrudDataSource<Model> {
|
||||
/// {@macro crud_in_memory_data_source_impl}
|
||||
CrudInMemoryDataSourceImpl({required this.toMap, Map<String, Model>? data})
|
||||
: _data = data ?? {};
|
||||
final Map<String, Model> _data;
|
||||
final StreamController<List<Model?>> _streamData = StreamController();
|
||||
class CrudDataSourceInMemoryImpl extends CrudDataSource {
|
||||
/// {@macro crud_data_source_in_memory_impl}
|
||||
CrudDataSourceInMemoryImpl({
|
||||
required super.id,
|
||||
Map<String, Map<String, dynamic>>? data,
|
||||
}) : _data = data ?? {};
|
||||
|
||||
final Map<String, Object?> Function(Model) toMap;
|
||||
final Map<String, Map<String, dynamic>> _data;
|
||||
final StreamController<List<Map<String, dynamic>?>> _streamData =
|
||||
StreamController();
|
||||
|
||||
@override
|
||||
Future<void> create(Model object, {String? id}) async {
|
||||
_data[id ?? object.id ?? ''] = object;
|
||||
Future<void> create(Map<String, dynamic> object, {String? id}) async {
|
||||
_data[id ?? this.id(OperationType.create, object) ?? ''] = object;
|
||||
|
||||
_streamData.add(_data.values.toList());
|
||||
}
|
||||
|
||||
@@ -54,14 +81,14 @@ class CrudInMemoryDataSourceImpl<Model extends ObjectModel>
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Model?> get(String id) async => _data[id];
|
||||
Future<Map<String, dynamic>?> get(String id) async => _data[id];
|
||||
|
||||
@override
|
||||
Future<List<Model?>> getAll() async => _data.values.toList();
|
||||
Future<List<Map<String, dynamic>?>> getAll() async => _data.values.toList();
|
||||
|
||||
@override
|
||||
Future<List<Model?>> query(List<QueryInterface> conditions) async {
|
||||
List<Model> result = _data.values.toList();
|
||||
Future<List<Map<String, dynamic>?>> search(List<Query> conditions) async {
|
||||
List<Map<String, dynamic>> result = _data.values.toList();
|
||||
|
||||
for (final c in conditions) {
|
||||
if (c is WhereQuery) {
|
||||
@@ -81,15 +108,15 @@ class CrudInMemoryDataSourceImpl<Model extends ObjectModel>
|
||||
}
|
||||
}
|
||||
|
||||
result.cast<Model>();
|
||||
result.cast<Map<String, dynamic>>();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<List<Model?>> stream({
|
||||
Stream<List<Map<String, dynamic>?>> stream({
|
||||
String? id,
|
||||
List<QueryInterface>? conditions,
|
||||
List<Query>? conditions,
|
||||
bool includeMetadataChanges = false,
|
||||
}) =>
|
||||
_streamData.stream.map((result) {
|
||||
@@ -97,7 +124,7 @@ class CrudInMemoryDataSourceImpl<Model extends ObjectModel>
|
||||
return result;
|
||||
}
|
||||
|
||||
List<Model?> res = result;
|
||||
List<Map<String, dynamic>?> res = result;
|
||||
|
||||
for (final c in conditions) {
|
||||
if (c is WhereQuery) {
|
||||
@@ -120,56 +147,63 @@ class CrudInMemoryDataSourceImpl<Model extends ObjectModel>
|
||||
@override
|
||||
Future<void> update(
|
||||
String id, {
|
||||
Model? object,
|
||||
Map<String, dynamic>? raw,
|
||||
Map<String, dynamic>? object,
|
||||
}) {
|
||||
// TODO(hpcl): implement update
|
||||
throw UnimplementedError();
|
||||
if (object != null && _data.containsKey(id)) {
|
||||
_data[id] = _data[id]!.merge(object);
|
||||
_streamData.add(_data.values.toList());
|
||||
}
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateAll(Map<String, Object?>? data) {
|
||||
// TODO(hcpl): implement updateAll
|
||||
throw UnimplementedError();
|
||||
if (data != null) {
|
||||
for (final entry in _data.entries) {
|
||||
_data[entry.key] = entry.value.merge(data);
|
||||
}
|
||||
_streamData.add(_data.values.toList());
|
||||
}
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
bool _whereQuery(QueryInterface condition, Model? object) {
|
||||
bool _whereQuery(Query condition, Map<String, dynamic>? object) {
|
||||
if (object == null) {
|
||||
return false;
|
||||
}
|
||||
final raw = toMap.call(object);
|
||||
if (condition is WhereQuery) {
|
||||
switch (condition.type) {
|
||||
case WhereQueryType.isEqualTo:
|
||||
return raw[condition.field] == condition.value;
|
||||
return object[condition.field] == condition.value;
|
||||
case WhereQueryType.isNotEqualTo:
|
||||
return raw[condition.field] != condition.value;
|
||||
return object[condition.field] != condition.value;
|
||||
case WhereQueryType.isLessThan:
|
||||
return (raw[condition.field] as num?) < (condition.value as num?);
|
||||
return (object[condition.field] as num?) < (condition.value as num?);
|
||||
case WhereQueryType.isLessThanOrEqualTo:
|
||||
return (raw[condition.field] as num?) <= (condition.value as num?);
|
||||
return (object[condition.field] as num?) <= (condition.value as num?);
|
||||
case WhereQueryType.isGreaterThan:
|
||||
return (raw[condition.field] as num?) > (condition.value as num?);
|
||||
return (object[condition.field] as num?) > (condition.value as num?);
|
||||
case WhereQueryType.isGreaterThanOrEqualTo:
|
||||
return (raw[condition.field] as num?) >= (condition.value as num?);
|
||||
return (object[condition.field] as num?) >= (condition.value as num?);
|
||||
case WhereQueryType.arrayContains:
|
||||
return (raw[condition.field] as List<Object>?)
|
||||
return (object[condition.field] as List<Object>?)
|
||||
?.contains(condition.value) ??
|
||||
false;
|
||||
case WhereQueryType.arrayContainsAny:
|
||||
bool res = false;
|
||||
for (final o in condition.value as List<Object>) {
|
||||
res = (raw[condition.field] as List<Object>?)?.contains(o) ?? false;
|
||||
res = (object[condition.field] as List<Object>?)?.contains(o) ??
|
||||
false;
|
||||
}
|
||||
return res;
|
||||
case WhereQueryType.whereIn:
|
||||
return (condition.value as List<Object>)
|
||||
.contains(raw[condition.field]);
|
||||
.contains(object[condition.field]);
|
||||
case WhereQueryType.whereNotIn:
|
||||
return !(condition.value as List<Object>)
|
||||
.contains(raw[condition.field]);
|
||||
.contains(object[condition.field]);
|
||||
case WhereQueryType.isNull:
|
||||
return raw[condition.field] == null;
|
||||
return object[condition.field] == null;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
-230
@@ -1,230 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/data_sources/crud_data_source.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
|
||||
|
||||
/// {@template crud_firestore_data_source_impl}
|
||||
/// A concrete implementation of [CrudDataSource] that uses
|
||||
/// [FirebaseFirestore] as the data source.
|
||||
/// {@endtemplate}
|
||||
class CrudFirestoreDataSourceImpl<Model extends ObjectModel, Entity>
|
||||
extends CrudDataSource<Model> {
|
||||
/// {@macro crud_firestore_data_source_impl}
|
||||
CrudFirestoreDataSourceImpl(
|
||||
String collection, {
|
||||
/// The function that converts a [DocumentSnapshot] to a [Model].
|
||||
required Model Function(
|
||||
DocumentSnapshot<Map<String, dynamic>>,
|
||||
SnapshotOptions?,
|
||||
) fromFirestore,
|
||||
|
||||
/// The function that converts a [Model] to a [Map<String, Object?>].
|
||||
required Map<String, Object?> Function(Model, SetOptions?) toFirestore,
|
||||
FirebaseFirestore? firestore,
|
||||
}) : _firestore = firestore ?? FirebaseFirestore.instance,
|
||||
_toFirestore = toFirestore {
|
||||
_collectionReference =
|
||||
_firestore.collection(collection).withConverter<Model>(
|
||||
fromFirestore: fromFirestore,
|
||||
toFirestore: toFirestore,
|
||||
);
|
||||
}
|
||||
final FirebaseFirestore _firestore;
|
||||
|
||||
final Map<String, Object?> Function(Model, SetOptions?) _toFirestore;
|
||||
late CollectionReference<Model> _collectionReference;
|
||||
|
||||
@override
|
||||
Future<void> create(Model object, {String? id}) {
|
||||
if (id != null) {
|
||||
return _collectionReference.doc(id).set(object);
|
||||
} else {
|
||||
if (object.id != null) {
|
||||
return _collectionReference.doc(object.id).set(object);
|
||||
}
|
||||
return _collectionReference.add(object);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(String id) => _collectionReference.doc(id).delete();
|
||||
|
||||
@override
|
||||
Future<void> deleteAll() async {
|
||||
final batch = _firestore.batch();
|
||||
final QuerySnapshot snapshots = await _collectionReference.get();
|
||||
for (final DocumentSnapshot snapshot in snapshots.docs) {
|
||||
batch.delete(snapshot.reference);
|
||||
}
|
||||
return batch.commit();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Model?> get(String id) async {
|
||||
final DocumentSnapshot<Model> snapshot =
|
||||
await _collectionReference.doc(id).get();
|
||||
return snapshot.data();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Model?>> getAll() async {
|
||||
final QuerySnapshot<Model> snapshots = await _collectionReference.get();
|
||||
return snapshots.docs.map((snapshot) => snapshot.data()).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Model?>> query(List<QueryInterface> conditions) async {
|
||||
Query<Model> query = _collectionReference;
|
||||
for (final condition in conditions) {
|
||||
query = _queryParser(condition, query);
|
||||
}
|
||||
final QuerySnapshot<Model> snapshots = await query.get();
|
||||
return snapshots.docs.map((snapshot) => snapshot.data()).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<List<Model?>> stream({
|
||||
String? id,
|
||||
List<QueryInterface>? conditions,
|
||||
bool includeMetadataChanges = false,
|
||||
}) {
|
||||
if (id != null) {
|
||||
return _collectionReference
|
||||
.doc(id)
|
||||
.snapshots(
|
||||
includeMetadataChanges: includeMetadataChanges,
|
||||
)
|
||||
.map<List<Model?>>(
|
||||
(snapshot) => [snapshot.data()],
|
||||
);
|
||||
} else {
|
||||
if (conditions != null) {
|
||||
Query<Model> query = _collectionReference;
|
||||
for (final condition in conditions) {
|
||||
query = _queryParser(condition, query);
|
||||
}
|
||||
return query
|
||||
.snapshots(
|
||||
includeMetadataChanges: includeMetadataChanges,
|
||||
)
|
||||
.map(
|
||||
(querySnapshot) => querySnapshot.docs
|
||||
.map((snapshot) => snapshot.data())
|
||||
.toList(),
|
||||
);
|
||||
} else {
|
||||
return _collectionReference
|
||||
.snapshots(
|
||||
includeMetadataChanges: includeMetadataChanges,
|
||||
)
|
||||
.map(
|
||||
(querySnapshot) => querySnapshot.docs
|
||||
.map((snapshot) => snapshot.data())
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> update(
|
||||
String id, {
|
||||
Model? object,
|
||||
Map<String, dynamic>? raw,
|
||||
}) {
|
||||
if (object != null) {
|
||||
return _collectionReference
|
||||
.doc(id)
|
||||
.update(_toFirestore.call(object, null));
|
||||
} else {
|
||||
if (raw != null) {
|
||||
return _collectionReference.doc(id).update(raw);
|
||||
} else {
|
||||
throw Exception('You must provide an object or a raw map');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateAll(Map<String, Object?>? data) async {
|
||||
if (data == null) {
|
||||
throw Exception('You must provide data to update');
|
||||
}
|
||||
final batch = _firestore.batch();
|
||||
final QuerySnapshot<Model> snapshots = await _collectionReference.get();
|
||||
for (final DocumentSnapshot snapshot in snapshots.docs) {
|
||||
batch.update(snapshot.reference, data);
|
||||
}
|
||||
return batch.commit();
|
||||
}
|
||||
|
||||
Query<Model> _queryParser(QueryInterface condition, Object query) {
|
||||
query as Query<Model>;
|
||||
if (condition is WhereQuery) {
|
||||
switch (condition.type) {
|
||||
case WhereQueryType.isEqualTo:
|
||||
return query.where(condition.field, isEqualTo: condition.value);
|
||||
case WhereQueryType.isNotEqualTo:
|
||||
return query.where(condition.field, isNotEqualTo: condition.value);
|
||||
case WhereQueryType.isLessThan:
|
||||
return query.where(condition.field, isLessThan: condition.value);
|
||||
case WhereQueryType.isLessThanOrEqualTo:
|
||||
return query.where(
|
||||
condition.field,
|
||||
isLessThanOrEqualTo: condition.value,
|
||||
);
|
||||
case WhereQueryType.isGreaterThan:
|
||||
return query.where(condition.field, isGreaterThan: condition.value);
|
||||
case WhereQueryType.isGreaterThanOrEqualTo:
|
||||
return query.where(
|
||||
condition.field,
|
||||
isGreaterThanOrEqualTo: condition.value,
|
||||
);
|
||||
case WhereQueryType.arrayContains:
|
||||
return query.where(condition.field, arrayContains: condition.value);
|
||||
case WhereQueryType.arrayContainsAny:
|
||||
return query.where(
|
||||
condition.field,
|
||||
arrayContainsAny: condition.value as List<Object>,
|
||||
);
|
||||
case WhereQueryType.whereIn:
|
||||
return query.where(
|
||||
condition.field,
|
||||
whereIn: condition.value as List<Object>,
|
||||
);
|
||||
case WhereQueryType.whereNotIn:
|
||||
return query.where(
|
||||
condition.field,
|
||||
whereNotIn: condition.value as List<Object>,
|
||||
);
|
||||
case WhereQueryType.isNull:
|
||||
return query.where(condition.field, isNull: condition.value as bool);
|
||||
}
|
||||
} else if (condition is LimitQuery) {
|
||||
return query.limit(condition.limit);
|
||||
} else if (condition is OrderByQuery) {
|
||||
return query.orderBy(
|
||||
condition.field,
|
||||
descending: !condition.ascending,
|
||||
);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Copyright (C) 2023 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
@@ -15,8 +15,8 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/data_sources/crud_data_source.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/model_mapper.dart';
|
||||
import 'package:wyatt_crud_bloc/src/data/data_sources/crud_data_source.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
@@ -24,19 +24,23 @@ import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
/// {@template crud_repository_impl}
|
||||
/// A repository that implements the [CrudRepository] interface.
|
||||
/// {@endtemplate}
|
||||
class CrudRepositoryImpl<Model extends ObjectModel>
|
||||
extends CrudRepository<Model> {
|
||||
class CrudRepositoryImpl<Model> extends CrudRepository<Model> {
|
||||
/// {@macro crud_repository_impl}
|
||||
const CrudRepositoryImpl({
|
||||
required CrudDataSource<Model> crudDataSource,
|
||||
required this.modelMapper,
|
||||
required CrudDataSource crudDataSource,
|
||||
}) : _crudDataSource = crudDataSource;
|
||||
final CrudDataSource<Model> _crudDataSource;
|
||||
|
||||
final CrudDataSource _crudDataSource;
|
||||
|
||||
@override
|
||||
final ModelMapper<Model> modelMapper;
|
||||
|
||||
@override
|
||||
FutureOrResult<void> create(Model object, {String? id}) =>
|
||||
Result.tryCatchAsync<void, AppException, AppException>(
|
||||
() async {
|
||||
await _crudDataSource.create(object, id: id);
|
||||
await _crudDataSource.create(modelMapper.toJson(object), id: id);
|
||||
},
|
||||
(error) => error,
|
||||
);
|
||||
@@ -44,14 +48,26 @@ class CrudRepositoryImpl<Model extends ObjectModel>
|
||||
@override
|
||||
FutureOrResult<Model?> get(String id) =>
|
||||
Result.tryCatchAsync<Model?, AppException, AppException>(
|
||||
() async => _crudDataSource.get(id),
|
||||
() async => modelMapper.fromJson(await _crudDataSource.get(id)),
|
||||
(error) => error,
|
||||
);
|
||||
|
||||
@override
|
||||
FutureOrResult<List<Model?>> getAll() =>
|
||||
Result.tryCatchAsync<List<Model?>, AppException, AppException>(
|
||||
() async => _crudDataSource.getAll(),
|
||||
() async {
|
||||
final lst = await _crudDataSource.getAll();
|
||||
if (lst.isNotNull) {
|
||||
return lst!.map((raw) {
|
||||
try {
|
||||
return modelMapper.fromJson(raw as Map<String, dynamic>?);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}).toList();
|
||||
}
|
||||
throw const ServerException();
|
||||
},
|
||||
(error) => error,
|
||||
);
|
||||
|
||||
@@ -62,7 +78,18 @@ class CrudRepositoryImpl<Model extends ObjectModel>
|
||||
Map<String, dynamic>? raw,
|
||||
}) =>
|
||||
Result.tryCatchAsync<void, AppException, AppException>(
|
||||
() async => _crudDataSource.update(id, object: object, raw: raw),
|
||||
() async {
|
||||
if (object != null) {
|
||||
await _crudDataSource.update(
|
||||
id,
|
||||
object: modelMapper.toJson(object),
|
||||
);
|
||||
} else if (raw != null) {
|
||||
await _crudDataSource.update(id, object: raw);
|
||||
} else {
|
||||
throw const ServerException();
|
||||
}
|
||||
},
|
||||
(error) => error,
|
||||
);
|
||||
|
||||
@@ -88,20 +115,40 @@ class CrudRepositoryImpl<Model extends ObjectModel>
|
||||
);
|
||||
|
||||
@override
|
||||
FutureOrResult<List<Model?>> query(List<QueryInterface> conditions) =>
|
||||
FutureOrResult<List<Model?>> search(List<Query> conditions) =>
|
||||
Result.tryCatchAsync<List<Model?>, AppException, AppException>(
|
||||
() async => _crudDataSource.query(conditions),
|
||||
() async {
|
||||
final lst = await _crudDataSource.search(conditions);
|
||||
if (lst.isNotNull) {
|
||||
return lst!.map((raw) {
|
||||
try {
|
||||
return modelMapper.fromJson(raw as Map<String, dynamic>?);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}).toList();
|
||||
}
|
||||
throw const ServerException();
|
||||
},
|
||||
(error) => error,
|
||||
);
|
||||
|
||||
@override
|
||||
StreamResult<List<Model?>> stream({
|
||||
String? id,
|
||||
List<QueryInterface>? conditions,
|
||||
List<Query>? conditions,
|
||||
}) =>
|
||||
_crudDataSource.stream(id: id, conditions: conditions).map((lst) {
|
||||
if (lst.isNotNull) {
|
||||
return Ok<List<Model?>, AppException>(lst);
|
||||
return Ok<List<Model?>, AppException>(
|
||||
lst!.map((raw) {
|
||||
try {
|
||||
return modelMapper.fromJson(raw as Map<String, dynamic>?);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
return Err<List<Model?>, AppException>(const ServerException());
|
||||
});
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
export 'crud_repository_impl.dart';
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
export 'crud_data_source.dart';
|
||||
@@ -14,7 +14,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
export 'data_sources/data_sources.dart';
|
||||
export 'entities/entities.dart';
|
||||
export 'repositories/repositories.dart';
|
||||
export 'entities/query.dart';
|
||||
export 'repositories/crud_repository.dart';
|
||||
export 'usecases/usecases.dart';
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
export 'object_model.dart';
|
||||
export 'query.dart';
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
|
||||
/// {@template object_model}
|
||||
/// An abstract class that represents an object model.
|
||||
/// {@endtemplate}
|
||||
abstract class ObjectModel extends Entity {
|
||||
/// {@macro object_model}
|
||||
const ObjectModel();
|
||||
|
||||
/// The id of the object model.
|
||||
String? get id;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Copyright (C) 2023 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
@@ -17,25 +17,20 @@
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
|
||||
|
||||
// // ignore: one_member_abstracts
|
||||
// abstract class QueryParser<Q> {
|
||||
// Q parser(QueryInterface condition, Q query);
|
||||
// }
|
||||
|
||||
typedef QueryParser<Q> = Q Function(QueryInterface condition, Q query);
|
||||
typedef QueryParser<Q> = Q Function(Query condition, Q query);
|
||||
|
||||
/// {@template query}
|
||||
/// An abstract class that represents a query.
|
||||
/// {@endtemplate}
|
||||
abstract class QueryInterface extends Entity {
|
||||
base class Query extends Entity {
|
||||
/// {@macro query}
|
||||
const QueryInterface();
|
||||
const Query();
|
||||
}
|
||||
|
||||
/// {@template where_query}
|
||||
/// Represents a where query.
|
||||
/// {@endtemplate}
|
||||
class WhereQuery<Value> extends QueryInterface {
|
||||
final class WhereQuery<Value> extends Query {
|
||||
/// {@macro where_query}
|
||||
const WhereQuery(this.type, this.field, this.value);
|
||||
|
||||
@@ -52,7 +47,7 @@ class WhereQuery<Value> extends QueryInterface {
|
||||
/// {@template limit_query}
|
||||
/// Represents a limit query.
|
||||
/// {@endtemplate}
|
||||
class LimitQuery extends QueryInterface {
|
||||
final class LimitQuery extends Query {
|
||||
/// {@macro limit_query}
|
||||
const LimitQuery(this.limit);
|
||||
|
||||
@@ -63,7 +58,7 @@ class LimitQuery extends QueryInterface {
|
||||
/// {@template offset_query}
|
||||
/// Represents an offset query.
|
||||
/// {@endtemplate}
|
||||
class OrderByQuery extends QueryInterface {
|
||||
final class OrderByQuery extends Query {
|
||||
/// {@macro offset_query}
|
||||
const OrderByQuery(this.field, {this.ascending = true});
|
||||
|
||||
|
||||
@@ -15,17 +15,18 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/model_mapper.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
|
||||
|
||||
/// {@template crud_repository}
|
||||
/// An abstract class that represents a SCRUD repository.
|
||||
/// {@endtemplate}
|
||||
abstract class CrudRepository<Model extends ObjectModel>
|
||||
extends BaseRepository {
|
||||
abstract class CrudRepository<Model> extends BaseRepository {
|
||||
/// {@macro crud_repository}
|
||||
const CrudRepository();
|
||||
|
||||
ModelMapper<Model> get modelMapper;
|
||||
|
||||
/// Creates a new object.
|
||||
FutureOrResult<void> create(Model object, {String? id});
|
||||
|
||||
@@ -52,11 +53,11 @@ abstract class CrudRepository<Model extends ObjectModel>
|
||||
FutureOrResult<void> deleteAll();
|
||||
|
||||
/// Queries objects by [conditions].
|
||||
FutureOrResult<List<Model?>> query(List<QueryInterface> conditions);
|
||||
FutureOrResult<List<Model?>> search(List<Query> conditions);
|
||||
|
||||
/// Streams objects by [conditions].
|
||||
StreamResult<List<Model?>> stream({
|
||||
String? id,
|
||||
List<QueryInterface>? conditions,
|
||||
List<Query>? conditions,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Copyright (C) 2023 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
@@ -14,31 +14,22 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/usecases.dart';
|
||||
|
||||
/// {@template create}
|
||||
/// A use case that creates an object model.
|
||||
/// {@endtemplate}
|
||||
class Create<Model extends ObjectModel> extends AsyncUseCase<Model, void>
|
||||
class Create<Model> extends Usecase<Model, void>
|
||||
with CreateOperation<Model, void> {
|
||||
/// {@macro create}
|
||||
const Create(this._crudRepository);
|
||||
const Create(this.crudRepository);
|
||||
|
||||
final CrudRepository<Model> _crudRepository;
|
||||
|
||||
@override
|
||||
FutureOr<void> onStart(Model? params) {
|
||||
if (params == null) {
|
||||
throw ClientException('$Model cannot be null.');
|
||||
}
|
||||
}
|
||||
final CrudRepository<Model> crudRepository;
|
||||
|
||||
@override
|
||||
FutureOrResult<void> execute(Model? params) =>
|
||||
_crudRepository.create(params!);
|
||||
crudRepository.create(params as Model);
|
||||
}
|
||||
|
||||
@@ -14,31 +14,21 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/usecases.dart';
|
||||
|
||||
/// {@template delete}
|
||||
/// A use case that deletes an object model.
|
||||
/// {@endtemplate}
|
||||
class Delete<Model extends ObjectModel> extends AsyncUseCase<String, void>
|
||||
with DeleteOperation<Model, String> {
|
||||
class Delete<Model> extends Usecase<String, void> with DeleteOperation<String> {
|
||||
/// {@macro delete}
|
||||
const Delete(this._crudRepository);
|
||||
const Delete(this.crudRepository);
|
||||
|
||||
final CrudRepository<Model> _crudRepository;
|
||||
|
||||
@override
|
||||
FutureOr<void> onStart(String? params) {
|
||||
if (params == null) {
|
||||
throw const ClientException('Id cannot be null.');
|
||||
}
|
||||
}
|
||||
final CrudRepository<Model> crudRepository;
|
||||
|
||||
@override
|
||||
FutureOrResult<void> execute(String? params) =>
|
||||
_crudRepository.delete(params!);
|
||||
crudRepository.delete(params!);
|
||||
}
|
||||
|
||||
@@ -16,19 +16,18 @@
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/usecases.dart';
|
||||
|
||||
/// {@template delete_all}
|
||||
/// A use case that deletes all the object models.
|
||||
/// {@endtemplate}
|
||||
class DeleteAll<Model extends ObjectModel> extends AsyncUseCase<void, void>
|
||||
with DeleteOperation<Model, void> {
|
||||
class DeleteAll<Model> extends NoParamUsecase<void> with DeleteOperation<void> {
|
||||
/// {@macro delete_all}
|
||||
const DeleteAll(this._crudRepository);
|
||||
const DeleteAll(this.crudRepository);
|
||||
|
||||
final CrudRepository<Model> _crudRepository;
|
||||
final CrudRepository<Model> crudRepository;
|
||||
|
||||
@override
|
||||
FutureOrResult<void> execute(void params) => _crudRepository.deleteAll();
|
||||
FutureOrResult<void> execute(void params) => crudRepository.deleteAll();
|
||||
}
|
||||
|
||||
@@ -14,31 +14,21 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/usecases.dart';
|
||||
|
||||
/// {@template get}
|
||||
/// A use case that gets an object model.
|
||||
/// {@endtemplate}
|
||||
class Get<Model extends ObjectModel> extends AsyncUseCase<String, Model?>
|
||||
with ReadOperation<Model, String, Model?> {
|
||||
class Get<Model> extends Usecase<String, Model?>
|
||||
with ReadOperation<String, Model?> {
|
||||
/// {@macro get}
|
||||
Get(this._crudRepository);
|
||||
Get(this.crudRepository);
|
||||
|
||||
final CrudRepository<Model> _crudRepository;
|
||||
final CrudRepository<Model> crudRepository;
|
||||
|
||||
@override
|
||||
FutureOr<void> onStart(String? params) {
|
||||
if (params == null) {
|
||||
throw const ClientException('Id cannot be null.');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOrResult<Model?> execute(String? params) =>
|
||||
_crudRepository.get(params!);
|
||||
FutureOrResult<Model?> execute(String? params) => crudRepository.get(params!);
|
||||
}
|
||||
|
||||
@@ -16,19 +16,19 @@
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/usecases.dart';
|
||||
|
||||
/// {@template get_all}
|
||||
/// A use case that gets all the object models.
|
||||
/// {@endtemplate}
|
||||
class GetAll<Model extends ObjectModel> extends AsyncUseCase<void, List<Model?>>
|
||||
with ReadOperation<Model, void, List<Model?>> {
|
||||
class GetAll<Model> extends NoParamUsecase<List<Model?>>
|
||||
with ReadOperation<void, List<Model?>> {
|
||||
/// {@macro get_all}
|
||||
const GetAll(this._crudRepository);
|
||||
const GetAll(this.crudRepository);
|
||||
|
||||
final CrudRepository<Model> _crudRepository;
|
||||
final CrudRepository<Model> crudRepository;
|
||||
|
||||
@override
|
||||
FutureOrResult<List<Model?>> execute(void params) => _crudRepository.getAll();
|
||||
FutureOrResult<List<Model?>> execute(void params) => crudRepository.getAll();
|
||||
}
|
||||
|
||||
@@ -30,5 +30,5 @@ class StreamParameters {
|
||||
final String? id;
|
||||
|
||||
/// The conditions of the query.
|
||||
final List<QueryInterface>? conditions;
|
||||
final List<Query>? conditions;
|
||||
}
|
||||
|
||||
+9
-19
@@ -14,33 +14,23 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/usecases.dart';
|
||||
|
||||
/// {@template query}
|
||||
/// {@template search}
|
||||
/// A use case that queries the object models.
|
||||
/// {@endtemplate}
|
||||
class Query<Model extends ObjectModel>
|
||||
extends AsyncUseCase<List<QueryInterface>, List<Model?>>
|
||||
with ReadOperation<Model, List<QueryInterface>, List<Model?>> {
|
||||
/// {@macro query}
|
||||
const Query(this._crudRepository);
|
||||
class Search<Model> extends Usecase<List<Query>, List<Model?>>
|
||||
with ReadOperation<List<Query>, List<Model?>> {
|
||||
/// {@macro search}
|
||||
const Search(this.crudRepository);
|
||||
|
||||
final CrudRepository<Model> _crudRepository;
|
||||
final CrudRepository<Model> crudRepository;
|
||||
|
||||
@override
|
||||
FutureOr<void> onStart(List<QueryInterface>? params) {
|
||||
if (params == null) {
|
||||
throw const ClientException('List of conditions cannot be null.');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOrResult<List<Model?>> execute(List<QueryInterface>? params) =>
|
||||
_crudRepository.query(params!);
|
||||
FutureOrResult<List<Model?>> execute(List<Query>? params) =>
|
||||
crudRepository.search(params!);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/usecases.dart';
|
||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||
|
||||
/// {@template stream}
|
||||
/// A use case that streams the object models.
|
||||
/// {@endtemplate}
|
||||
class Stream<Model>
|
||||
extends Usecase<StreamParameters, StreamResult<List<Model?>>>
|
||||
with ReadOperation<StreamParameters, StreamResult<List<Model?>>> {
|
||||
/// {@macro stream}
|
||||
const Stream(this.crudRepository);
|
||||
|
||||
final CrudRepository<Model> crudRepository;
|
||||
|
||||
@override
|
||||
FutureOrResult<StreamResult<List<Model?>>> execute(
|
||||
StreamParameters? params,
|
||||
) async =>
|
||||
Ok(
|
||||
crudRepository.stream(
|
||||
id: params!.id,
|
||||
conditions: params.conditions,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -14,35 +14,24 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/usecases.dart';
|
||||
|
||||
/// {@template update}
|
||||
/// A use case that updates an object model.
|
||||
/// {@endtemplate}
|
||||
class Update<Model extends ObjectModel>
|
||||
extends AsyncUseCase<UpdateParameters<Model>, void>
|
||||
with UpdateOperation<Model, UpdateParameters<Model>> {
|
||||
class Update<Model> extends Usecase<UpdateParameters<Model>, void>
|
||||
with UpdateOperation<UpdateParameters<Model>> {
|
||||
/// {@macro update}
|
||||
const Update(this._crudRepository);
|
||||
const Update(this.crudRepository);
|
||||
|
||||
final CrudRepository<Model> _crudRepository;
|
||||
|
||||
@override
|
||||
FutureOr<void> onStart(UpdateParameters<Model>? params) {
|
||||
if (params == null) {
|
||||
throw const ClientException('Update parameters cannot be null.');
|
||||
}
|
||||
}
|
||||
final CrudRepository<Model> crudRepository;
|
||||
|
||||
@override
|
||||
FutureOrResult<void> execute(UpdateParameters<Model>? params) =>
|
||||
_crudRepository.update(
|
||||
crudRepository.update(
|
||||
params!.id,
|
||||
object: params.object,
|
||||
raw: params.raw,
|
||||
|
||||
@@ -14,32 +14,22 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/usecases.dart';
|
||||
|
||||
/// {@template update_all}
|
||||
/// A use case that updates all the object models.
|
||||
/// {@endtemplate}
|
||||
class UpdateAll<Model extends ObjectModel>
|
||||
extends AsyncUseCase<Map<String, Object?>, void>
|
||||
with UpdateOperation<Model, Map<String, Object?>> {
|
||||
class UpdateAll<Model> extends Usecase<Map<String, Object?>, void>
|
||||
with UpdateOperation<Map<String, Object?>> {
|
||||
/// {@macro update_all}
|
||||
const UpdateAll(this._crudRepository);
|
||||
const UpdateAll(this.crudRepository);
|
||||
|
||||
final CrudRepository<Model> _crudRepository;
|
||||
|
||||
@override
|
||||
FutureOr<void> onStart(Map<String, Object?>? params) {
|
||||
if (params == null) {
|
||||
throw const ClientException('Data cannot be null.');
|
||||
}
|
||||
}
|
||||
final CrudRepository<Model> crudRepository;
|
||||
|
||||
@override
|
||||
FutureOrResult<void> execute(Map<String, Object?>? params) =>
|
||||
_crudRepository.updateAll(params!);
|
||||
crudRepository.updateAll(params!);
|
||||
}
|
||||
|
||||
@@ -14,12 +14,36 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/enums/operation_type.dart';
|
||||
|
||||
export 'create.dart';
|
||||
export 'delete.dart';
|
||||
export 'delete_all.dart';
|
||||
export 'get.dart';
|
||||
export 'get_all.dart';
|
||||
export 'params/params.dart';
|
||||
export 'query.dart';
|
||||
export 'search.dart';
|
||||
export 'update.dart';
|
||||
export 'update_all.dart';
|
||||
|
||||
abstract class Usecase<In, Out> extends AsyncUseCase<In, Out> {
|
||||
const Usecase();
|
||||
|
||||
@override
|
||||
FutureOr<void> onStart(In? params) {
|
||||
if (params == null) {
|
||||
throw ClientException('$In cannot be null.');
|
||||
}
|
||||
}
|
||||
|
||||
OperationType get operationType;
|
||||
}
|
||||
|
||||
abstract class NoParamUsecase<Out> extends AsyncUseCase<void, Out> {
|
||||
const NoParamUsecase();
|
||||
|
||||
OperationType get operationType;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/model_identifier.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/create.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/delete.dart';
|
||||
@@ -25,7 +25,7 @@ import 'package:wyatt_crud_bloc/src/domain/usecases/delete_all.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/get.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/get_all.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/query.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/search.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/update.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/update_all.dart';
|
||||
import 'package:wyatt_crud_bloc/src/features/crud/blocs/crud_base_cubit/crud_base_cubit.dart';
|
||||
@@ -33,8 +33,7 @@ import 'package:wyatt_crud_bloc/src/features/crud/blocs/crud_base_cubit/crud_bas
|
||||
/// {@template crud_cubit_advanced}
|
||||
/// Cubit that handles CRUD operations with more granularity.
|
||||
/// {@endtemplate}
|
||||
abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
extends CrudBaseCubit {
|
||||
abstract class CrudAdvancedCubit<Model> extends CrudBaseCubit {
|
||||
/// {@macro crud_cubit}
|
||||
CrudAdvancedCubit() : super();
|
||||
|
||||
@@ -43,10 +42,14 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
Delete<Model>? get crudDelete;
|
||||
GetAll<Model>? get crudGetAll;
|
||||
Get<Model>? get crudGet;
|
||||
Query<Model>? get crudQuery;
|
||||
Search<Model>? get crudSearch;
|
||||
UpdateAll<Model>? get crudUpdateAll;
|
||||
Update<Model>? get crudUpdate;
|
||||
|
||||
/// Model identifier.
|
||||
/// Used to identify a model.
|
||||
ModelIdentifier<Model> get modelIdentifier;
|
||||
|
||||
FutureOr<void> create(Model model) async {
|
||||
final crud = crudCreate;
|
||||
if (crud == null) {
|
||||
@@ -54,7 +57,7 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
}
|
||||
|
||||
final stateCopy = state;
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudCreating());
|
||||
final result = await crud.call(model);
|
||||
emit(
|
||||
result.fold(
|
||||
@@ -63,7 +66,7 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
if (stateCopy.data == null) {
|
||||
return CrudLoaded<Model?>(model);
|
||||
}
|
||||
if (stateCopy.data!.id == model.id) {
|
||||
if (modelIdentifier.areEqual(stateCopy.data, model)) {
|
||||
return CrudLoaded<Model?>(model);
|
||||
}
|
||||
|
||||
@@ -78,7 +81,7 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
return CrudListLoaded<Model?>(lst);
|
||||
}
|
||||
|
||||
return const CrudOkReturn();
|
||||
return const CrudCreated();
|
||||
},
|
||||
(error) => CrudError(error.toString()),
|
||||
),
|
||||
@@ -92,13 +95,16 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
}
|
||||
|
||||
final stateCopy = state;
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudDeleting());
|
||||
final result = await crud.call(id);
|
||||
emit(
|
||||
result.fold(
|
||||
(_) {
|
||||
if (stateCopy is CrudLoaded<Model?>) {
|
||||
if (stateCopy.data?.id == id) {
|
||||
if (modelIdentifier.isIdentifier(
|
||||
model: stateCopy.data,
|
||||
identifier: id,
|
||||
)) {
|
||||
return CrudLoaded<Model?>(null);
|
||||
}
|
||||
|
||||
@@ -106,11 +112,19 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
}
|
||||
if (stateCopy is CrudListLoaded<Model?>) {
|
||||
return CrudListLoaded<Model?>(
|
||||
stateCopy.data.where((element) => element?.id != id).toList(),
|
||||
stateCopy.data.where((element) {
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
return !modelIdentifier.isIdentifier(
|
||||
model: element,
|
||||
identifier: id,
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
return const CrudOkReturn();
|
||||
return const CrudDeleted();
|
||||
},
|
||||
(error) => CrudError(error.toString()),
|
||||
),
|
||||
@@ -123,7 +137,7 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
return;
|
||||
}
|
||||
final stateCopy = state;
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudDeleting());
|
||||
final result = await crud.call(null);
|
||||
emit(
|
||||
result.fold(
|
||||
@@ -147,7 +161,7 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
if (crud == null) {
|
||||
return;
|
||||
}
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudReading());
|
||||
final result = await crud.call(id);
|
||||
emit(
|
||||
result.fold(
|
||||
@@ -162,7 +176,7 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
if (crud == null) {
|
||||
return;
|
||||
}
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudReading());
|
||||
final result = await crud.call(null);
|
||||
emit(
|
||||
result.fold(
|
||||
@@ -172,13 +186,13 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<void> query(List<QueryInterface> conditions) async {
|
||||
final crud = crudQuery;
|
||||
FutureOr<void> search(List<Query> conditions) async {
|
||||
final crud = crudSearch;
|
||||
if (crud == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudReading());
|
||||
final result = await crud.call(conditions);
|
||||
emit(
|
||||
result.fold(
|
||||
@@ -195,13 +209,16 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
}
|
||||
|
||||
final stateCopy = state;
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudUpdating());
|
||||
final result = await crud.call(param);
|
||||
emit(
|
||||
await result.foldAsync(
|
||||
(_) async {
|
||||
if (stateCopy is CrudLoaded<Model?>) {
|
||||
if (stateCopy.data?.id == param.id) {
|
||||
if (modelIdentifier.isIdentifier(
|
||||
model: stateCopy.data,
|
||||
identifier: param.id,
|
||||
)) {
|
||||
// Same object, need to update actual stateCopy
|
||||
final crudGet = this.crudGet;
|
||||
if (crudGet == null) {
|
||||
@@ -216,8 +233,15 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
return stateCopy;
|
||||
}
|
||||
if (stateCopy is CrudListLoaded<Model?>) {
|
||||
final bool listContains =
|
||||
stateCopy.data.any((element) => element?.id == param.id);
|
||||
final bool listContains = stateCopy.data.any((element) {
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
return modelIdentifier.isIdentifier(
|
||||
model: element,
|
||||
identifier: param.id,
|
||||
);
|
||||
});
|
||||
if (listContains) {
|
||||
// Loaded objects contains the modified object.
|
||||
|
||||
@@ -228,17 +252,23 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
}
|
||||
final newVersion = await crudGet.call(param.id);
|
||||
if (newVersion.isOk) {
|
||||
final newList = stateCopy.data
|
||||
.where(
|
||||
(element) => element?.id != param.id,
|
||||
)
|
||||
.toList();
|
||||
final newList = stateCopy.data.where(
|
||||
(element) {
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
return !modelIdentifier.isIdentifier(
|
||||
model: element,
|
||||
identifier: param.id,
|
||||
);
|
||||
},
|
||||
).toList();
|
||||
return CrudListLoaded<Model?>(newList + [newVersion.ok]);
|
||||
}
|
||||
}
|
||||
return stateCopy;
|
||||
}
|
||||
return const CrudOkReturn();
|
||||
return const CrudUpdated();
|
||||
},
|
||||
(error) async => CrudError(error.toString()),
|
||||
),
|
||||
@@ -252,7 +282,7 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
}
|
||||
|
||||
final stateCopy = state;
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudUpdating());
|
||||
final result = await crud.call(param);
|
||||
emit(
|
||||
await result.foldAsync(
|
||||
@@ -264,7 +294,8 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
// No read operation, can't update stateCopy.
|
||||
return stateCopy;
|
||||
}
|
||||
final actualId = stateCopy.data?.id;
|
||||
final actualId =
|
||||
modelIdentifier.getIdentifierOrNull(stateCopy.data);
|
||||
final newVersion = await crudGet.call(actualId ?? '');
|
||||
if (newVersion.isOk) {
|
||||
return CrudLoaded<Model?>(newVersion.ok);
|
||||
@@ -272,8 +303,8 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
return stateCopy;
|
||||
}
|
||||
if (stateCopy is CrudListLoaded<Model?>) {
|
||||
final crudQuery = this.crudQuery;
|
||||
if (crudQuery == null) {
|
||||
final crudSearch = this.crudSearch;
|
||||
if (crudSearch == null) {
|
||||
// No read operation, can't update stateCopy.
|
||||
return stateCopy;
|
||||
}
|
||||
@@ -281,10 +312,11 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
// (not all because previous stateCopy can be a query result)
|
||||
final List<String?> ids = stateCopy.data
|
||||
.map(
|
||||
(e) => e?.id,
|
||||
(e) => modelIdentifier.getIdentifierOrNull(e),
|
||||
)
|
||||
.where((element) => element != null)
|
||||
.toList();
|
||||
final result = await crudQuery.call([
|
||||
final result = await crudSearch.call([
|
||||
WhereQuery(
|
||||
WhereQueryType.whereIn,
|
||||
'id',
|
||||
@@ -296,7 +328,7 @@ abstract class CrudAdvancedCubit<Model extends ObjectModel>
|
||||
}
|
||||
return stateCopy;
|
||||
}
|
||||
return const CrudOkReturn();
|
||||
return const CrudUpdated();
|
||||
},
|
||||
(error) async => CrudError(error.toString()),
|
||||
),
|
||||
|
||||
+30
-2
@@ -16,7 +16,7 @@
|
||||
|
||||
part of 'crud_base_cubit.dart';
|
||||
|
||||
abstract class CrudState extends Equatable {
|
||||
sealed class CrudState extends Equatable {
|
||||
const CrudState();
|
||||
|
||||
@override
|
||||
@@ -29,10 +29,26 @@ class CrudInitial extends CrudState {
|
||||
}
|
||||
|
||||
/// Loading state of the CrudBaseCubit.
|
||||
class CrudLoading extends CrudState {
|
||||
abstract class CrudLoading extends CrudState {
|
||||
const CrudLoading();
|
||||
}
|
||||
|
||||
class CrudCreating extends CrudLoading {
|
||||
const CrudCreating();
|
||||
}
|
||||
|
||||
class CrudReading extends CrudLoading {
|
||||
const CrudReading();
|
||||
}
|
||||
|
||||
class CrudUpdating extends CrudLoading {
|
||||
const CrudUpdating();
|
||||
}
|
||||
|
||||
class CrudDeleting extends CrudLoading {
|
||||
const CrudDeleting();
|
||||
}
|
||||
|
||||
/// Error state of the CrudBaseCubit.
|
||||
class CrudError extends CrudState {
|
||||
const CrudError(this.message);
|
||||
@@ -57,6 +73,18 @@ class CrudOkReturn extends CrudSuccess {
|
||||
const CrudOkReturn();
|
||||
}
|
||||
|
||||
class CrudCreated extends CrudOkReturn {
|
||||
const CrudCreated();
|
||||
}
|
||||
|
||||
class CrudUpdated extends CrudOkReturn {
|
||||
const CrudUpdated();
|
||||
}
|
||||
|
||||
class CrudDeleted extends CrudOkReturn {
|
||||
const CrudDeleted();
|
||||
}
|
||||
|
||||
/// Loaded state of the CrudBaseCubit.
|
||||
/// This state is used to indicate that the operation was successful.
|
||||
/// Contains one object.
|
||||
|
||||
@@ -18,7 +18,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:wyatt_crud_bloc/src/core/enums/where_query_type.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/mixins/operation.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
|
||||
import 'package:wyatt_crud_bloc/src/core/model_identifier.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/create.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/delete.dart';
|
||||
@@ -26,33 +26,42 @@ import 'package:wyatt_crud_bloc/src/domain/usecases/delete_all.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/get.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/get_all.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/query.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/search.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/update.dart';
|
||||
import 'package:wyatt_crud_bloc/src/domain/usecases/update_all.dart';
|
||||
import 'package:wyatt_crud_bloc/src/features/crud/blocs/crud_base_cubit/crud_base_cubit.dart';
|
||||
|
||||
typedef DefaultCreate<Model> = CreateOperation<Model, void>;
|
||||
typedef DefaultRead = ReadOperation<dynamic, dynamic>;
|
||||
typedef DefaultUpdate = UpdateOperation<dynamic>;
|
||||
typedef DefaultDelete = DeleteOperation<dynamic>;
|
||||
|
||||
/// {@template crud_cubit}
|
||||
/// Cubit that handles CRUD operations.
|
||||
/// Cubit that handles SCRUD operations.
|
||||
/// {@endtemplate}
|
||||
abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
abstract class CrudCubit<Model> extends CrudBaseCubit {
|
||||
/// {@macro crud_cubit}
|
||||
CrudCubit() : super();
|
||||
|
||||
/// Create operation.
|
||||
/// Can be create.
|
||||
CreateOperation<Model, dynamic>? get createOperation;
|
||||
CreateOperation<Model, void>? get createOperation;
|
||||
|
||||
/// Read operation.
|
||||
/// Can be get, getAll, query.
|
||||
ReadOperation<Model, dynamic, dynamic>? get readOperation;
|
||||
/// Can be get, getAll, search or stream.
|
||||
ReadOperation<dynamic, dynamic>? get readOperation;
|
||||
|
||||
/// Update operation.
|
||||
/// Can be update, updateAll.
|
||||
UpdateOperation<Model, dynamic>? get updateOperation;
|
||||
UpdateOperation<dynamic>? get updateOperation;
|
||||
|
||||
/// Delete operation.
|
||||
/// Can be delete or deleteAll.
|
||||
DeleteOperation<Model, dynamic>? get deleteOperation;
|
||||
DeleteOperation<dynamic>? get deleteOperation;
|
||||
|
||||
/// Model identifier.
|
||||
/// Used to identify a model.
|
||||
ModelIdentifier<Model> get modelIdentifier;
|
||||
|
||||
Expected? _checkOperation<Expected>(dynamic operation) {
|
||||
if (operation == null) {
|
||||
@@ -70,27 +79,27 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> read({String? id, List<QueryInterface>? conditions}) async {
|
||||
FutureOr<void> read({String? id, List<Query>? conditions}) async {
|
||||
if (_checkOperation<Get<Model>>(readOperation) != null && id != null) {
|
||||
return _get(id);
|
||||
}
|
||||
if (_checkOperation<GetAll<Model>>(readOperation) != null) {
|
||||
return _getAll();
|
||||
}
|
||||
if (_checkOperation<Query<Model>>(readOperation) != null &&
|
||||
if (_checkOperation<Search<Model>>(readOperation) != null &&
|
||||
conditions != null) {
|
||||
return _query(conditions);
|
||||
return _search(conditions);
|
||||
}
|
||||
if (_checkOperation<Query<Model>>(readOperation) != null &&
|
||||
if (_checkOperation<Stream<Model>>(readOperation) != null &&
|
||||
conditions == null) {
|
||||
return _getAll();
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> update(
|
||||
FutureOr<void> update({
|
||||
UpdateParameters<Model>? single,
|
||||
Map<String, dynamic>? all,
|
||||
) async {
|
||||
}) async {
|
||||
if (_checkOperation<Update<Model>>(updateOperation) != null &&
|
||||
single != null) {
|
||||
return _update(single);
|
||||
@@ -118,7 +127,7 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
}
|
||||
|
||||
final stateCopy = state;
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudCreating());
|
||||
final result = await crud.call(model);
|
||||
emit(
|
||||
result.fold(
|
||||
@@ -127,7 +136,7 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
if (stateCopy.data == null) {
|
||||
return CrudLoaded<Model?>(model);
|
||||
}
|
||||
if (stateCopy.data!.id == model.id) {
|
||||
if (stateCopy.data == model) {
|
||||
return CrudLoaded<Model?>(model);
|
||||
}
|
||||
|
||||
@@ -142,7 +151,7 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
return CrudListLoaded<Model?>(lst);
|
||||
}
|
||||
|
||||
return const CrudOkReturn();
|
||||
return const CrudCreated();
|
||||
},
|
||||
(error) => CrudError(error.toString()),
|
||||
),
|
||||
@@ -156,13 +165,16 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
}
|
||||
|
||||
final stateCopy = state;
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudDeleting());
|
||||
final result = await crud.call(id);
|
||||
emit(
|
||||
result.fold(
|
||||
(_) {
|
||||
if (stateCopy is CrudLoaded<Model?>) {
|
||||
if (stateCopy.data?.id == id) {
|
||||
if (modelIdentifier.isIdentifier(
|
||||
model: stateCopy.data,
|
||||
identifier: id,
|
||||
)) {
|
||||
return CrudLoaded<Model?>(null);
|
||||
}
|
||||
|
||||
@@ -170,11 +182,19 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
}
|
||||
if (stateCopy is CrudListLoaded<Model?>) {
|
||||
return CrudListLoaded<Model?>(
|
||||
stateCopy.data.where((element) => element?.id != id).toList(),
|
||||
stateCopy.data.where((element) {
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
return !modelIdentifier.isIdentifier(
|
||||
model: element,
|
||||
identifier: id,
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
return const CrudOkReturn();
|
||||
return const CrudDeleted();
|
||||
},
|
||||
(error) => CrudError(error.toString()),
|
||||
),
|
||||
@@ -187,7 +207,7 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
return;
|
||||
}
|
||||
final stateCopy = state;
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudDeleting());
|
||||
final result = await crud.call(null);
|
||||
emit(
|
||||
result.fold(
|
||||
@@ -199,7 +219,7 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
return CrudListLoaded<Model?>(const []);
|
||||
}
|
||||
|
||||
return const CrudOkReturn();
|
||||
return const CrudDeleted();
|
||||
},
|
||||
(error) => CrudError(error.toString()),
|
||||
),
|
||||
@@ -211,7 +231,7 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
if (crud == null) {
|
||||
return;
|
||||
}
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudReading());
|
||||
final result = await crud.call(id);
|
||||
emit(
|
||||
result.fold(
|
||||
@@ -226,7 +246,7 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
if (crud == null) {
|
||||
return;
|
||||
}
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudReading());
|
||||
final result = await crud.call(null);
|
||||
emit(
|
||||
result.fold(
|
||||
@@ -236,13 +256,13 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<void> _query(List<QueryInterface> conditions) async {
|
||||
final crud = _checkOperation<Query<Model>>(readOperation);
|
||||
FutureOr<void> _search(List<Query> conditions) async {
|
||||
final crud = _checkOperation<Search<Model>>(readOperation);
|
||||
if (crud == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudReading());
|
||||
final result = await crud.call(conditions);
|
||||
emit(
|
||||
result.fold(
|
||||
@@ -259,13 +279,16 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
}
|
||||
|
||||
final stateCopy = state;
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudUpdating());
|
||||
final result = await crud.call(param);
|
||||
emit(
|
||||
await result.foldAsync(
|
||||
(_) async {
|
||||
if (stateCopy is CrudLoaded<Model?>) {
|
||||
if (stateCopy.data?.id == param.id) {
|
||||
if (modelIdentifier.isIdentifier(
|
||||
model: stateCopy.data,
|
||||
identifier: param.id,
|
||||
)) {
|
||||
// Same object, need to update actual stateCopy
|
||||
final crudGet = _checkOperation<Get<Model>>(readOperation);
|
||||
if (crudGet == null) {
|
||||
@@ -280,29 +303,48 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
return stateCopy;
|
||||
}
|
||||
if (stateCopy is CrudListLoaded<Model?>) {
|
||||
final bool listContains =
|
||||
stateCopy.data.any((element) => element?.id == param.id);
|
||||
final bool listContains = stateCopy.data.any((element) {
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
return modelIdentifier.isIdentifier(
|
||||
model: element,
|
||||
identifier: param.id,
|
||||
);
|
||||
});
|
||||
if (listContains) {
|
||||
// Loaded objects contains the modified object.
|
||||
|
||||
final crudGet = _checkOperation<Get<Model>>(readOperation);
|
||||
if (crudGet == null) {
|
||||
// No read operation, can't update stateCopy.
|
||||
return stateCopy;
|
||||
if (crudGet != null) {
|
||||
final newVersion = await crudGet.call(param.id);
|
||||
if (newVersion.isOk) {
|
||||
final newList = stateCopy.data.where(
|
||||
(element) {
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
return !modelIdentifier.isIdentifier(
|
||||
model: element,
|
||||
identifier: param.id,
|
||||
);
|
||||
},
|
||||
).toList();
|
||||
return CrudListLoaded<Model?>(newList + [newVersion.ok]);
|
||||
}
|
||||
}
|
||||
final newVersion = await crudGet.call(param.id);
|
||||
if (newVersion.isOk) {
|
||||
final newList = stateCopy.data
|
||||
.where(
|
||||
(element) => element?.id != param.id,
|
||||
)
|
||||
.toList();
|
||||
return CrudListLoaded<Model?>(newList + [newVersion.ok]);
|
||||
|
||||
// If get operation is not available, just reload all objects.
|
||||
final crudGetAll = _checkOperation<GetAll<Model>>(readOperation);
|
||||
if (crudGetAll != null) {
|
||||
final newVersion = await crudGetAll.call(null);
|
||||
if (newVersion.isOk) {
|
||||
return CrudListLoaded<Model?>(newVersion.ok ?? []);
|
||||
}
|
||||
}
|
||||
}
|
||||
return stateCopy;
|
||||
}
|
||||
return const CrudOkReturn();
|
||||
return const CrudUpdated();
|
||||
},
|
||||
(error) async => CrudError(error.toString()),
|
||||
),
|
||||
@@ -316,7 +358,7 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
}
|
||||
|
||||
final stateCopy = state;
|
||||
emit(const CrudLoading());
|
||||
emit(const CrudUpdating());
|
||||
final result = await crud.call(param);
|
||||
emit(
|
||||
await result.foldAsync(
|
||||
@@ -328,7 +370,8 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
// No read operation, can't update stateCopy.
|
||||
return stateCopy;
|
||||
}
|
||||
final actualId = stateCopy.data?.id;
|
||||
final actualId =
|
||||
modelIdentifier.getIdentifierOrNull(stateCopy.data);
|
||||
final newVersion = await crudGet.call(actualId ?? '');
|
||||
if (newVersion.isOk) {
|
||||
return CrudLoaded<Model?>(newVersion.ok);
|
||||
@@ -336,7 +379,7 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
return stateCopy;
|
||||
}
|
||||
if (stateCopy is CrudListLoaded<Model?>) {
|
||||
final crudQuery = _checkOperation<Query<Model>>(readOperation);
|
||||
final crudQuery = _checkOperation<Search<Model>>(readOperation);
|
||||
if (crudQuery == null) {
|
||||
// No read operation, can't update stateCopy.
|
||||
return stateCopy;
|
||||
@@ -345,8 +388,9 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
// (not all because previous stateCopy can be a query result)
|
||||
final List<String?> ids = stateCopy.data
|
||||
.map(
|
||||
(e) => e?.id,
|
||||
(e) => modelIdentifier.getIdentifierOrNull(e),
|
||||
)
|
||||
.where((element) => element != null)
|
||||
.toList();
|
||||
final result = await crudQuery.call([
|
||||
WhereQuery(
|
||||
@@ -360,7 +404,7 @@ abstract class CrudCubit<Model extends ObjectModel> extends CrudBaseCubit {
|
||||
}
|
||||
return stateCopy;
|
||||
}
|
||||
return const CrudOkReturn();
|
||||
return const CrudUpdated();
|
||||
},
|
||||
(error) async => CrudError(error.toString()),
|
||||
),
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
export 'crud_builder.dart';
|
||||
@@ -17,4 +17,4 @@
|
||||
export 'blocs/crud_advanced_cubit.dart';
|
||||
export 'blocs/crud_base_cubit/crud_base_cubit.dart';
|
||||
export 'blocs/crud_cubit.dart';
|
||||
export 'builder/builder.dart';
|
||||
export 'builder/crud_builder.dart';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Copyright (C) 2023 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
|
||||
Reference in New Issue
Block a user