DBCollectionFree

DBCollectionFree is a service library that offers a schema-free, flexible way to manage MongoDB collections in Dart

DBCollectionFree

DBCollectionFree is a service library that offers a schema-free, flexible way to manage MongoDB collections in Dart. It allows direct interaction with MongoDB collections, providing utilities for common database operations, field validation, and customizable forms.

DBCollectionFree is an ideal choice for building an API when you need rapid development without the hassle of defining rigid data models. It offers a more flexible approach to manage MongoDB collections directly, enabling you to add or modify fields on the go. This can save you valuable time during development, allowing you to focus on building and deploying your API rather than on data schema management. Perfect for prototyping or for use cases where data structures may evolve frequently, DBCollectionFree makes it simple to get your API up and running quickly.

This example explains how to set up and use DBCollectionFree for managing your MongoDB collections without predefined data models. The included example demonstrates a PersonCollectionFree implementation for handling personal data.

Features

  • Flexible Schema Management: No predefined data model required.
  • CRUD Operations: Provides insert, update, replace, delete, and find functions.
  • Field Validation: Supports required fields, length constraints, and data types (e.g., String, int, ObjectId).
  • Customizable Forms: Define field properties, validation rules, and data filters.
  • MongoDB-Specific Operations: Includes support for ObjectId checks, MongoDB query builders, and optimized field updates.

Usage

Setting Up a Collection

To define a MongoDB collection using DBCollectionFree, you create a class that extends DBCollectionFree and configure the collection's fields and their validation rules. The example below defines a PersonCollectionFree class with fields such as name, age, email, birthday, married, and height.

Example Code

Define the Collection

import 'package:mongo_dart/mongo_dart.dart';
import 'package:webapp/wa_model_less.dart';

class PersonCollectionFree extends DBCollectionFree {
  PersonCollectionFree({required super.db})
      : super(
          name: 'person',
          form: formPerson,
        );

  static DBFormFree get formPerson => DBFormFree(
        fields: {
          '_id': DBFieldFree<ObjectId>(
            readonly: true,
            hideJson: true,
          ),
          'name': DBFieldFree<String?>(
            validators: [
              FieldValidator.requiredField(),
              FieldValidator.fieldLength(min: 3),
            ],
          ),
          'age': DBFieldFree<int?>(
            validators: [
              FieldValidator.requiredField(),
              FieldValidator.isNumberField(min: 1, max: 120),
            ],
          ),
          'email': DBFieldFree<String?>(
            validators: [
              FieldValidator.requiredField(),
              FieldValidator.isEmailField(),
            ],
          ),
          'birthday': DBFieldFree<DateTime?>(
            validators: [
              FieldValidator.requiredField(),
            ],
          ),
          'married': DBFieldFree<bool?>(
            defaultValue: false,
          ),
          'height': DBFieldFree<double?>(
            validators: [
              FieldValidator.isNumberDoubleField(min: 0.5, max: 2.5),
            ],
          ),
        },
      );
}

Usage Example

We are use this

  • check the home_controller.dart in example
class HomeController extends WaController {
  HomeController(super.rq);
  var personCollection = PersonCollectionFree(db: server.db);


...



  Future<String> addNewPerson() async {
    final res = await personCollection.insert(rq.getAllData());
    if (res.success) {
      rq.addParam('data', res.formValues);
    } else {
      rq.addParam('form', res.toJson());
    }
    return _renderPerson(data: {
      'success': res.success,
    });
  }

  Future<String> replacePerson() async {
    final id = rq.getParam('id', def: '').toString();
    final res = await personCollection.replaceOne(id, rq.getAllData());

    if (res == null) {
      return _renderPerson(
        data: {'success': false},
        status: 404,
      );
    }

    if (res.success) {
      rq.addParam('data', res.formValues);
    } else {
      rq.addParam('form', res.toJson());
    }
    return _renderPerson(data: {
      'success': res.success,
    });
  }

  Future<String> allPerson() async {
    final countAll = await personCollection.getCount();
    final pageSize = rq.get<int>('pageSize', def: 20);
    final orderBy = rq.get<String>('orderBy', def: '_id');
    final orderReverse = rq.get<bool>('orderReverse', def: true);

    UIPaging paging = UIPaging(
      rq: rq,
      total: countAll,
      pageSize: pageSize,
      widget: '',
      page: rq.get<int>('page', def: 1),
    );

    final res = await personCollection.getAll(
      limit: paging.pageSize,
      skip: paging.start,
      sort: DQ.order(orderBy, orderReverse),
    );

    return _renderPerson(data: {
      'success': res.isNotEmpty,
      'data': res,
      'paging': await paging.renderData(),
    });
  }

  Future<String> onePerson() async {
    final id = rq.getParam('id', def: '').toString();
    final res = await personCollection.getById(id);
    return _renderPerson(data: {
      'success': res != null,
      'data': res,
    });
  }

  Future<String> updateOrDeletePerson() async {
    final id = rq.getParam('id', def: '').toString();
    final action = rq.get<String>('action', def: '');
    if (action == 'DELETE') {
      return deletePerson();
    }

    final email = rq.get<String>('email', def: '');
    final res = await personCollection.updateField(id, 'email', email);
    if (res == null) {
      return _renderPerson(
        data: {'success': false},
        status: 404,
      );
    }
    if (res.success) {
      return _renderPerson(data: {
        'success': res.success,
        'data': res.formValues,
      });
    }
    return _renderPerson(data: {
      'success': res.success,
      'form': res.toJson(),
    });
  }

  Future<String> deletePerson() async {
    final id = rq.getParam('id', def: '').toString();
    final res = await personCollection.delete(id);
    return _renderPerson(data: {
      'success': res,
    });
  }

  Future<String> _renderPerson({
    required Map<String, Object?> data,
    status = 200,
  }) async {
    if (rq.isApiEndpoint) {
      return rq.renderDataParam(
        data: data,
        status: status,
      );
    }

    final countAll = await personCollection.getCount();
    final pageSize = rq.get<int>('pageSize', def: 10);
    final orderBy = rq.get<String>('orderBy', def: '_id');
    final orderReverse = rq.get<bool>('orderReverse', def: true);

    UIPaging paging = UIPaging(
      rq: rq,
      total: countAll,
      pageSize: pageSize,
      widget: 'template/paging',
      page: rq.get<int>('page', def: 1),
    );

    final res = await personCollection.getAll(
      limit: paging.pageSize,
      skip: paging.start,
      sort: DQ.order(orderBy, orderReverse),
    );

    data = {
      ...data,
      'success': res.isNotEmpty,
      'allPerson': res,
      'paging': await paging.render(),
    };
    rq.addParams(data);
    return renderTemplate('example/person');
  }


  ...



}

Methods Overview

  • insert: Validates and inserts a document.
  • replaceOne: Replaces a document by its ID with a new data map.
  • getAll: Retrieves all documents based on specified filters and sort options.
  • existId: Checks if a document exists based on an ObjectId.
  • delete: Deletes a document by its ID.
  • updateField: Updates a single field within a document by its ID.
  • getById: Retrieves a document by its ID.

Validation and Custom Form Rules

Each field can be customized with validation rules such as required, min, max, and format validations like isEmailField().

Error Handling

Errors are tracked and updated based on MongoDB response codes, which can then be used to provide feedback for each field.