<!--
 * 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/>.
-->

# CRUD BloC

<p align="left">
  <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"><img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /></a>
  <img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" />
</p>

CRUD Bloc Pattern utilities for Flutter.

This package defines a set of classes that can be used to implement the CRUD Bloc Pattern.

* Model
* Data Source
  + In Memory
  + Firestore
* Repository
* Use Case
  + Create
  + Get
  + Get All
  + Update
  + Update All
  + Delete
  + Delete All
  + Query
* Bloc
  + Standard (C R U D), you have to choose the responsiblity of the bloc for each use case. For example, you can have a cubit that only handles the creation of an entity, and another cubit that only handles the deletion of an entity. Each cubit can only have one operation responsibility of each type, for example you can't have a cubit that handles get and get all.
  + Advanced, you can set every use case to be handled by the bloc. This is useful if you want to have a single bloc that handles all the operations of an entity.

## Usage

Create a model class that extends the `ObjectModel` class.

```dart
class User extends ObjectModel {
  @override
  final String? id;

  final String? name;
  
  const User({
    required this.name,
    this.id,
  });

  Map<String, Object> toMap() {
    return {
      'name': name ?? '',
    };
  }

  @override
  String toString() => 'User(id: $id, name: $name)';
}
```

You have to implement a bloc.

```dart
/// A [CrudCubit] for [User].
class UserCubit extends CrudCubit<User> {
  final CrudRepository<User> _crudRepository;

  UserCubit(this._crudRepository);

  @override
  CreateOperation<User, dynamic>? get createOperation =>
      Create(_crudRepository);

  @override
  DeleteOperation<User, dynamic>? get deleteOperation =>
      Delete(_crudRepository);

  @override
  ReadOperation<User, dynamic, dynamic>? get readOperation =>
      GetAll(_crudRepository);

  @override
  UpdateOperation<User, dynamic>? get updateOperation =>
      Update(_crudRepository);
}
```

> You can also use the `CrudAdvancedCubit` class to implement a bloc that handles all the use cases.

Then you can use the bloc in your widget with a data source and a repository.

```dart
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    final CrudDataSource<User> userLocalDataSource =
        CrudInMemoryDataSourceImpl<User>(toMap: (user) => user.toMap());

    final CrudRepository<User> userRepository =
        CrudRepositoryImpl(crudDataSource: userLocalDataSource);

    return RepositoryProvider<CrudRepository<User>>.value(
      value: userRepository,
      child: BlocProvider<UserCubit>(
        create: (context) => UserCubit(userRepository)..read(),
        child: MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const MyHomePage(),
        ),
      ),
    );
  }
}
```

And anywhere in your widget tree you can use the BlocBuilder to build your widget.

```dart
...
BlocBuilder<UserCubit, CrudState>(
  builder: (context, state) {
    return CrudBuilder.typed<CrudListLoaded<User?>>(
      state: state,
      builder: ((context, state) {
        return ListView.builder(
          shrinkWrap: true,
          itemCount: state.data.length,
          itemBuilder: (context, index) {
            final user = state.data.elementAt(index);
            return ListTile(
              title: Text(user?.name ?? 'Error'),
              subtitle: Text(user?.id ?? 'Error'),
              onTap: () {
                context.read<UserCubit>().delete(id: (user?.id)!);
              },
            );
          },
        );
      }),
      initialBuilder: (context, state) => const Text("Loading..."),
      loadingBuilder: (context, state) => const Text("Loading..."),
      errorBuilder: (context, state) => Text("Error: $state"),
    );
  },
),
...
```
